diff --git a/.bowerrc b/.bowerrc new file mode 100644 index 000000000..1fdbf4373 --- /dev/null +++ b/.bowerrc @@ -0,0 +1,5 @@ +{ + "directory": "tests/vendor", + "color" : false, + "interactive": false +} diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..678489ba7 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,11 @@ +.git +.github +node_modules +build +tests/build +tests/pages +tests/vendor +tests/coverage +tests/results +tests/samples +tests/perf/results diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..1063ffe8e --- /dev/null +++ b/.editorconfig @@ -0,0 +1,11 @@ +[*] +end_of_line = lf +insert_final_newline = true + +[*.js] +indent_style = space +indent_size = 2 + +[*.html] +indent_style = space +indent_size = 2 diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 000000000..b9c975474 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,2 @@ +**/*.headers +node_modules/ diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 000000000..c8f645436 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,146 @@ +{ + "root": true, + "env": { + "browser": true + }, + "plugins": [ + "html" + ], + "globals": { + "BOOMR": true, + "BOOMR_start": true, + "BOOMR_lstart": true, + "BOOMR_onload": true, + "BOOMR_test": true, + "console": false, + "unescape": false, + "BOOMR_configt": true, + "BOOMR_check_doc_domain": true, + "_BOOMR_userAgentCheck": true + }, + "rules": { + // + // Rules that were enabled by default in pre-1.0 eslint + // https://github.com/eslint/eslint/blob/master/docs/user-guide/migrating-to-1.0.0.md + // re-enable all the ones we are not specifically disabling + // + "no-alert": "error", + "no-array-constructor": "error", + "no-caller": "error", + "no-catch-shadow": "error", + "no-eval": "error", + "no-extend-native": "error", + "no-extra-bind": "error", + "no-implied-eval": "error", + "no-iterator": "error", + "no-label-var": "error", + "no-labels": "error", + "no-lone-blocks": "error", + "no-loop-func": "error", + "no-multi-str": "error", + "no-native-reassign": "error", + "no-new": "error", + "no-new-func": "error", + "no-new-object": "error", + "no-new-wrappers": "error", + "no-octal-escape": "error", + "no-process-exit": "error", + "no-proto": "error", + "no-return-assign": "error", + "no-script-url": "error", + "no-sequences": "error", + "no-shadow": "error", + "no-shadow-restricted-names": "error", + "no-spaced-func": "error", + "no-undef-init": "error", + "no-unused-expressions": "error", + "no-use-before-define": ["error", { "functions": false }], + "no-with": "error", + "comma-spacing": "error", + "curly": ["error", "all"], + "eol-last": "error", + "no-extra-parens": ["error", "functions"], + "eqeqeq": "error", + "new-parens": "error", + "semi": "error", + "space-infix-ops": "error", + "yoda": ["error", "never"], + "spaced-comment": ["error", "always", { + "block": { + "balanced": true + } + }], + + // + // Changes over defaults + // + "keyword-spacing": "error", + "no-mixed-spaces-and-tabs": "error", + "quotes": ["error", "double", "avoid-escape"], + "dot-notation": ["error", {"allowKeywords": false}], + "space-unary-ops": "error", + "key-spacing": ["error", {"beforeColon": false, "afterColon": true, "mode": "minimum"}], + "no-empty": "error", + "brace-style": ["error", "stroustrup", { "allowSingleLine": false }], + "semi-spacing": ["error", {"before": false, "after": true}], + "indent": ["error", 2, {"VariableDeclarator": "first", "MemberExpression": 1, "CallExpression": {"arguments": 1}}], + "space-before-function-paren": ["error", "never"], + "no-trailing-spaces": ["error", { "skipBlankLines": false }], + "linebreak-style": ["error", "unix"], + "comma-dangle": ["error", "never"], + "operator-linebreak": ["error", "after"], + "space-in-parens": ["error", "never"], + "no-debugger": "error", + "block-spacing": "error", + "lines-around-comment": ["error", { allowBlockStart: true, allowObjectStart: true, ignorePattern: "BEGIN_|END_|SOASTA PRIVATE" }], + "no-inline-comments": ["error", { "ignorePattern": "^ ?(BEGIN_|END_|fails|keep)" } ], + "newline-after-var": ["error", "always"], + "newline-before-return": "error", + "max-len": ["error", 120], + "no-irregular-whitespace": "error", + "no-multiple-empty-lines": ["error", { "max": 1} ], + "padded-blocks": ["error", "never"], + "one-var-declaration-per-line": "error", + "padding-line-between-statements": ["error", + { blankLine: "always", prev: "block-like", next: "if" }, + { blankLine: "always", prev: "expression", next: "if" }, + { blankLine: "always", prev: "var", next: "if" }, + { blankLine: "always", prev: "block-like", next: "var" }, + { blankLine: "always", prev: "try", next: "*" }, + { blankLine: "always", prev: "block-like", next: "expression" }, + { blankLine: "always", prev: "*", next: "function" } + ], + + // + // To enable soon + // + + //"require-jsdoc": "error", + //"valid-jsdoc": "error", + + // + // Disabled rules + // + + // We have a lot of variables in underscore_casing + "camelcase": 0, + + // Not ready for strict-mode yet + "strict": 0, + + // We have some functions like BOOMR_check_doc_domain or BOOMR. + "new-cap": 0, + + // We use console.log for debugging + "no-console": 0, + + // We use _s in a couple places for internal vars + "no-underscore-dangle": 0, + + // We delete some global vars for compat with older IE versions + "no-delete-var": 0, + + // We use spaces for alignment in many places + "no-multi-spaces": 0 + } +} diff --git a/.github/workflows/compressed-size.yml b/.github/workflows/compressed-size.yml new file mode 100644 index 000000000..de3b2c991 --- /dev/null +++ b/.github/workflows/compressed-size.yml @@ -0,0 +1,23 @@ +name: Compressed Size + +on: + push: + branches: + - master + pull_request: + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [12.x] + + steps: + - uses: actions/checkout@v2 + - uses: preactjs/compressed-size-action@v2 + with: + repo-token: '${{ secrets.GITHUB_TOKEN }}' + pattern: '{build/boomerang-1.0.0.min.js,build/plugins/*.js}' + compression: brotli + show-total: false diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 000000000..c41d50b2c --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,23 @@ +name: Lint + +on: + push: + branches: + - master + pull_request: + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [12.x] + + steps: + - uses: actions/checkout@v2 + - run: npm ci --no-audit --prefer-offline + - uses: a-b-r-o-w-n/eslint-action@v2 + with: + repo-token: '${{ secrets.GITHUB_TOKEN }}' + files: '**/*.js' + extensions: '.js' diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml new file mode 100644 index 000000000..0885c82e6 --- /dev/null +++ b/.github/workflows/node.js.yml @@ -0,0 +1,137 @@ +# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node +# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions + +name: Node.js CI + +on: + push: + branches: + - master + pull_request: + +jobs: + build-and-lint: + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [12.x] + steps: + - uses: actions/checkout@v2 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + - run: npm ci --no-audit --prefer-offline + - run: npm run build + - run: npm run lint + + unit-test: + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [12.x] + steps: + - uses: actions/checkout@v2 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + - run: npm ci --no-audit --prefer-offline + - run: echo "127.0.0.1 boomerang-test.local" | sudo tee -a /etc/hosts + - run: echo "127.0.0.1 boomerang-test2.local" | sudo tee -a /etc/hosts + - run: npm run unit-test + + e2e-test-1: + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [12.x] + env: + CI_NODE_INDEX: 0 + CI_NODE_TOTAL: 5 + steps: + - uses: actions/checkout@v2 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + - run: npm ci --no-audit --prefer-offline + - run: echo "127.0.0.1 boomerang-test.local" | sudo tee -a /etc/hosts + - run: echo "127.0.0.1 boomerang-test2.local" | sudo tee -a /etc/hosts + - run: npm run e2e-test + + e2e-test-2: + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [12.x] + env: + CI_NODE_INDEX: 1 + CI_NODE_TOTAL: 5 + steps: + - uses: actions/checkout@v2 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + - run: npm ci --no-audit --prefer-offline + - run: echo "127.0.0.1 boomerang-test.local" | sudo tee -a /etc/hosts + - run: echo "127.0.0.1 boomerang-test2.local" | sudo tee -a /etc/hosts + - run: npm run e2e-test + + e2e-test-3: + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [12.x] + env: + CI_NODE_INDEX: 2 + CI_NODE_TOTAL: 5 + steps: + - uses: actions/checkout@v2 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + - run: npm ci --no-audit --prefer-offline + - run: echo "127.0.0.1 boomerang-test.local" | sudo tee -a /etc/hosts + - run: echo "127.0.0.1 boomerang-test2.local" | sudo tee -a /etc/hosts + - run: npm run e2e-test + + e2e-test-4: + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [12.x] + env: + CI_NODE_INDEX: 3 + CI_NODE_TOTAL: 5 + steps: + - uses: actions/checkout@v2 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + - run: npm ci --no-audit --prefer-offline + - run: echo "127.0.0.1 boomerang-test.local" | sudo tee -a /etc/hosts + - run: echo "127.0.0.1 boomerang-test2.local" | sudo tee -a /etc/hosts + - run: npm run e2e-test + + e2e-test-5: + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [12.x] + env: + CI_NODE_INDEX: 4 + CI_NODE_TOTAL: 5 + steps: + - uses: actions/checkout@v2 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + - run: npm ci --no-audit --prefer-offline + - run: echo "127.0.0.1 boomerang-test.local" | sudo tee -a /etc/hosts + - run: echo "127.0.0.1 boomerang-test2.local" | sudo tee -a /etc/hosts + - run: npm run e2e-test diff --git a/.gitignore b/.gitignore index 70d323c3d..b729a210e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,32 @@ -.gitignore *.gz -boomerang-*.js +*.swp +/boomerang-*.js +build/* +*.log +*.diff +.project +.settings/ +tests/vendor/ +tests/coverage/ +tests/results/* +tests/pages/* +node_modules/ +build/ +tests/server/env.json +npm-debug.log +/tests/e2e/e2e.json +/tests/e2e/e2e-debug.json +tests/page-templates/12-react/support/app-component.js +tests/page-templates/12-react/support/app-component.js.map +tests/page-templates/12-react/support/app.js +*.#* +*#* +*~ +.idea/ +*.DS_Store +tests/perf/results/ +tests/perf/pages/ +tests/perf/scenarios.json +plugins.user.json +environments.json +.vscode/ diff --git a/.markdownlint.json b/.markdownlint.json new file mode 100644 index 000000000..1e8e100f3 --- /dev/null +++ b/.markdownlint.json @@ -0,0 +1,11 @@ +{ + "MD024": false, + "MD013": false, + "MD041": false, + "MD033": { + "allowed_elements": ["a"] + }, + "MD004": { + "style": "asterisk" + } +} \ No newline at end of file diff --git a/.npmignore b/.npmignore new file mode 100644 index 000000000..bb002f001 --- /dev/null +++ b/.npmignore @@ -0,0 +1,14 @@ +.bowerrc +.editorconfig +.eslintignore +.eslintrc +jsdoc.conf.json +doc-template/ +images/ +tests/ +build/ +tasks/ +.settings/ +.project +.github/ +docker/ diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 000000000..dae199aec --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +v12 diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..1813005b8 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,15 @@ +{ + "editor.codeActionsOnSave": { + "source.fixAll.eslint": "explicit" + }, + "eslint.validate": ["javascript"], + + // The number of spaces a tab is equal to. + "editor.tabSize": 2, + + // Insert spaces when pressing Tab. + "editor.insertSpaces": true, + + // When opening a file, `editor.tabSize` and `editor.insertSpaces` will be detected based on the file contents. + "editor.detectIndentation": true +} \ No newline at end of file diff --git a/Gruntfile.js b/Gruntfile.js new file mode 100644 index 000000000..6f07ae80a --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,1739 @@ +/* eslint-env node */ +"use strict"; + +// +// Imports +// +var fs = require("fs"); +var path = require("path"); +var fse = require("fs-extra"); +var stripJsonComments = require("strip-json-comments"); +var grunt = require("grunt"); +var async = require("async"); + +// +// Constants +// + +// +// Domains for test purposes +// +var DEFAULT_TEST_MAIN_DOMAIN = "boomerang-test.local"; +var DEFAULT_TEST_SECONDARY_DOMAIN = "boomerang-test2.local"; + +var boomerangE2ETestDomain = grunt.option("main-domain") || DEFAULT_TEST_MAIN_DOMAIN; +var boomerangE2ESecondDomain = grunt.option("secondary-domain") || DEFAULT_TEST_SECONDARY_DOMAIN; + +var BUILD_PATH = "build"; +var TEST_BUILD_PATH = path.join("tests", "build"); +var TEST_RESULTS_PATH = path.join("tests", "results"); +var TEST_DEBUG_PORT = parseInt(grunt.option("test-port")) || 4002; +var TEST_SCHEME = grunt.option("test-scheme") || "http"; +var TEST_URL_BASE = grunt.option("test-url") || TEST_SCHEME + "://" + boomerangE2ETestDomain + ":" + TEST_DEBUG_PORT; + +var SELENIUM_ADDRESS = grunt.option("selenium-address") || "http://" + boomerangE2ETestDomain + ":4444/wd/hub"; +var E2E_BASE_URL = TEST_SCHEME + "://" + boomerangE2ETestDomain + ":" + TEST_DEBUG_PORT + "/"; + +var DEFAULT_BROWSER = grunt.option("test-browser") || "ChromeHeadless"; + +var DEFAULT_UGLIFY_BOOMERANGJS_OPTIONS = { + preserveComments: false, + mangle: { + // for errors.js + reserved: [ + "createStackForSend", + "loadFinished", + "BOOMR_addError", + "BOOMR_plugins_errors_onerror", + "BOOMR_plugins_errors_onxhrerror", + "BOOMR_plugins_errors_console_error", + "BOOMR_plugins_errors_wrap", + "BOOMR_plugins_errors_wrapped_function", + "BOOMR_plugins_errors_wrapped_removeEventListener" + ] + }, + ie8: true, + sourceMap: true, + compress: { + keep_fnames: true, + sequences: false + } +}; + +var SAUCELABS_CONFIG = { + username: process.env.CI_SAUCELABS_USERNAME, + key: function() { + return process.env.CI_SAUCELABS_KEY; + }, + build: process.env.CI_BUILD_NUMBER, + tunneled: false +}; + +var LINT_TARGETS = [ + "*.js", + "plugins/*.js", + "snippets/*.js", + "tasks/*.js", + "tests/*.js", + "tests/unit/*.js", + "tests/unit/*.html", + "tests/e2e/*.js", + "tests/server/*.js", + "tests/page-templates/**/*.js", + "tests/page-templates/**/*.html", + "tests/page-templates/**/*.js", + "tests/test-templates/**/*.js", + "!tests/page-templates/12-react/support/*", + // fails on snippet include + "!tests/page-templates/03-load-order/01-after-page-load.html", + // fails on snippet include + "!tests/page-templates/03-load-order/07-after-page-load-boomr-page-ready.html", + // parse error on snippet include + "!tests/page-templates/29-opt-out-opt-in/01-opt-in-origin-injected-loader-wrapper.html" +]; + +// Get WebDriver versions, allowing for a single argument or an array +var webDriverVersions = grunt.option("webdriver-versions"); + +if (Array.isArray(webDriverVersions)) { + webDriverVersions = webDriverVersions.join(" "); +} + +function fileForHtml(file) { + return grunt.file.read(file) + .replace(/&/g, "&") + .replace(/"/g, """) + .replace(//g, ">"); +} + +/** + * Gets the build configuration + */ +function getBuildConfig() { + var buildConfig = { + server: grunt.option("server") || DEFAULT_TEST_MAIN_DOMAIN || "localhost", + beaconUrlsAllowed: grunt.option("beacon-urls-allowed") || "" + }; + + return buildConfig; +} + +// +// Grunt config +// +function getConfig() { + // + // Paths + // + var testsDir = path.join(__dirname, "tests"); + var perfTestsDir = path.join(testsDir, "perf"); + var pageTemplateSnippetsDir = path.join(testsDir, "page-template-snippets"); + var pluginsDir = path.join(__dirname, "plugins"); + var snippetsDir = path.join(__dirname, "snippets"); + + // + // Build numbers + // + var pkg = grunt.file.readJSON("package.json"); + var buildNumber = grunt.option("build-number") || 0; + var releaseVersion = pkg.releaseVersion + "." + buildNumber; + var buildRevision = grunt.option("build-revision") || 0; + var buildSuffix = grunt.option("build-suffix") ? (grunt.option("build-suffix") + ".") : ""; + var buildFlavor = grunt.option("build-flavor") || ""; + + if (buildFlavor) { + buildSuffix = buildFlavor + "." + buildSuffix; + } + + // + // Determine source files: + // boomerang.js and plugins/*.js order + // + var src = ["boomerang.js"]; + + // default plugins + var plugins = grunt.file.readJSON("plugins.json"); + + // allow overwriting with user plugins (plugins.user.json) + try { + var userPlugins = grunt.file.readJSON("plugins.user.json"); + + if (userPlugins.plugins) { + // use the user plugins instead + grunt.log.ok("Using plugins.user.json"); + + plugins = userPlugins; + } + } + catch (e) { + // NOP + } + + // use a specific flavor + if (buildFlavor) { + if (!plugins.flavors[buildFlavor]) { + return grunt.fail.fatal("Build flavor " + buildFlavor + " does not exist"); + } + + grunt.log.ok("Building flavor: " + buildFlavor + " with " + plugins.flavors[buildFlavor].plugins.length + + " plugins: " + JSON.stringify(plugins.flavors[buildFlavor].plugins)); + + src.push(plugins.flavors[buildFlavor].plugins); + + buildRevision = plugins.flavors[buildFlavor].revision; + } + else { + src.push(plugins.plugins); + } + + // always the last plugin + src.push(path.join(pluginsDir, "zzz-last-plugin.js")); + + // calculate version string + var boomerangVersion = releaseVersion + "." + buildRevision; + + // + // Snippets + // + var autoXhrSnippet = path.join(snippetsDir, "autoxhr-snippet.js"); + var continuitySnippet = path.join(snippetsDir, "continuity-snippet.js"); + var errorsSnippet = path.join(snippetsDir, "errors-snippet.js"); + var loaderSnippet = path.join(snippetsDir, "loader-snippet.js"); + var loaderSnippetAfterOnload = path.join(snippetsDir, "loader-snippet-after-onload.js"); + + // + // Ensure env.json exists + // + var envFile = path.resolve(path.join(testsDir, "server", "env.json")); + var env; + + if (!fs.existsSync(envFile)) { + // use the sample file if it exists + var sampleFile = path.resolve(path.join(testsDir, "server", "env.json.sample")); + + if (fs.existsSync(sampleFile)) { + console.info("Creating env.json from defaults"); + fse.copySync(sampleFile, envFile); + } + } + + // load the env.json or default content + if (fs.existsSync(envFile)) { + env = grunt.file.readJSON(envFile); + } + else { + // default content + env = { + publish: "www" + }; + } + + // + // Browser Unit Tests + // + var browserUnitTests = []; + var browserUnitTestsFile = path.join(testsDir, "browsers-unit.json"); + + if (fs.existsSync(browserUnitTestsFile)) { + browserUnitTests = JSON.parse(stripJsonComments(grunt.file.read(browserUnitTestsFile))); + } + + // + // Build SauceLabs E2E test URLs + // + var e2eTests = []; + + if (grunt.file.exists("tests/e2e/e2e.json")) { + var e2eData = JSON.parse(stripJsonComments(grunt.file.read("tests/e2e/e2e.json"))); + + e2eTests = e2eData.tests || e2eData; + } + + var e2eUrls = []; + + for (var i = 0; i < e2eTests.length; i++) { + e2eUrls.push(TEST_URL_BASE + "pages/" + e2eTests[i].path + "/" + e2eTests[i].file + ".html"); + } + + // + // Output files + // + + // build file name is based on the version number + var buildFilePrefix = "boomerang-" + boomerangVersion; + var buildPathPrefix = path.join(BUILD_PATH, buildFilePrefix); + + var testBuildFilePrefix = "boomerang"; + var testBuildPathPrefix = path.join(TEST_BUILD_PATH, testBuildFilePrefix); + + var buildDebug = buildPathPrefix + "-debug." + buildSuffix + "js"; + var buildDebugGz = buildPathPrefix + "-debug." + buildSuffix + "js.gz"; + var buildDebugMin = buildPathPrefix + "-debug." + buildSuffix + "min.js"; + var buildDebugMinGz = buildPathPrefix + "-debug." + buildSuffix + "min.js.gz"; + var buildRelease = buildPathPrefix + "." + buildSuffix + "js"; + var buildReleaseGz = buildPathPrefix + "." + buildSuffix + "js.gz"; + var buildReleaseMin = buildPathPrefix + "." + buildSuffix + "min.js"; + var buildReleaseMinGz = buildPathPrefix + "." + buildSuffix + "min.js.gz"; + var buildTest = testBuildPathPrefix + "-latest-debug.js"; + var buildTestMin = testBuildPathPrefix + "-latest-debug.min.js"; + + var buildPluginsDir = path.join(BUILD_PATH, "plugins"); + var buildSnippetsDir = path.join(BUILD_PATH, "snippets"); + var buildSnippetsStrippedDir = path.join(BUILD_PATH, "snippets.stripped"); + + // + // Build configuration + // + var buildConfig = getBuildConfig(); + + var bannerFilePathRelative = "./lib/banner.txt"; + var bannerFilePathAbsolute = path.resolve(bannerFilePathRelative); + var bannerString = grunt.file.read(bannerFilePathAbsolute); + + // Load perf test tasks + if (fs.existsSync(perfTestsDir)) { + // The perf tests use NodeJS 8+ features such as async/await and util.promisify + var nodeVersionMajor = Number(process.version.match(/^v(\d+)\.\d+/)[1]); + + if (nodeVersionMajor >= 8) { + grunt.registerTask( + "perf-tests", + "Tests Performance", + require("./tests/perf/perf-tests")); + + grunt.registerTask( + "perf-compare", + "Compares current Performance to Baseline", + require("./tests/perf/perf-compare")); + } + else { + grunt.log.writeln("Warning: Node version " + + process.version + + " does not support async or util.promisify, used by perf tests."); + + grunt.log.writeln("Use NodeJS 8+ to run perf tests."); + } + } + + return { + // package info + pkg: pkg, + + // + // Variables to use in tasks + // + buildConfig: buildConfig, + boomerangVersion: boomerangVersion, + buildFilePrefix: buildFilePrefix, + buildPathPrefix: buildPathPrefix, + testBuildPathPrefix: testBuildPathPrefix, + buildSuffix: buildSuffix, + + // + // Tasks + // + githash: { + main: { + options: {} + } + }, + concat: { + options: { + stripBanners: false, + seperator: ";" + }, + debug: { + src: src, + dest: buildDebug + }, + "debug-tests": { + src: [src, "tests/boomerang-test-plugin.js"], + dest: buildTest + }, + release: { + src: src, + dest: buildRelease + }, + autoXhrSnippet: { + src: autoXhrSnippet, + dest: path.join(pageTemplateSnippetsDir, "instrumentXHRSnippetNoScript.tpl") + }, + continuitySnippet: { + src: continuitySnippet, + dest: path.join(pageTemplateSnippetsDir, "continuitySnippetNoScript.tpl") + }, + errorsSnippet: { + src: errorsSnippet, + dest: path.join(pageTemplateSnippetsDir, "captureErrorsSnippetNoScript.tpl") + }, + loaderSnippet: { + src: loaderSnippet, + dest: path.join(pageTemplateSnippetsDir, "boomerangSnippetNoScript.tpl") + }, + loaderSnippetAfterOnload: { + src: loaderSnippetAfterOnload, + dest: path.join(pageTemplateSnippetsDir, "boomerangAfterOnloadSnippetNoScript.tpl") + } + }, + mkdir: { + test: { + options: { + mode: "0777", + create: [TEST_RESULTS_PATH] + } + }, + build: { + options: { + mode: "0777", + create: [BUILD_PATH] + } + } + }, + eslint: { + target: LINT_TARGETS, + fix: { + options: { + fix: true + }, + src: LINT_TARGETS + } + }, + "string-replace": { + all: { + files: [ + { + src: buildRelease, + dest: buildRelease + }, + { + src: buildDebug, + dest: buildDebug + }, + { + src: buildTest, + dest: buildTest + } + ], + options: { + replacements: [ + { + // Replace 1.0 with 1.[0 or jenkins build #].[date] + pattern: "%boomerang_version%", + replacement: boomerangVersion + }, + { + // strip out BOOMR = window.BOOMR || {}; in plugins + pattern: /BOOMR\s*=\s*window\.BOOMR\s*\|\|\s*{};/g, + replacement: "" + }, + { + // strip out BOOMR.plugins = BOOMR.plugins || {}; in plugins + pattern: /BOOMR\.plugins\s*=\s*BOOMR\.plugins\s*\|\|\s*{};/g, + replacement: "" + }, + { + pattern: /beacon_urls_allowed: \[\]/, + replacement: "beacon_urls_allowed: [" + buildConfig.beaconUrlsAllowed + "]" + } + ] + } + }, + "debug-tests": { + files: [{ + src: buildTest, + dest: buildTest + }], + options: { + replacements: [ + { + pattern: "%boomerang_version%", + replacement: "1.0.0" + }, + { + // Send beacons to null + pattern: /beacon_url: .*/, + replacement: "beacon_url: \"/beacon\"," + } + ] + } + }, + release: { + files: [{ + src: buildRelease, + dest: buildRelease + }], + options: { + // strip out some NOPs + replacements: [ + { + pattern: /else{}/g, + replacement: "" + }, + { + pattern: /\(window\)\);/g, + replacement: "\(window\)\);\n" + }, + { + pattern: /\(\)\);\(function\(/g, + replacement: "\(\)\);\n(function(" + } + ] + } + }, + "doc-source-code": { + files: [ + { + "./": "build/doc/boomerangjs/**/*.html" + } + ], + options: { + replacements: [ + { + pattern: /%minified_consent_inline_plugin_code%/g, + replacement: fileForHtml.bind(this, "build/plugins/consent-inlined-plugin.min.js") + }, + { + pattern: /%loader_snippet%/g, + replacement: fileForHtml.bind(this, "build/snippets.stripped/loader-snippet.js") + }, + { + pattern: /%minified_loader_snippet%/g, + replacement: fileForHtml.bind(this, "build/snippets/loader-snippet.min.js") + }, + { + pattern: /%delayed_loader_snippet%/g, + replacement: fileForHtml.bind(this, "build/snippets.stripped/loader-snippet-after-onload.js") + }, + { + pattern: /%minified_delayed_loader_snippet%/g, + replacement: fileForHtml.bind(this, "build/snippets/loader-snippet-after-onload.min.js") + }, + { + pattern: /\/\* eslint-.*\*\/\n/g, + replacement: "" + }, + { + pattern: /%autoxhr_snippet%/g, + replacement: fileForHtml.bind(this, "build/snippets.stripped/autoxhr-snippet.js") + }, + { + pattern: /%minified_autoxhr_snippet%/g, + replacement: fileForHtml.bind(this, "build/snippets/autoxhr-snippet.min.js") + }, + { + pattern: /%continuity_snippet%/g, + replacement: fileForHtml.bind(this, "build/snippets.stripped/continuity-snippet.js") + }, + { + pattern: /%minified_continuity_snippet%/g, + replacement: fileForHtml.bind(this, "build/snippets/continuity-snippet.min.js") + }, + { + pattern: /%errors_snippet%/g, + replacement: fileForHtml.bind(this, "build/snippets.stripped/errors-snippet.js") + }, + { + pattern: /%minified_errors_snippet%/g, + replacement: fileForHtml.bind(this, "build/snippets/errors-snippet.min.js") + } + ] + } + }, + readme: { + files: [ + { + src: "doc/README.template.md", + dest: "README.md" + } + ], + options: { + replacements: [ + { + pattern: /%loader_snippet%/g, + // not escaping for HTML in a Markdown file + replacement: grunt.file.read.bind(this, "snippets/loader-snippet.js") + }, + { + pattern: /%minified_loader_snippet%/g, + // not escaping for HTML in a Markdown file + replacement: grunt.file.read.bind(this, "build/snippets/loader-snippet.min.js") + } + ] + } + }, + "plugins-remove-sourcemappingurl": { + files: [ + { + "./": path.join(buildPluginsDir, "*.min.js") + } + ], + options: { + replacements: [ + { + pattern: /\n\/\/# sourceMappingURL=.*/g, + replacement: "" + } + ] + } + }, + "remove-sourcemappingurl": { + files: [ + { + src: buildReleaseMin, + dest: buildReleaseMin + } + ], + options: { + replacements: [ + { + pattern: /\/\/# sourceMappingURL=.*/g, + replacement: "" + } + ] + } + }, + "eslint-rules": { + files: [ + { + src: "README.md", + dest: "README.md" + }, + { + src: path.join(pageTemplateSnippetsDir, "boomerangSnippetNoScript.tpl"), + dest: path.join(pageTemplateSnippetsDir, "boomerangSnippetNoScript.tpl") + } + ], + options: { + replacements: [ + { + pattern: /\/\* eslint-.*\*\/\n/g, + replacement: "" + } + ] + } + } + }, + strip_code: { + debug: { + files: [{ + src: buildRelease + }], + options: { + start_comment: "BEGIN_DEBUG", + end_comment: "END_DEBUG" + } + }, + "debug-log": { + files: [{ + src: buildRelease + }], + options: { + patterns: [ + /BOOMR\.debug\(.*\);/g, + /BOOMR\.info\(.*\);/g, + /BOOMR\.warn\(.*\);/g, + /BOOMR\.error\(.*\);/g, + /debugLog\(.*\);/g + ] + } + }, + prod: { + files: [ + { + src: buildDebug + }, + { + src: "<%= testBuildPathPrefix %>*.js" + } + ], + options: { + start_comment: "BEGIN_PROD", + end_comment: "END_PROD" + } + }, + "test-code": { + files: [{ + src: [ + "README.md" + ] + }], + options: { + start_comment: "BEGIN_TEST_CODE", + end_comment: "END_TEST_CODE" + } + }, + "snippets": { + files: [{ + src: path.join(buildSnippetsStrippedDir, "*.js") + }], + options: { + start_comment: "BEGIN_TEST_CODE", + end_comment: "END_TEST_CODE" + } + } + }, + copy: { + webserver: { + files: [ + { + expand: true, + nonull: true, + cwd: "tests/", + src: "**/*", + force: true, + dest: env.publish + "/" + } + ] + }, + "coverage-e2e": { + files: [ + { + src: [ + "tests/pages/**/*", + "tests/e2e/**/*", + "tests/server/**/*", + "tests/vendor/**/*", + "tests/test-templates/**/*", + "tests/boomerang-test-framework.js", + "tests/boomerang-test-plugin.js" + ], + force: true, + dest: "tests/instrumented/" + } + ] + }, + "perf-baseline": { + files: [ + { + expand: false, + nonull: true, + src: "tests/perf/results/metrics.json", + force: true, + dest: "tests/perf/results/baseline.json" + } + ] + }, + "snippets-stripped": { + files: [ + { + expand: true, + nonull: true, + cwd: snippetsDir, + src: "*.js", + force: true, + dest: buildSnippetsStrippedDir + } + ] + } + }, + uglify: { + options: { + banner: bannerString + "/* Boomerang Version: <%= boomerangVersion %> " + + (grunt.option("commit") || "<%= githash.main.hash %>") + " */\n" + }, + default: { + options: DEFAULT_UGLIFY_BOOMERANGJS_OPTIONS, + files: [{ + expand: true, + cwd: "build/", + src: [ + "<%= buildFilePrefix %>-debug.<%= buildSuffix %>js", + "<%= buildFilePrefix %>.<%= buildSuffix %>js" + ], + dest: "build/", + ext: ".min.js", + extDot: "last" + }] + }, + "debug-test-min": { + options: DEFAULT_UGLIFY_BOOMERANGJS_OPTIONS, + files: [{ + src: buildTest, + dest: buildTestMin + }] + }, + "inline-consent-plugin": { + options: { + preserveComments: false, + mangle: true, + banner: "", + sourceMap: false, + compress: { + sequences: false + } + }, + files: [{ + src: "plugins/consent-inlined-plugin.js", + dest: path.join(pageTemplateSnippetsDir, "consentInlinePluginNoScriptMin.tpl") + }] + }, + plugins: { + options: { + preserveComments: false, + mangle: true, + banner: "", + sourceMap: true, + compress: { + sequences: false + } + }, + files: [{ + expand: true, + cwd: "plugins/", + src: ["./*.js"], + dest: buildPluginsDir, + ext: ".min.js", + extDot: "first" + }] + }, + snippets: { + options: { + preserveComments: false, + mangle: true, + banner: "", + // NOTE: Not compressing so things like our optimization aren't removed + compress: false + }, + files: [{ + expand: true, + cwd: buildSnippetsStrippedDir, + src: [ + "*.js" + ], + dest: buildSnippetsDir, + ext: ".min.js", + extDot: "first" + }] + } + }, + compress: { + main: { + options: { + mode: "gzip", + level: 9 + }, + files: [ + { + src: buildRelease, + dest: buildReleaseGz + }, + { + src: buildDebug, + dest: buildDebugGz + }, + { + src: buildReleaseMin, + dest: buildReleaseMinGz + }, + { + src: buildDebugMin, + dest: buildDebugMinGz + } + ] + }, + plugins: { + options: { + mode: "gzip", + level: 9 + }, + files: [{ + expand: true, + cwd: buildPluginsDir, + src: "./*.js", + dest: buildPluginsDir, + ext: ".min.js.gz", + extDot: "first" + }] + } + }, + "babel": { + "spa-react-test-templates": { + files: { + "tests/page-templates/12-react/support/app-component.js": "tests/page-templates/12-react/support/app.jsx" + } + } + }, + browserify: { + "spa-react-test-templates": { + files: { + "tests/page-templates/12-react/support/app.js": [ + "node_modules/react/dist/react.js", + "node_modules/react-dom/dist/react-dom.js", + "node_modules/react-router/umd/ReactRouter.js", + "tests/page-templates/12-react/support/app-component.js" + ] + } + } + }, + filesize: { + csv: { + files: [{ + expand: true, + cwd: "build", + src: ["./**/*.min.js", "./**/*.min.js.gz"], + ext: ".min.js.gz", + extDot: "first" + }], + options: { + output: { + path: "tests/results/filesizes.csv", + // https://github.com/k-maru/grunt-filesize/issues/8 + format: "\"{filename}\",{size},{kb},{now:YYYYMMDDhhmmss};", + append: true + } + } + }, + console: { + files: [{ + expand: true, + cwd: "build", + src: ["./**/*.min.js", "./**/*.min.js.gz"], + ext: ".min.js.gz", + extDot: "first" + }], + options: { + output: { + format: "{filename}: Size of {size:0,0} bytes ({kb:0.00} kilobyte)", + stdout: true + } + } + } + }, + clean: { + options: {}, + build: [ + "build/*", + "tests/build/*", + "tests/results/*.tap", + "tests/results/*.xml", + "tests/coverage/*", + "tests/pages/**/*", + "tests/instrumented/**/*" + ], + "spa-react-test-templates": [ + "tests/pages/12-react/support/app.js", + "tests/page-templates/12-react/support/app.js", + "tests/page-templates/12-react/support/app-component.js", + "tests/page-templates/12-react/support/*.map" + ], + src: ["plugins/*~", "*.js~", "*.html~"] + }, + karma: { + options: { + singleRun: true, + colors: true, + configFile: "./tests/karma.config.js", + basePath: "./", + files: [ + // relative to tests/ dir + "vendor/mocha/mocha.css", + "vendor/mocha/mocha.js", + "vendor/assertive-chai/dist/assertive-chai.js", + "boomerang-test-framework.js", + "unit/*.js", + "build/*.js" + ], + hostname: DEFAULT_TEST_MAIN_DOMAIN, + SELENIUM_ADDRESS: grunt.option("selenium-address") + }, + unit: { + browsers: [DEFAULT_BROWSER] + }, + coverage: { + browsers: [DEFAULT_BROWSER], + configFile: "./tests/karma.coverage.config.js" + }, + all: { + browsers: [ + "Chrome", + "ChromeHeadless", + "Edge", + "Firefox", + "FirefoxHeadless", + "IE", + "Opera", + "Safari", + "Edge" + ] + }, + allHeadless: { + browsers: [ + "ChromeHeadless", + "FirefoxHeadless" + ] + }, + Chrome: { + browsers: ["Chrome"] + }, + ChromeHeadless: { + browsers: ["ChromeHeadless"] + }, + Firefox: { + browsers: ["Firefox"] + }, + FirefoxHeadless: { + browsers: ["FirefoxHeadless"] + }, + IE: { + browsers: ["IE"] + }, + Edge: { + browsers: ["Edge"] + }, + Opera: { + browsers: ["Opera"] + }, + Safari: { + browsers: ["Safari"] + }, + debug: { + singleRun: false + } + }, + protractor: { + options: { + noColor: false, + keepAlive: false + }, + Chrome: { + options: { + configFile: "tests/protractor-config/chrome.js", + args: { + seleniumAddress: SELENIUM_ADDRESS, + specs: ["tests/e2e/e2e.js"], + baseUrl: E2E_BASE_URL + } + } + }, + ChromeHeadless: { + options: { + configFile: "tests/protractor-config/chromeheadless.js", + args: { + seleniumAddress: SELENIUM_ADDRESS, + specs: ["tests/e2e/e2e.js"], + baseUrl: E2E_BASE_URL + } + } + }, + Firefox: { + options: { + configFile: "tests/protractor-config/firefox.js", + args: { + seleniumAddress: SELENIUM_ADDRESS, + specs: ["tests/e2e/e2e.js"], + baseUrl: E2E_BASE_URL + } + } + }, + FirefoxHeadless: { + options: { + configFile: "tests/protractor-config/firefoxheadless.js", + args: { + seleniumAddress: SELENIUM_ADDRESS, + specs: ["tests/e2e/e2e.js"], + baseUrl: E2E_BASE_URL + } + } + }, + Edge: { + options: { + configFile: "tests/protractor-config/edge.js", + args: { + seleniumAddress: SELENIUM_ADDRESS, + specs: ["tests/e2e/e2e.js"], + baseUrl: E2E_BASE_URL + } + } + }, + IE: { + options: { + configFile: "tests/protractor-config/ie.js", + args: { + seleniumAddress: SELENIUM_ADDRESS, + specs: ["tests/e2e/e2e.js"], + baseUrl: E2E_BASE_URL + } + } + }, + Safari: { + options: { + configFile: "tests/protractor-config/safari.js", + args: { + seleniumAddress: SELENIUM_ADDRESS, + specs: ["tests/e2e/e2e.js"], + baseUrl: E2E_BASE_URL + } + } + }, + debug: { + options: { + configFile: "tests/protractor-config/debug.js", + args: { + seleniumAddress: SELENIUM_ADDRESS, + specs: ["tests/e2e/e2e-debug.js"], + baseUrl: E2E_BASE_URL + } + } + } + }, + protractor_webdriver: { + options: { + keepAlive: true, + command: "webdriver-manager start " + (webDriverVersions || "") + }, + e2e: {} + }, + // Instrument code for e2e tests coverage + instrument: { + files: [ + "tests/build/*.js" + ], + options: { + lazy: true, + basePath: "tests/instrumented" + } + }, + protractor_coverage: { + options: { + noColor: false, + keepAlive: true, + coverageDir: "tests/instrumented/coverage" + }, + ChromeHeadless: { + options: { + configFile: "tests/protractor-config/chromeheadless.js", + args: { + seleniumAddress: SELENIUM_ADDRESS, + specs: ["tests/instrumented/tests/e2e/e2e.js"], + baseUrl: E2E_BASE_URL + } + } + } + }, + makeReport: { + src: "tests/instrumented/coverage/**/*.json", + options: { + type: "html", + dir: "tests/coverage/e2e", + print: "detail" + } + }, + express: { + options: { + port: TEST_DEBUG_PORT, + hostname: "0.0.0.0" + }, + dev: { + options: { + script: "tests/server/app.js", + args: [TEST_SCHEME] + } + }, + secondary: { + options: { + script: "tests/server/app.js", + port: (TEST_DEBUG_PORT + 1), + args: [TEST_SCHEME] + } + }, + "coverage-e2e": { + options: { + script: "tests/instrumented/tests/server/app.js", + args: [TEST_SCHEME], + bases: ["tests/instrumented/tests/e2e", "tests/instrumented/tests/pages", "tests/instrumented/tests/server"] + } + }, + doc: { + options: { + port: (TEST_DEBUG_PORT - 1), + script: "tests/server/doc-server.js" + } + } + }, + "saucelabs-mocha": { + unit: { + options: Object.assign({ + urls: [TEST_URL_BASE + "unit/"], + testname: "Boomerang Unit Tests", + browsers: browserUnitTests + }, SAUCELABS_CONFIG) + }, + "unit-debug": { + options: Object.assign({ + urls: [TEST_URL_BASE + "unit/"], + testname: "Boomerang Unit Tests", + browsers: [{ + browserName: "internet explorer", + "version": "11", + "platform": "Windows 8.1" + }] + }, SAUCELABS_CONFIG) + }, + e2e: { + options: Object.assign({ + urls: e2eUrls, + testname: "Boomerang E2E Tests", + browsers: browserUnitTests + }, SAUCELABS_CONFIG) + }, + "e2e-debug": { + options: Object.assign({ + urls: e2eUrls, + testname: "Boomerang E2E Tests", + browsers: [{ + browserName: "internet explorer", + "version": "11", + "platform": "Windows 8.1" + }] + }, SAUCELABS_CONFIG) + } + }, + jsdoc: { + dist: { + src: ["boomerang.js", "plugins/*.js"], + jsdoc: "./node_modules/jsdoc/jsdoc.js", + options: { + destination: "build/doc", + package: "package.json", + readme: "README.md", + configure: "jsdoc.conf.json", + template: "doc-template" + } + } + }, + watch: { + test: { + files: [ + "tests/e2e/*.js", + "tests/page-template-snippets/**/*", + "tests/page-templates/**/*", + "tests/unit/**/*", + "tests/test-templates/**/*.js", + "!tests/page-templates/12-react/support/*.jsx", + "!*.#*", + "!*~", + "!#*#" + ], + tasks: ["pages-builder"] + }, + "test-react": { + files: [ + "tests/page-templates/12-react/support/*.jsx" + ], + tasks: ["test:build:react"] + }, + boomerang: { + files: [ + "boomerang.js", + "plugins/*.js", + "plugins.json" + ], + tasks: ["build:test"] + }, + express: { + files: [ + "tests/server/*.js" + ], + tasks: ["express:dev", "express:secondary"] + }, + doc: { + files: [ + "boomerang.js", + "plugins/*.js", + "doc/**/**", + "README.md" + ], + tasks: ["clean", "jsdoc", "doc-source-code"] + } + }, + shell: { + "generate-certificate": { + stdin: true, + command: [ + "openssl req", + "-x509", + "-newkey rsa:4096", + "-sha256", + "-days 3560", + "-nodes", + "-keyout tests/server/certs/" + DEFAULT_TEST_MAIN_DOMAIN + ".pem", + "-out tests/server/certs/" + DEFAULT_TEST_MAIN_DOMAIN + ".crt", + "-subj '/CN=" + DEFAULT_TEST_MAIN_DOMAIN + "'", + "-extensions san", + "-config /dev/stdin" + ].join(" ") + } + } + }; +} + +// +// Grunt call +// +module.exports = function() { + // + // Paths + // + var testsDir = path.join(__dirname, "tests"); + var perfTestsDir = path.join(testsDir, "perf"); + var pageTemplateSnippetsDir = path.join(testsDir, "page-template-snippets"); + var pluginsDir = path.join(__dirname, "plugins"); + var snippetsDir = path.join(__dirname, "snippets"); + // + // Config + // + + grunt.initConfig(getConfig()); + var buildConfig = getBuildConfig(); + + grunt.loadNpmTasks("grunt-eslint"); + grunt.loadNpmTasks("grunt-babel"); + grunt.loadNpmTasks("grunt-browserify"); + grunt.loadNpmTasks("grunt-express-server"); + grunt.loadNpmTasks("grunt-karma"); + grunt.loadNpmTasks("grunt-contrib-concat"); + grunt.loadNpmTasks("grunt-string-replace"); + grunt.loadNpmTasks("grunt-contrib-uglify"); + grunt.loadNpmTasks("grunt-contrib-clean"); + grunt.loadNpmTasks("grunt-contrib-copy"); + grunt.loadNpmTasks("grunt-contrib-compress"); + grunt.loadNpmTasks("grunt-filesize"); + grunt.loadNpmTasks("grunt-mkdir"); + grunt.loadNpmTasks("grunt-protractor-runner"); + grunt.loadNpmTasks("grunt-protractor-webdriver"); + grunt.loadNpmTasks("grunt-protractor-coverage"); + grunt.loadNpmTasks("grunt-template"); + grunt.loadNpmTasks("grunt-saucelabs"); + grunt.loadNpmTasks("grunt-strip-code"); + grunt.loadNpmTasks("grunt-contrib-watch"); + grunt.loadNpmTasks("grunt-jsdoc"); + grunt.loadNpmTasks("grunt-githash"); + grunt.loadNpmTasks("grunt-shell"); + grunt.loadNpmTasks("grunt-istanbul"); + + // tasks/*.js + if (grunt.file.exists("tasks")) { + grunt.loadTasks("tasks"); + } + + grunt.registerTask("pages-builder", "Builds our HTML tests/pages", function() { + return require(path.join(testsDir, "builder"))( + this, + path.join(testsDir, "page-templates"), + pageTemplateSnippetsDir, + path.join(testsDir, "pages"), + path.join(testsDir, "e2e"), + path.join(testsDir, "e2e", "e2e.json") + ); + }); + + grunt.registerTask("pages-builder-perf", "Builds our HTML tests/pages for Perf Testing", function() { + return require(path.join(testsDir, "builder"))( + this, + path.join(testsDir, "perf", "page-templates"), + pageTemplateSnippetsDir, + path.join(testsDir, "perf", "pages"), + path.join(testsDir, "perf", "pages"), + path.join(testsDir, "perf", "scenarios.json") + ); + }); + + // Custom aliases for configured grunt tasks + var aliases = { + "default": ["lint", "build", "test", "metrics"], + + // + // Build + // + "build": [ + "concat", + "build:apply-templates", + "githash", + "copy:snippets-stripped", + "strip_code:snippets", + "uglify", + "string-replace:remove-sourcemappingurl", + "compress", + "metrics" + ], + + "build:test": [ + "concat:debug", + "concat:debug-tests", + "!build:apply-templates", + "uglify:debug-test-min", + "uglify:inline-consent-plugin" + ], + + // Build steps + "build:apply-templates": [ + "string-replace:all", + "!string-replace:debug-tests", + "string-replace:release", + "!strip_code:debug", + "!strip_code:debug-log", + "!strip_code:prod" + ], + + // metrics to generate + "metrics": ["filesize:console"], + + // + // Lint + // + "lint": ["eslint"], + "lint:fix": ["eslint:fix"], + + // + // Docs + // + "doc-source-code": [ + "uglify:plugins", + "string-replace:plugins-remove-sourcemappingurl", + "string-replace:doc-source-code" + ], + + // Documentation + "doc": [ + "string-replace:readme", + "strip_code:test-code", + "string-replace:eslint-rules", + "jsdoc", + "strip_code:test-code", + "doc-source-code" + ], + "test:doc": ["clean", "jsdoc", "doc-source-code", "express:doc", "watch:doc"], + + // + // Test tasks + // + "test": ["build", "test:build", "test:unit", "test:e2e"], + + // builds test files + "test:build": ["mkdir:test", "test:build:react", "pages-builder", "build"], + + // react test files + "test:build:react": ["babel:spa-react-test-templates", "browserify:spa-react-test-templates"], + + // useful for debugging tests, leaves a webbrowser open at http://localhost:3001 + "test:debug": [ + "test:build", + "build:test", + "express:dev", + "express:secondary", + "test:debug:watch" + ], + + // open your browser to http://localhost:4000/debug.html to debug + "test:karma:debug": ["test:build", "build:test", "karma:debug"], + + // unit tests + "test:unit": ["test:build", "build", "karma:unit:" + DEFAULT_BROWSER], + "test:unit:all": ["build", "karma:all"], + "test:unit:allHeadless": ["build", "karma:allHeadless"], + "test:unit:Chrome": ["build", "karma:Chrome"], + "test:unit:ChromeHeadless": ["build", "karma:ChromeHeadless"], + "test:unit:Firefox": ["build", "karma:Firefox"], + "test:unit:FirefoxHeadless": ["build", "karma:FirefoxHeadless"], + "test:unit:Edge": ["build", "karma:Edge"], + "test:unit:IE": ["build", "karma:IE"], + "test:unit:Opera": ["build", "karma:Opera"], + "test:unit:Safari": ["build", "karma:Safari"], + + // End-to-End tests + "test:e2e": ["test:e2e:" + DEFAULT_BROWSER], + "test:e2e:browser": ["test:build", "build", "express:dev", "express:secondary"], + "test:e2e:debug": ["test:e2e:browser", "protractor:debug"], + "test:e2e:Chrome": ["test:e2e:browser", "protractor:Chrome"], + "test:e2e:ChromeHeadless": ["test:e2e:browser", "protractor:ChromeHeadless"], + "test:e2e:Firefox": ["test:e2e:browser", "protractor:Firefox"], + "test:e2e:FirefoxHeadless": ["test:e2e:browser", "protractor:FirefoxHeadless"], + "test:e2e:Edge": ["test:e2e:browser", "protractor:Edge"], + "test:e2e:IE": ["test:e2e:browser", "protractor:IE"], + "test:e2e:Safari": ["test:e2e:browser", "protractor:Safari"], + + // SauceLabs tests + "test:matrix": ["test:matrix:unit", "test:matrix:e2e"], + "test:matrix:e2e": ["pages-builder", "saucelabs-mocha:e2e"], + "test:matrix:e2e:debug": ["pages-builder", "saucelabs-mocha:e2e-debug"], + "test:matrix:unit": ["saucelabs-mocha:unit"], + "test:matrix:unit:debug": ["saucelabs-mocha:unit-debug"], + + // + // Perf tasks + // + "perf": ["build", "pages-builder-perf", "express:dev", "perf-tests"], + "perf:baseline": ["build", "pages-builder-perf", "express:dev", "perf-tests", "copy:perf-baseline"], + "perf:compare": ["build", "pages-builder-perf", "express:dev", "perf-tests", "perf-compare"], + + // + // Coverage tasks + // + "test:unit:coverage": ["test:build", "build", "karma:coverage:ChromeHeadless"], + + "test:e2e:coverage": [ + "test:build", + "build", + "copy:coverage-e2e", + "instrument", + "express:coverage-e2e", + "protractor_webdriver", + "protractor_coverage:ChromeHeadless", + "makeReport:coverage-e2e" + ], + + "test:coverage": ["test:unit:coverage", "test:e2e:coverage"] + }; + + // launch selenium if another address wasn't provided + if (!grunt.option("selenium-address")) { + aliases["test:e2e:browser"].push("protractor_webdriver"); + } + + function isAlias(task) { + return aliases[task] ? true : false; + } + + // tasks that need to be run more than once (denoted by starting with !) + var rerunTasks = {}; + + function resolveAlias(task) { + var tasks = aliases[task], + resolved = false; + + function checkDuplicates(insertableTask) { + if (rerunTasks[insertableTask]) { + // always return true for tasks that were marked as rerun + return true; + } + + return tasks.indexOf(insertableTask) === -1; + } + + while (!resolved) { + if (tasks.filter(isAlias).length === 0) { + resolved = true; + } + + for (var index = 0; index < tasks.length; index++) { + // if the task starts with !, it should be run more than once + if (tasks[index].startsWith("!")) { + // trim back to the real name + tasks[index] = tasks[index].substr(1); + + // keep track of this task + rerunTasks[tasks[index]] = true; + } + + if (isAlias(tasks[index])) { + var aliasTask = tasks[index]; + var beforeTask = tasks.slice(0, index); + var afterTask = tasks.slice(index + 1, tasks.length); + var insertTask = aliases[aliasTask].filter(checkDuplicates); + + tasks = [].concat(beforeTask, insertTask, afterTask); + } + } + } + + return tasks; + } + + Object.keys(aliases).map(function(alias) { + var resolved = resolveAlias(alias); + + grunt.log.debug("Resolving task alias: " + alias + " to " + JSON.stringify(resolved)); + grunt.registerTask(alias, resolved); + }); + + // Don't re-generate Docs during test:debug builds running + grunt.registerTask("test:debug:watch", function() { + delete grunt.config.data.watch.doc; + grunt.task.run("watch"); + }); + + // + // build:flavors + // + grunt.registerTask("build:flavors", function() { + var done = this.async(); + + // config + var buildNumber = grunt.option("build-number") || 0; + + if (buildNumber === 0) { + grunt.fail.fatal("--build-number must be specified"); + } + + runForEachFlavor( + "build", + "build", + [ + "--build-number=" + buildNumber + ], + true, + done); + }); + + // + // test:unit:flavors + // + grunt.registerTask("test:unit:flavors", function() { + var done = this.async(); + + runForEachFlavor( + "test-unit", + "test:unit", + [], + false, + done); + }); + + // + // test:e2e:flavors + // + grunt.registerTask("test:e2e:flavors", function() { + var done = this.async(); + + // protractor_webdriver should've already been started so we can reuse it + grunt.task.requires("protractor_webdriver"); + + runForEachFlavor( + "test-e2e", + "test:e2e", + [ + "--selenium-address=" + SELENIUM_ADDRESS + ], + false, + done); + }); +}; + +/** + * Runs the specific task for each build flavor + * + * @param {string} logName Log file name + * @param {string} taskName Grunt task name + * @param {string[]} args Grunt command-line arguments + * @param {boolean} failOnError Fail on error + * @param {function} done Callback + */ +function runForEachFlavor(logName, taskName, args, failOnError, done) { + // get all plugin definitions + var plugins = grunt.file.readJSON("plugins.json"); + + var pkg = grunt.file.readJSON("package.json"); + var buildNumber = grunt.option("build-number") || 0; + var releaseVersion = pkg.releaseVersion + "." + buildNumber; + + var flavorList = []; + + // Add the base "full" version as its also a flavor. + flavorList.push(releaseVersion + ".0"); + + // Build list of all flavor version codes in this release + for (var f in plugins.flavors) { + if (!plugins.flavors.hasOwnProperty(f)) { + continue; + } + + var buildFlavor = plugins.flavors[f]; + + flavorList.push(releaseVersion + "." + buildFlavor.revision); + } + + // output log + var outputLogFile = path.join("build", logName + ".full.log"); + var outputLogStream = fs.createWriteStream(outputLogFile, { + flags: "a" + }); + + grunt.log.ok("Running " + taskName + " on full build"); + + // Let the environment know the flavor + process.env.BUILD_FLAVOR = "full"; + + var argsWithTask = args.concat(taskName); + var argsWithTaskAndFlavors = argsWithTask.concat([ + "--parent-flavor-version=" + releaseVersion + ".0", + "--is-parent-flavor=true", + "--is-child-flavor=false" + ]); + // + // Full build + // + var child = grunt.util.spawn({ + grunt: true, + args: argsWithTaskAndFlavors + }, function(error, result, code) { + outputLogStream.close(); + + if (error) { + logSpawnError(error, result); + + if (failOnError) { + grunt.fail.fatal("Build failed"); + + return done("Build failed"); + } + } + + // set parent flavor version + var argsWithTaskAndPVersion = argsWithTask.concat([ + "--parent-flavor-version=" + releaseVersion + ".0", + "--is-parent-flavor=false", + "--is-child-flavor=true" + ]); + // + // Each flavor + // + + async.eachSeries(Object.keys(plugins.flavors), function(flavor, cb) { + // Let the environment know the flavor + process.env.BUILD_FLAVOR = flavor; + + // output log + outputLogFile = path.join("build", logName + "." + flavor + ".log"); + outputLogStream = fs.createWriteStream(outputLogFile, { + flags: "a" + }); + + grunt.log.ok("Running " + taskName + " on " + flavor); + + var child2 = grunt.util.spawn({ + grunt: true, + args: argsWithTaskAndPVersion.concat("--build-flavor=" + flavor) + }, function(errorFlavor, resultFlavor) { + outputLogStream.close(); + + if (errorFlavor) { + logSpawnError(errorFlavor, resultFlavor); + } + + cb((failOnError && errorFlavor) ? ("Error with " + flavor) : undefined); + }); + + child2.stdout.pipe(outputLogStream); + child2.stderr.pipe(outputLogStream); + }, done); + }); + + child.stdout.pipe(outputLogStream); + child.stderr.pipe(outputLogStream); +} + +function logSpawnError(error, result) { + // Log output + console.log(error); + + if (result.stderr) { + console.error("********* stderr *********"); + console.error(result.stderr); + } + + if (result.stdout) { + console.error("********* stdout *********"); + console.error(result.stdout); + } +} diff --git a/LICENSE.txt b/LICENSE.txt index 8243188df..27f06ddde 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,6 +1,9 @@ Software Copyright License Agreement (BSD License) -Copyright (c) 2010, Yahoo! Inc. +Copyright (c) 2011, Yahoo! Inc. +Copyright (c) 2011-2012, Log-Normal, Inc. +Copyright (c) 2012-2017, SOASTA, Inc. +Copyright (c) 2017-2023, Akamai Technologies, Inc. All rights reserved. Redistribution and use of this software in source and binary forms, diff --git a/Makefile b/Makefile deleted file mode 100644 index ceac7221f..000000000 --- a/Makefile +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (c) 2011, Yahoo! Inc. All rights reserved. -# Copyrights licensed under the BSD License. See the accompanying LICENSE.txt file for terms. - -PLUGINS := - -VERSION := $(shell sed -ne '/^BOOMR\.version/{s/^.*"\([^"]*\)".*/\1/;p;q;}' boomerang.js) -DATE := $(shell date +%s) - -MINIFIER := cat - -all: boomerang-$(VERSION).$(DATE).js - -usage: - echo "Create a release version of boomerang:" - echo " make" - echo "" - echo "Create a release version of boomerang with the dns plugin:" - echo " make PLUGINS=dns.js" - echo "" - echo "Create a yuicompressor minified release version of boomerang:" - echo " make MINIFIER=\"java -jar /path/to/yuicompressor-2.4.2.jar --type js\"" - echo "" - echo "Create a jsmin minified release version of boomerang:" - echo " make MINIFIER=\"/path/to/jsmin\"" - echo "" - -boomerang-$(VERSION).$(DATE).js: boomerang.js $(PLUGINS) - echo - echo "Making $@ ..." - echo "using plugins: $(PLUGINS)..." - cat boomerang.js $(PLUGINS) zzz_last_plugin.js | sed -e 's/^\(BOOMR\.version = "\)$(VERSION)\("\)/\1$(VERSION).$(DATE)\2/' | $(MINIFIER) | perl -pe "s/\(window\)\);/\(window\)\);\n/g" > $@ && echo "done" - echo - -.PHONY: all -.SILENT: diff --git a/README.md b/README.md index c8ebf3bd1..70fb85b9f 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,503 @@ -Copyright (c) 2011, Yahoo! Inc. All rights reserved. -Copyrights licensed under the BSD License. See the accompanying LICENSE.txt file for terms. +**boomerang always comes back, except when it hits something.** -boomerang always comes back, except when it hits something. +# Summary -This piece of javascript measures a whole bunch of performance characteristics of your user's -web browsing experience. All you have to do is stick it into your web pages and call the -init() method. +[![Join the chat at https://gitter.im/SOASTA/boomerang](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/SOASTA/boomerang) -documentation is in the docs/ directory, it's all HTML, so your best bet is to check it out -and view it locally, though it works best through a web server (you'll need cookies). +boomerang is a JavaScript library that measures the page load time experienced by +real users, commonly called RUM (Real User Measurement). It has the ability to +send this data back to your server for further analysis. With boomerang, you +find out exactly how fast your users think your site is. -An online version of the docs is here: http://lognormal.github.com/boomerang/doc/ +Apart from page load time, boomerang measures performance timings, metrics and +characteristics of your user's web browsing experience. All you have to do is +include it in your web pages and call the `BOOMR.init()` method. Once the +performance data is captured, it will be beaconed to your chosen URL. -The latest code and docs is available on http://github.com/lognormal/boomerang/ +boomerang is designed to be a performant and flexible library that can be adapted +to your site's needs. It has an extensive plugin architecture, and works with +both traditional and modern websites (including Single Page Apps). -Discussions are best done using github issues at https://github.com/lognormal/boomerang/issues -You'll need a github account to participate. +boomerang's goal is to not affect the load time of the page (avoiding the +[Observer Effect](https://en.wikipedia.org/wiki/Observer_effect_(information_technology))). +It can be loaded in an asynchronous way that will not delay the page load even +if `boomerang.js` is unavailable. + +# Features + +* Supports: + * IE 6+, Edge, all major versions of Firefox, Chrome, Opera, and Safari + * Desktop and mobile devices +* Captures (all optional): + * Page characteristics such as the URL and Referrer + * Overall page load times (via [NavigationTiming](https://www.w3.org/TR/navigation-timing/) if available) + * DNS, TCP, Request and Response timings (via [NavigationTiming](https://www.w3.org/TR/navigation-timing/)) + * Browser characteristics such as screen size, orientation, memory usage, visibility state + * DOM characteristics such as the number of nodes, HTML length, number of images, scripts, etc + * [ResourceTiming](https://www.w3.org/TR/resource-timing-1/) data (to reconstruct the page's Waterfall) + * Bandwidth + * Mobile connection data + * DNS latency + * JavaScript Errors + * XMLHttpRequest instrumentation + * Third-Party analytics providers IDs + * Single Page App interactions + +# Usage + +boomerang can be included on your page in one of two ways: [synchronously](#synchronously) or [asynchronously](#asynchronously). + +The asynchronous method is recommended. + + + +## The simple synchronous way + +```html + + + + +``` + +**Note:** You must include at least one plugin (it doesn't have to be `RT`) or +else the beacon will never fire. + +Each plugin has its own configuration as well -- these configuration options +should be included in the `BOOMR.init()` call: + +```javascript +BOOMR.init({ + beacon_url: "http://yoursite.com/beacon/", + ResourceTiming: { + enabled: true, + clearOnBeacon: true + } +}); +``` + + + +## The faster, more involved, asynchronous way + +Loading boomerang asynchronously ensures that even if `boomerang.js` is +unavailable (or loads slowly), your host page will not be affected. + +### 1. Add a plugin to init your code + +Create a plugin (or use the sample `zzz-last-plugin.js`) with a call +to `BOOMR.init`: + +```javascript +BOOMR.init({ + config: parameters, + ... +}); +BOOMR.t_end = new Date().getTime(); +``` + +You could also include any other code you need. For example, you could include +a timer to measure when boomerang has finished loading (as above). + +### 2. Build boomerang + +The [build process](#documentation) bundles `boomerang.js` and all of the plugins +listed in `plugins.json` (in that order). + +To build boomerang with all of your desired plugins, you would run: + +```bash +grunt clean build +``` + +This creates a deployable boomerang in the `build` directory, e.g. `build/boomerang-.min.js`. + +Install this file on your web server or origin server where your CDN can pick it +up. Set a far future +[max-age](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control) +header for it. This file will never change. + +### 3. Asynchronously include the script on your page + +There are two methods of asynchronously including boomerang on your page: by +adding it to your main document, or via the IFRAME/Preload method. + +The former method could block your `onload` event (affecting the measured +performance of your page), so the later method is recommended. + + + +#### 3.1. Adding it to the main document + +Include the following code at the _top_ of your HTML document: + +```javascript + +``` + +Best practices will suggest including all scripts at the bottom of your page. +However, that only applies to scripts that block downloading of other resources. + +Including a script this way will not block other resources, however it _will_ +block `onload`. + +Including the script at the top of your page gives it a good chance of loading +before the rest of your page does, thereby reducing the probability of it +blocking the `onload` event. + +If you don't want to block `onload` either, use the following IFRAME/Preload method: + + + +#### 3.2. Adding it via an IFRAME/Preload + +The method described in 3.1 will still block `onload` on most browsers. + +To avoid blocking `onload`, we can load boomerang in an asynchronous IFRAME or via LINK preload (for browsers that support +it). The general process is documented on in +[this blog post](https://calendar.perfplanet.com/2018/a-csp-compliant-non-blocking-script-loader/). + +For boomerang, the asynchronous loader snippet you'll use is: + +```javascript + +``` + +Minified: + +```javascript + +``` + +Change the `boomerangUrl` to the location of Boomerang on your server. + +The `id` of the script node created by this code MUST be `boomr-if-as` (for IFRAME mode) or `boomr-scr-as` (for +Preload mode) as boomerang looks for those ids to determine if it's running within an IFRAME and to determine the +URL of the script. + +boomerang will still export the `BOOMR` object to the parent window if running +inside an IFRAME, so the rest of your code should remain unchanged. + +#### 3.3. Identifying when boomerang has loaded + +If you load boomerang asynchronously, there's some uncertainty in when boomerang +has completed loading. To get around this, you can subscribe to the +`onBoomerangLoaded` Custom Event on the `document` object: + +```javascript +// Modern browsers +if (document.addEventListener) { + document.addEventListener("onBoomerangLoaded", function(e) { + // e.detail.BOOMR is a reference to the BOOMR global object + }); +} +// IE 6, 7, 8 we use onPropertyChange and look for propertyName === "onBoomerangLoaded" +else if (document.attachEvent) { + document.attachEvent("onpropertychange", function(e) { + if (!e) e=event; + if (e.propertyName === "onBoomerangLoaded") { + // e.detail.BOOMR is a reference to the BOOMR global object + } + }); +} +``` + +Note that this only works on browsers that support the CustomEvent interface, +which is Chrome (including Android), Firefox 6+ (including Android), Opera +(including Android, but not Opera Mini), Safari (including iOS), IE 6+ +(but see the code above for the special way to listen for the event on IE6, 7 & 8). + +boomerang also fires the `onBeforeBoomerangBeacon` and `onBoomerangBeacon` +events just before and during beaconing. + + + +# Installation + +There are several ways of including Boomerang in your project: + +1. Boomerang can be downloaded from the official [Boomerang Github repository](https://github.com/akamai/boomerang). + +2. NPM: `npm install boomerangjs` + +3. Bower: `bower install boomerang` + +Once fetched, see [Building Boomerang](https://akamai.github.io/boomerang/tutorial-building.html) +for more details on how to include the plugins you require. + + + +# Documentation + +Documentation is in the `docs/` directory. Boomerang documentation is +written in Markdown and is built via [JSDoc](http://usejsdoc.org/). + +You can build the current documentation by running Grunt: + +```bash +grunt jsdoc +``` + +HTML files will be built under `build/docs`. + +Open-source Boomerang Documentation is currently published at +[akamai.github.io/boomerang/](https://akamai.github.io/boomerang/). + +The team at Akamai works on mPulse Boomerang, which contains a few mPulse-specific plugins and may have additional +changes being tested before being backported to the open-source Boomerang. mPulse Boomerang usage documentation is +available at [docs.soasta.com/boomerang/](https://docs.soasta.com/boomerang/) and mPulse Boomerang API documentation +is at [developer.akamai.com/tools/boomerang/docs/](https://developer.akamai.com/tools/boomerang/docs/). + +Additional documentation: + +* [API Documentation](https://akamai.github.io/boomerang/): The `BOOMR` API +* [Building Boomerang](https://akamai.github.io/boomerang/tutorial-building.html): How to build boomerang with plugins +* [Contributing](https://akamai.github.io/boomerang/tutorial-contributing.html): Contributing to the open-source project +* [Creating Plugins](https://akamai.github.io/boomerang/tutorial-creating-plugins.html): Creating a plugin +* [Methodology](https://akamai.github.io/boomerang/tutorial-methodology.html): How boomerang works internally +* [How-Tos](https://akamai.github.io/boomerang/tutorial-howtos.html): Short recipes on how to do a bunch of things with boomerang + +# Source code + +The boomerang source code is primarily on GitHub at [github.com/akamai/boomerang](https://github.com/akamai/boomerang). + +Feel free to fork it and [contribute](https://akamai.github.io/boomerang/tutorial-contributing.html) to it. + +You can also get a [check out the releases](https://github.com/akamai/boomerang/releases) +or download a [tarball](https://github.com/akamai/boomerang/archive/master.tar.gz) or +[zip](http://github.com/akamai/boomerang/archive/master.zip) of the code. + +# Support + +We use [GitHub Issues](https://github.com/akamai/boomerang/issues) for discussions, +feature requests and bug reports. + +Get in touch at [github.com/akamai/boomerang/issues](https://github.com/akamai/boomerang/issues). + +boomerang is supported by the developers at [Akamai](http://akamai.com/), and the +awesome community of open-source developers that use and hack it. That's you. Thank you! + +# Contributions + +Boomerang is brought to you by: + +* the former [Exceptional Performance](http://developer.yahoo.com/performance/) team at the company once known as + [Yahoo!](http://www.yahoo.com/), aided by the [Yahoo! Developer Network](http://developer.yahoo.com/), +* the folks at [LogNormal](http://www.lognormal.com/), continued by +* the mPulse team at [SOASTA](https://www.soasta.com/), ongoing by +* the mPulse team at [Akamai](https://www.akamai.com/), and +* many independent contributors whose contributions are cemented in our git history + +To help out, please read our [contributing](https://akamai.github.io/boomerang/tutorial-contributing.html) page. + +# Copyright + +* _Copyright (c) 2011, Yahoo! Inc. All rights reserved._ +* _Copyright (c) 2011-2012, Log-Normal Inc. All rights reserved._ +* _Copyright (c) 2012-2017 SOASTA, Inc. All rights reserved._ +* _Copyright (c) 2017-2023, Akamai Technologies, Inc. All rights reserved._ +* _Copyrights licensed under the BSD License. See the accompanying LICENSE.txt file for terms._ diff --git a/boomerang-0.9.1280532889.js b/boomerang-0.9.1280532889.js deleted file mode 100644 index 88f7527af..000000000 --- a/boomerang-0.9.1280532889.js +++ /dev/null @@ -1,5 +0,0 @@ -/* - * Copyright (c) 2011, Yahoo! Inc. All rights reserved. - * Copyrights licensed under the BSD License. See the accompanying LICENSE file for terms. - */ -(function(a){var e,c,b,g=a.document;if(typeof BOOMR==="undefined"){BOOMR={}}if(BOOMR.version){return}BOOMR.version="0.9";e={beacon_url:"",site_domain:a.location.hostname.replace(/.*?([^.]+\.[^.]+)\.?$/,"$1").toLowerCase(),user_ip:"",events:{page_ready:[],page_unload:[],before_beacon:[]},vars:{},disabled_plugins:{},fireEvent:function(d,l){var j,k,m;if(!this.events.hasOwnProperty(d)){return false}m=this.events[d];for(j=0;j=0){h+=d.length;j=j.substring(h,j.indexOf(";",h));return j}return null},setCookie:function(h,d,n,r,l,m){var q="",j,p,o,i="";if(!h){return false}for(j in d){if(d.hasOwnProperty(j)){q+="&"+encodeURIComponent(j)+"="+encodeURIComponent(d[j])}}q=q.replace(/^&/,"");if(n){i=new Date();i.setTime(i.getTime()+n*1000);i=i.toGMTString()}p=h+"="+q;o=p+((n)?"; expires="+i:"")+((r)?"; path="+r:"")+((typeof l!=="undefined")?"; domain="+(l!==null?l:e.site_domain):"")+((m)?"; secure":"");if(p.length<4000){g.cookie=o;return(q===this.getCookie(h))}return false},getSubCookies:function(k){var j,h,d,n,m={};if(!k){return null}j=k.split("&");if(j.length===0){return null}for(h=0,d=j.length;h0)}},init:function(h){var l,d,j=["beacon_url","site_domain","user_ip"];if(!h){h={}}for(l=0;l50){BOOMR.utils.removeCookie(b.cookie);BOOMR.error("took more than 50ms to set cookie... aborting: "+d+" -> "+e,"rt")}return this}};BOOMR.plugins.RT={init:function(d){b.complete=false;b.timers={};BOOMR.utils.pluginConfig(b,d,"RT",["cookie","cookie_exp","strict_referrer"]);BOOMR.subscribe("page_ready",this.done,null,this);BOOMR.subscribe("page_unload",b.start,null,this);return this},startTimer:function(d){if(d){b.timers[d]={start:new Date().getTime()};b.complete=false}return this},endTimer:function(d,e){if(d){b.timers[d]=b.timers[d]||{};if(typeof b.timers[d].end==="undefined"){b.timers[d].end=(typeof e==="number"?e:new Date().getTime())}}return this},setTimer:function(d,e){if(d){b.timers[d]={delta:e}}return this},done:function(){var l,o,d,j,e,k={t_done:1,t_resp:1,t_page:1},i=0,m,h,n=[],f,g;if(b.complete){return this}this.endTimer("t_done");o=c.URL.replace(/#.*/,"");d=j=c.referrer.replace(/#.*/,"");e=BOOMR.utils.getSubCookies(BOOMR.utils.getCookie(b.cookie));BOOMR.utils.removeCookie(b.cookie);if(e!==null&&typeof e.s!=="undefined"&&typeof e.r!=="undefined"){d=e.r;if(!b.strict_referrer||d===j){l=parseInt(e.s,10)}}if(!l){BOOMR.warn("start cookie not set, trying WebTiming API","rt");g=a.performance||a.msPerformance||a.webkitPerformance||a.mozPerformance;if(g&&g.timing){f=g.timing}else{if(a.chrome&&a.chrome.csi){f={requestStart:a.chrome.csi().startE}}}if(f){l=f.requestStart||f.fetchStart||f.navigationStart||undefined}else{BOOMR.warn("This browser doesn't support the WebTiming API","rt")}}BOOMR.removeVar("t_done","t_page","t_resp","u","r","r2");for(m in b.timers){if(!b.timers.hasOwnProperty(m)){continue}h=b.timers[m];if(typeof h.delta!=="number"){if(typeof h.start!=="number"){h.start=l}h.delta=h.end-h.start}if(isNaN(h.delta)){continue}if(k.hasOwnProperty(m)){BOOMR.addVar(m,h.delta)}else{n.push(m+"|"+h.delta)}i++}if(i){BOOMR.addVar({u:o,r:d});if(j!==d){BOOMR.addVar("r2",j)}if(n.length){BOOMR.addVar("t_other",n.join(","))}}b.timers={};b.complete=true;BOOMR.sendBeacon();return this},is_complete:function(){return b.complete}}}(window));(function(b){var e=b.document;BOOMR=BOOMR||{};BOOMR.plugins=BOOMR.plugins||{};var a=[{name:"image-0.png",size:11483,timeout:1400},{name:"image-1.png",size:40658,timeout:1200},{name:"image-2.png",size:164897,timeout:1300},{name:"image-3.png",size:381756,timeout:1500},{name:"image-4.png",size:1234664,timeout:1200},{name:"image-5.png",size:4509613,timeout:1200},{name:"image-6.png",size:9084559,timeout:1200}];a.end=a.length;a.start=0;a.l={name:"image-l.gif",size:35,timeout:1000};var c={base_url:"images/",timeout:15000,nruns:5,latency_runs:10,user_ip:"",cookie_exp:7*86400,cookie:"BA",results:[],latencies:[],latency:null,runs_left:0,aborted:false,complete:false,running:false,ncmp:function(f,d){return(f-d)},iqr:function(h){var g=h.length-1,f,m,k,d=[],j;f=(h[Math.floor(g*0.25)]+h[Math.ceil(g*0.25)])/2;m=(h[Math.floor(g*0.75)]+h[Math.ceil(g*0.75)])/2;k=(m-f)*1.5;g++;for(j=0;jf-k){d.push(h[j])}}return d},calc_latency:function(){var h,f,j=0,g=0,k,m,d,o,l;l=this.iqr(this.latencies.sort(this.ncmp));f=l.length;BOOMR.debug(l,"bw");for(h=1;h=0&&l<3;x--){if(typeof p[x]==="undefined"){break}if(p[x].t===null){continue}t++;l++;z=a[x].size*1000/p[x].t;g.push(z);s=a[x].size*1000/(p[x].t-this.latency.mean);v.push(s)}}BOOMR.debug("got "+t+" readings","bw");BOOMR.debug("bandwidths: "+g,"bw");BOOMR.debug("corrected: "+v,"bw");if(g.length>3){g=this.iqr(g.sort(this.ncmp));v=this.iqr(v.sort(this.ncmp))}else{g=g.sort(this.ncmp);v=v.sort(this.ncmp)}BOOMR.debug("after iqr: "+g,"bw");BOOMR.debug("corrected: "+v,"bw");t=Math.max(g.length,v.length);for(y=0;y=a.end-1||typeof this.results[this.nruns-h].r[f+1]!=="undefined"){BOOMR.debug(this.results[this.nruns-h],"bw");if(h===this.nruns){a.start=f}this.defer(this.iterate)}else{this.load_img(f+1,h,this.img_loaded)}},finish:function(){if(!this.latency){this.latency=this.calc_latency()}var f=this.calc_bw(),d={bw:f.median_corrected,bw_err:parseFloat(f.stderr_corrected,10),lat:this.latency.mean,lat_err:parseFloat(this.latency.stderr,10),bw_time:Math.round(new Date().getTime()/1000)};BOOMR.addVar(d);if(!isNaN(d.bw)){BOOMR.utils.setCookie(this.cookie,{ba:Math.round(d.bw),be:d.bw_err,l:d.lat,le:d.lat_err,ip:this.user_ip,t:d.bw_time},(this.user_ip?this.cookie_exp:0),"/",null)}this.complete=true;BOOMR.sendBeacon();this.running=false},iterate:function(){if(this.aborted){return false}if(!this.runs_left){this.finish()}else{if(this.latency_runs){this.load_img("l",this.latency_runs--,this.lat_loaded)}else{this.results.push({r:[]});this.load_img(a.start,this.runs_left--,this.img_loaded)}}},setVarsFromCookie:function(l){var i=parseInt(l.ba,10),k=parseFloat(l.be,10),j=parseInt(l.l,10)||0,f=parseFloat(l.le,10)||0,d=l.ip.replace(/\.\d+$/,"0"),m=parseInt(l.t,10),h=this.user_ip.replace(/\.\d+$/,"0"),g=Math.round((new Date().getTime())/1000);if(d===h&&m>=g-this.cookie_exp){this.complete=true;BOOMR.addVar({bw:i,lat:j,bw_err:k,lat_err:f});return true}return false}};BOOMR.plugins.BW={init:function(d){var f;BOOMR.utils.pluginConfig(c,d,"BW",["base_url","timeout","nruns","cookie","cookie_exp"]);if(d&&d.user_ip){c.user_ip=d.user_ip}a.start=0;c.runs_left=c.nruns;c.latency_runs=10;c.results=[];c.latencies=[];c.latency=null;c.complete=false;c.aborted=false;BOOMR.removeVar("ba","ba_err","lat","lat_err");f=BOOMR.utils.getSubCookies(BOOMR.utils.getCookie(c.cookie));if(!f||!f.ba||!c.setVarsFromCookie(f)){BOOMR.subscribe("page_ready",this.run,null,this)}return this},run:function(){if(c.running||c.complete){return this}if(b.location.protocol==="https:"){BOOMR.info("HTTPS detected, skipping bandwidth test","bw");c.complete=true;return this}c.running=true;setTimeout(this.abort,c.timeout);c.defer(c.iterate);return this},abort:function(){c.aborted=true;c.finish();return this},is_complete:function(){return c.complete}}}(window)); diff --git a/boomerang.js b/boomerang.js index c66fc7f86..011ac812d 100644 --- a/boomerang.js +++ b/boomerang.js @@ -1,1319 +1,5360 @@ -/* - * Copyright (c) 2011, Yahoo! Inc. All rights reserved. +/** + * @copyright (c) 2011, Yahoo! Inc. All rights reserved. + * @copyright (c) 2012, Log-Normal, Inc. All rights reserved. + * @copyright (c) 2012-2017, SOASTA, Inc. All rights reserved. + * @copyright (c) 2017-2023, Akamai Technologies, Inc. All rights reserved. * Copyrights licensed under the BSD License. See the accompanying LICENSE.txt file for terms. */ /** -\file boomerang.js -boomerang measures various performance characteristics of your user's browsing -experience and beacons it back to your server. - -\details -To use this you'll need a web site, lots of users and the ability to do -something with the data you collect. How you collect the data is up to -you, but we have a few ideas. -*/ - -// Measure the time the script started -// This has to be global so that we don't wait for the entire -// BOOMR function to download and execute before measuring the -// time. We also declare it without `var` so that we can later -// `delete` it. This is the only way that works on Internet Explorer -BOOMR_start = new Date().getTime(); - -// beaconing section -// the parameter is the window -(function(w) { - -var impl, boomr, k, d=w.document; + * @class BOOMR + * @desc + * boomerang measures various performance characteristics of your user's browsing + * experience and beacons it back to your server. + * + * To use this you'll need a web site, lots of users and the ability to do + * something with the data you collect. How you collect the data is up to + * you, but we have a few ideas. + * + * Everything in boomerang is accessed through the `BOOMR` object, which is + * available on `window.BOOMR`. It contains the public API, utility functions + * ({@link BOOMR.utils}) and all of the plugins ({@link BOOMR.plugins}). + * + * Each plugin has its own API, but is reachable through {@link BOOMR.plugins}. + * + * ## Beacon Parameters + * + * The core boomerang object will add the following parameters to the beacon. + * + * Note that each individual {@link BOOMR.plugins plugin} will add its own + * parameters as well. + * + * * `v`: Boomerang version + * * `sv`: Boomerang Loader Snippet version + * * `sm`: Boomerang Loader Snippet method + * * `u`: The page's URL (for most beacons), or the `XMLHttpRequest` URL + * * `n`: The beacon number + * * `pgu`: The page's URL (for `XMLHttpRequest` beacons) + * * `pid`: Page ID (8 characters) + * * `r`: Navigation referrer (from `document.location`) + * * `vis.pre`: `1` if the page transitioned from prerender to visible + * * `vis.st`: Document's visibility state when beacon was sent + * * `vis.lh`: Timestamp when page was last hidden + * * `vis.lv`: Timestamp when page was last visible + * * `xhr.pg`: The `XMLHttpRequest` page group + * * `errors`: Error messages of errors detected in Boomerang code, separated by a newline + * * `rt.si`: Session ID + * * `rt.ss`: Session start timestamp + * * `rt.sl`: Session length (number of pages), can be increased by XHR beacons as well + * * `ua.plt`: `navigator.platform` or if available `navigator.userAgentData.platform` + * * `ua.arch`: navigator userAgentData architecture, if client hints requested + * * `ua.model`: navigator userAgentData model, if client hints requested + * * `ua.pltv`: navigator userAgentData platform version, if client hints requested + * * `ua.vnd`: `navigator.vendor` + */ -// Short namespace because I don't want to keep typing BOOMERANG -if(typeof BOOMR === "undefined") { - BOOMR = {}; -} -// don't allow this code to be included twice -if(BOOMR.version) { - return; -} +/** + * @typedef TimeStamp + * @type {number} + * + * @desc + * A [Unix Epoch](https://en.wikipedia.org/wiki/Unix_time) timestamp (milliseconds + * since 1970) created by [BOOMR.now()]{@link BOOMR.now}. + * + * If `DOMHighResTimeStamp` (`performance.now()`) is supported, it is + * a `DOMHighResTimeStamp` (with microsecond resolution in the fractional), + * otherwise, it is `Date.now()`. + */ -BOOMR.version = "0.9"; - - -// impl is a private object not reachable from outside the BOOMR object -// users can set properties by passing in to the init() method -impl = { - // properties - beacon_url: "", - // strip out everything except last two parts of hostname. - // This doesn't work well for domains that end with a country tld, - // but we allow the developer to override site_domain for that. - site_domain: w.location.hostname. - replace(/.*?([^.]+\.[^.]+)\.?$/, '$1'). - toLowerCase(), - //! User's ip address determined on the server. Used for the BA cookie - user_ip: '', - - events: { - "page_ready": [], - "page_unload": [], - "visibility_changed": [], - "before_beacon": [] - }, - - vars: {}, - - disabled_plugins: {}, - - fireEvent: function(e_name, data) { - var i, h, e; - if(!this.events.hasOwnProperty(e_name)) { - return false; - } - - e = this.events[e_name]; - - for(i=0; i= 0 ) { - i += name.length; - cookies = cookies.substring(i, cookies.indexOf(';', i)); - return cookies; - } - - return null; - }, - - setCookie: function(name, subcookies, max_age, path, domain, sec) { - var value = "", - k, nameval, c, - exp = ""; - - if(!name) { - return false; - } - - for(k in subcookies) { - if(subcookies.hasOwnProperty(k)) { - value += '&' + encodeURIComponent(k) - + '=' + encodeURIComponent(subcookies[k]); - } - } - value = value.replace(/^&/, ''); - - if(max_age) { - exp = new Date(); - exp.setTime(exp.getTime() + max_age*1000); - exp = exp.toGMTString(); - } - - nameval = name + '=' + value; - c = nameval + - ((max_age) ? "; expires=" + exp : "" ) + - ((path) ? "; path=" + path : "") + - ((typeof domain !== "undefined") ? "; domain=" - + (domain !== null ? domain : impl.site_domain ) : "") + - ((sec) ? "; secure" : ""); - - if ( nameval.length < 4000 ) { - d.cookie = c; - // confirm cookie was set (could be blocked by user's settings, etc.) - return ( value === this.getCookie(name) ); - } - - return false; - }, - - getSubCookies: function(cookie) { - var cookies_a, - i, l, kv, - cookies={}; - - if(!cookie) { - return null; - } - - cookies_a = cookie.split('&'); - - if(cookies_a.length === 0) { - return null; - } - - for(i=0, l=cookies_a.length; i0); - } - }, - - init: function(config) { - var i, k, - properties = ["beacon_url", "site_domain", "user_ip"]; - - if(!config) { - config = {}; - } - - for(i=0; i -1)?'&':'?') + - 'v=' + encodeURIComponent(BOOMR.version) + - '&u=' + encodeURIComponent(d.URL.replace(/#.*/, '')); - // use d.URL instead of location.href because of a safari bug - - for(k in impl.vars) { - if(impl.vars.hasOwnProperty(k)) { - nparams++; - url += "&" + encodeURIComponent(k) - + "=" - + ( - impl.vars[k]===undefined || impl.vars[k]===null - ? '' - : encodeURIComponent(impl.vars[k]) - ); - } - } - - // only send beacon if we actually have something to beacon back - if(nparams) { - img = new Image(); - img.src=url; - } - - return this; - } - -}; - -delete BOOMR_start; - -var make_logger = function(l) { - return function(m, s) { - this.log(m, l, "boomerang" + (s?"."+s:"")); return this; - }; -}; - -boomr.debug = make_logger("debug"); -boomr.info = make_logger("info"); -boomr.warn = make_logger("warn"); -boomr.error = make_logger("error"); - -if(w.YAHOO && w.YAHOO.widget && w.YAHOO.widget.Logger) { - boomr.log = w.YAHOO.log; -} -else if(typeof w.Y !== "undefined" && typeof w.Y.log !== "undefined") { - boomr.log = w.Y.log; -} -else if(typeof console !== "undefined" && typeof console.log !== "undefined") { - boomr.log = function(m,l,s) { console.log(s + ": [" + l + "] ", m); }; +/* BEGIN_DEBUG */ +// we don't yet have BOOMR.utils.mark() +if ("performance" in window && + window.performance && + typeof window.performance.mark === "function" && + !window.BOOMR_no_mark) { + window.performance.mark("boomr:startup"); } +/* END_DEBUG */ +/** + * @global + * @type {TimeStamp} + * @desc + * This variable is added to the global scope (`window`) until Boomerang loads, + * at which point it is removed. + * + * Timestamp the boomerang.js script started executing. + * + * This has to be global so that we don't wait for this entire + * script to download and execute before measuring the + * time. We also declare it without `var` so that we can later + * `delete` it. This is the only way that works on Internet Explorer. + */ +BOOMR_start = new Date().getTime(); -for(k in boomr) { - if(boomr.hasOwnProperty(k)) { - BOOMR[k] = boomr[k]; - } +/** + * @function + * @global + * @desc + * This function is added to the global scope (`window`). + * + * Check the value of `document.domain` and fix it if incorrect. + * + * This function is run at the top of boomerang, and then whenever + * {@link BOOMR.init} is called. If boomerang is running within an IFRAME, this + * function checks to see if it can access elements in the parent + * IFRAME. If not, it will fudge around with `document.domain` until + * it finds a value that works. + * + * This allows site owners to change the value of `document.domain` at + * any point within their page's load process, and we will adapt to + * it. + * + * @param {string} domain Domain name as retrieved from page URL + */ +function BOOMR_check_doc_domain(domain) { + /* eslint no-unused-vars:0 */ + var test; + + /* BEGIN_DEBUG */ + // we don't yet have BOOMR.utils.mark() + if ("performance" in window && + window.performance && + typeof window.performance.mark === "function" && + !window.BOOMR_no_mark) { + window.performance.mark("boomr:check_doc_domain"); + } + /* END_DEBUG */ + + if (!window) { + return; + } + + // If domain is not passed in, then this is a global call + // domain is only passed in if we call ourselves, so we + // skip the frame check at that point + if (!domain) { + // If we're running in the main window, then we don't need this + if (window.parent === window || !document.getElementById("boomr-if-as")) { + // nothing to do + return; + } + + if (window.BOOMR && BOOMR.boomerang_frame && BOOMR.window) { + try { + // If document.domain is changed during page load (from www.blah.com to blah.com, for example), + // BOOMR.window.location.href throws "Permission Denied" in IE. + // Resetting the inner domain to match the outer makes location accessible once again + if (BOOMR.boomerang_frame.document.domain !== BOOMR.window.document.domain) { + BOOMR.boomerang_frame.document.domain = BOOMR.window.document.domain; + } + } + catch (err) { + if (!BOOMR.isCrossOriginError(err)) { + BOOMR.addError(err, "BOOMR_check_doc_domain.domainFix"); + } + } + } + + domain = document.domain; + } + + if (!domain || domain.indexOf(".") === -1) { + // not okay, but we did our best + return; + } + + // window.parent might be null if we're running during unload from + // a detached iframe + if (!window.parent) { + return; + } + + // 1. Test without setting document.domain + try { + test = window.parent.document; + + // all okay + return; + } + // 2. Test with document.domain + catch (err) { + try { + document.domain = domain; + } + catch (err2) { + // An exception might be thrown if the document is unloaded + // or when the domain is incorrect. If so, we can't do anything + // more, so bail. + return; + } + } + + try { + test = window.parent.document; + + // all okay + return; + } + // 3. Strip off leading part and try again + catch (err) { + domain = domain.replace(/^[\w\-]+\./, ""); + } + + BOOMR_check_doc_domain(domain); } -BOOMR.plugins = BOOMR.plugins || {}; - -}(window)); - -// end of boomerang beaconing section -// Now we start built in plugins. - - -// This is the Round Trip Time plugin. Abbreviated to RT -// the parameter is the window -(function(w) { - -var d=w.document; - -BOOMR = BOOMR || {}; -BOOMR.plugins = BOOMR.plugins || {}; - -// private object -var impl = { - complete: false, //! Set when this plugin has completed - - timers: {}, //! Custom timers that the developer can use - // Format for each timer is { start: XXX, end: YYY, delta: YYY-XXX } - cookie: 'RT', //! Name of the cookie that stores the start time and referrer - cookie_exp:600, //! Cookie expiry in seconds - strict_referrer: true, //! By default, don't beacon if referrers don't match. - // If set to false, beacon both referrer values and let - // the back end decide - - navigationType: 0, - navigationStart: undefined, - responseStart: undefined, - - // The start method is fired on page unload. It is called with the scope - // of the BOOMR.plugins.RT object - start: function() { - var t_end, t_start = new Date().getTime(); - - // Disable use of RT cookie by setting its name to a falsy value - if(!this.cookie) { - return this; - } - - // We use document.URL instead of location.href because of a bug in safari 4 - // where location.href is URL decoded - if(!BOOMR.utils.setCookie(this.cookie, - { s: t_start, r: d.URL.replace(/#.*/, '') }, - this.cookie_exp, - "/", null) - ) { - BOOMR.error("cannot set start cookie", "rt"); - return this; - } - - t_end = new Date().getTime(); - if(t_end - t_start > 50) { - // It took > 50ms to set the cookie - // The user Most likely has cookie prompting turned on so - // t_start won't be the actual unload time - // We bail at this point since we can't reliably tell t_done - BOOMR.utils.removeCookie(this.cookie); - - // at some point we may want to log this info on the server side - BOOMR.error("took more than 50ms to set cookie... aborting: " - + t_start + " -> " + t_end, "rt"); - } - - return this; - }, - - initNavTiming: function() { - var ti, p, source; - - if(this.navigationStart) { - return; - } - - // Get start time from WebTiming API see: - // https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/NavigationTiming/Overview.html - // http://blogs.msdn.com/b/ie/archive/2010/06/28/measuring-web-page-performance.aspx - // http://blog.chromium.org/2010/07/do-you-know-how-slow-your-web-page-is.html - p = w.performance || w.msPerformance || w.webkitPerformance || w.mozPerformance; - - if(p && p.navigation) { - this.navigationType = p.navigation.type; - } - - if(p && p.timing) { - ti = p.timing; - } - else if(w.chrome && w.chrome.csi && w.chrome.csi().startE) { - // Older versions of chrome also have a timing API that's sort of documented here: - // http://ecmanaut.blogspot.com/2010/06/google-bom-feature-ms-since-pageload.html - // source here: - // http://src.chromium.org/viewvc/chrome/trunk/src/chrome/renderer/loadtimes_extension_bindings.cc?view=markup - ti = { - navigationStart: w.chrome.csi().startE - }; - source = "csi"; - } - else if(w.gtbExternal && w.gtbExternal.startE()) { - // The Google Toolbar exposes navigation start time similar to old versions of chrome - // This would work for any browser that has the google toolbar installed - ti = { - navigationStart: w.gtbExternal.startE() - }; - source = 'gtb'; - } - - if(ti) { - // Always use navigationStart since it falls back to fetchStart - // If not set, we leave t_start alone so that timers that depend - // on it don't get sent back. Never use requestStart since if - // the first request fails and the browser retries, it will contain - // the value for the new request. - BOOMR.addVar("rt.start", source || "navigation"); - this.navigationStart = ti.navigationStart || undefined; - this.responseStart = ti.responseStart || undefined; - - // bug in Firefox 7 & 8 https://bugzilla.mozilla.org/show_bug.cgi?id=691547 - if(navigator.userAgent.match(/Firefox\/[78]\./)) { - this.navigationStart = ti.unloadEventStart || ti.fetchStart || undefined; - } - } - else { - BOOMR.warn("This browser doesn't support the WebTiming API", "rt"); - } - - return; - } -}; - -BOOMR.plugins.RT = { - // Methods - - init: function(config) { - impl.complete = false; - impl.timers = {}; - - BOOMR.utils.pluginConfig(impl, config, "RT", - ["cookie", "cookie_exp", "strict_referrer"]); - - BOOMR.subscribe("page_ready", this.done, null, this); - BOOMR.subscribe("page_unload", impl.start, null, impl); - - if(BOOMR.t_start) { - // How long does it take Boomerang to load up and execute - this.startTimer('boomerang', BOOMR.t_start); - this.endTimer('boomerang', BOOMR.t_end); // t_end === null defaults to current time - - // How long did it take till Boomerang started - this.endTimer('boomr_fb', BOOMR.t_start); - } - - return this; - }, - - startTimer: function(timer_name, time_value) { - if(timer_name) { - if (timer_name === 't_page') { - this.endTimer('t_resp', time_value); - } - impl.timers[timer_name] = {start: (typeof time_value === "number" ? time_value : new Date().getTime())}; - impl.complete = false; - } - - return this; - }, - - endTimer: function(timer_name, time_value) { - if(timer_name) { - impl.timers[timer_name] = impl.timers[timer_name] || {}; - if(!("end" in impl.timers[timer_name])) { - impl.timers[timer_name].end = - (typeof time_value === "number" ? time_value : new Date().getTime()); - } - } - - return this; - }, - - setTimer: function(timer_name, time_delta) { - if(timer_name) { - impl.timers[timer_name] = { delta: time_delta }; - } - - return this; - }, - - // Called when the page has reached a "usable" state. This may be when the - // onload event fires, or it could be at some other moment during/after page - // load when the page is usable by the user - done: function() { - var t_start, r, r2, - subcookies, basic_timers = { t_done: 1, t_resp: 1, t_page: 1}, - ntimers = 0, t_name, timer, t_other=[]; - - if(impl.complete) { - return this; - } - - impl.initNavTiming(); - - if( - (d.webkitVisibilityState && d.webkitVisibilityState === "prerender") - || - (d.msVisibilityState && d.msVisibilityState === 3) - ) { - // This means that onload fired through a pre-render. We'll capture this - // time, but wait for t_done until after the page has become either visible - // or hidden (ie, it moved out of the pre-render state) - // http://code.google.com/chrome/whitepapers/pagevisibility.html - // http://www.w3.org/TR/2011/WD-page-visibility-20110602/ - // http://code.google.com/chrome/whitepapers/prerender.html - - this.startTimer("t_load", impl.navigationStart); - this.endTimer("t_load"); // this will measure actual onload time for a prerendered page - this.startTimer("t_prerender", impl.navigationStart); - this.startTimer("t_postrender"); // time from prerender to visible or hidden - - BOOMR.subscribe("visibility_changed", this.done, null, this); - - return this; - } - - // If the dev has already called endTimer, then this call will do nothing - // else, it will stop the page load timer - this.endTimer("t_done"); - - if(impl.responseStart) { - // Use NavTiming API to figure out resp latency and page time - // t_resp will use the cookie if available or fallback to NavTiming - this.endTimer("t_resp", impl.responseStart); - if(impl.timers.t_load) { - this.setTimer("t_page", impl.timers.t_load.end - impl.responseStart); - } - else { - this.setTimer("t_page", new Date().getTime() - impl.responseStart); - } - } - else if(impl.timers.hasOwnProperty('t_page')) { - // If the dev has already started t_page timer, we can end it now as well - this.endTimer("t_page"); - } - - // If a prerender timer was started, we can end it now as well - if(impl.timers.hasOwnProperty('t_postrender')) { - this.endTimer("t_postrender"); - this.endTimer("t_prerender"); - } - - // A beacon may be fired automatically on page load or if the page dev fires - // it manually with their own timers. It may not always contain a referrer - // (eg: XHR calls). We set default values for these cases - - r = r2 = d.referrer.replace(/#.*/, ''); - - // If impl.cookie is not set, the dev does not want to use cookie time - if(impl.cookie) { - subcookies = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie(impl.cookie)); - BOOMR.utils.removeCookie(impl.cookie); - - if(subcookies && subcookies.s && subcookies.r) { - r = subcookies.r; - if(!impl.strict_referrer || r === r2) { - t_start = parseInt(subcookies.s, 10); - } - } - } - - if(t_start && impl.navigationType != 2) { // 2 is TYPE_BACK_FORWARD but the constant may not be defined across browsers - BOOMR.addVar("rt.start", "cookie"); // if the user hit the back button, referrer will match, and cookie will match - } // but will have time of previous page start, so t_done will be wrong - else { - t_start = impl.navigationStart; - } - - // make sure old variables don't stick around - BOOMR.removeVar('t_done', 't_page', 't_resp', 'r', 'r2', 'rt.bstart', 'rt.end'); - - BOOMR.addVar('rt.bstart', BOOMR.t_start); - BOOMR.addVar('rt.end', impl.timers.t_done.end); - - for(t_name in impl.timers) { - if(!impl.timers.hasOwnProperty(t_name)) { - continue; - } - - timer = impl.timers[t_name]; - - // if delta is a number, then it was set using setTimer - // if not, then we have to calculate it using start & end - if(typeof timer.delta !== "number") { - if(typeof timer.start !== "number") { - timer.start = t_start; - } - timer.delta = timer.end - timer.start; - } - - // If the caller did not set a start time, and if there was no start cookie - // then timer.delta will be NaN, in which case we discard it. - if(isNaN(timer.delta)) { - continue; - } - - if(basic_timers.hasOwnProperty(t_name)) { - BOOMR.addVar(t_name, timer.delta); - } - else { - t_other.push(t_name + '|' + timer.delta); - } - ntimers++; - } - - if(ntimers) { - BOOMR.addVar("r", r); - - if(r2 !== r) { - BOOMR.addVar("r2", r2); - } - - if(t_other.length) { - BOOMR.addVar("t_other", t_other.join(',')); - } - } - - impl.timers = {}; - impl.complete = true; - - BOOMR.sendBeacon(); // we call sendBeacon() anyway because some other plugin - // may have blocked waiting for RT to complete - return this; - }, - - is_complete: function() { return impl.complete; } - -}; - -}(window)); -// End of RT plugin +BOOMR_check_doc_domain(); -// This is the Bandwidth & Latency plugin abbreviated to BW -// the parameter is the window +// Construct BOOMR +// w is window (function(w) { - -var d=w.document; - -BOOMR = BOOMR || {}; -BOOMR.plugins = BOOMR.plugins || {}; - -// We choose image sizes so that we can narrow down on a bandwidth range as -// soon as possible the sizes chosen correspond to bandwidth values of -// 14-64kbps, 64-256kbps, 256-1024kbps, 1-2Mbps, 2-8Mbps, 8-30Mbps & 30Mbps+ -// Anything below 14kbps will probably timeout before the test completes -// Anything over 60Mbps will probably be unreliable since latency will make up -// the largest part of download time. If you want to extend this further to -// cover 100Mbps & 1Gbps networks, use image sizes of 19,200,000 & 153,600,000 -// bytes respectively -// See https://spreadsheets.google.com/ccc?key=0AplxPyCzmQi6dDRBN2JEd190N1hhV1N5cHQtUVdBMUE&hl=en_GB -// for a spreadsheet with the details -var images=[ - { name: "image-0.png", size: 11483, timeout: 1400 }, - { name: "image-1.png", size: 40658, timeout: 1200 }, - { name: "image-2.png", size: 164897, timeout: 1300 }, - { name: "image-3.png", size: 381756, timeout: 1500 }, - { name: "image-4.png", size: 1234664, timeout: 1200 }, - { name: "image-5.png", size: 4509613, timeout: 1200 }, - { name: "image-6.png", size: 9084559, timeout: 1200 } -]; - -images.end = images.length; -images.start = 0; - -// abuse arrays to do the latency test simply because it avoids a bunch of -// branches in the rest of the code. -// I'm sorry Douglas -images.l = { name: "image-l.gif", size: 35, timeout: 1000 }; - -// private object -var impl = { - // properties - base_url: 'images/', - timeout: 15000, - nruns: 5, - latency_runs: 10, - user_ip: '', - cookie_exp: 7*86400, - cookie: 'BA', - - // state - results: [], - latencies: [], - latency: null, - runs_left: 0, - aborted: false, - complete: false, - running: false, - - // methods - - // numeric comparator. Returns negative number if a < b, positive if a > b and 0 if they're equal - // used to sort an array numerically - ncmp: function(a, b) { return (a-b); }, - - // Calculate the interquartile range of an array of data points - iqr: function(a) - { - var l = a.length-1, q1, q3, fw, b = [], i; - - q1 = (a[Math.floor(l*0.25)] + a[Math.ceil(l*0.25)])/2; - q3 = (a[Math.floor(l*0.75)] + a[Math.ceil(l*0.75)])/2; - - fw = (q3-q1)*1.5; - - l++; - - for(i=0; i q1-fw) { - b.push(a[i]); - } - } - - return b; - }, - - calc_latency: function() - { - var i, n, - sum=0, sumsq=0, - amean, median, - std_dev, std_err, - lat_filtered; - - // We first do IQR filtering and use the resulting data set - // for all calculations - lat_filtered = this.iqr(this.latencies.sort(this.ncmp)); - n = lat_filtered.length; - - BOOMR.debug(lat_filtered, "bw"); - - // First we get the arithmetic mean, standard deviation and standard error - // We ignore the first since it paid the price of DNS lookup, TCP connect - // and slow start - for(i=1; i=0 && nimgs<3; j--) { - // if we hit an undefined image time, we skipped everything before this - if(!r[j]) { - break; - } - if(r[j].t === null) { - continue; - } - - n++; - nimgs++; - - // multiply by 1000 since t is in milliseconds and not seconds - bw = images[j].size*1000/r[j].t; - bandwidths.push(bw); - - bw_c = images[j].size*1000/(r[j].t - this.latency.mean); - bandwidths_corrected.push(bw_c); - } - } - - BOOMR.debug('got ' + n + ' readings', "bw"); - - BOOMR.debug('bandwidths: ' + bandwidths, "bw"); - BOOMR.debug('corrected: ' + bandwidths_corrected, "bw"); - - // First do IQR filtering since we use the median here - // and should use the stddev after filtering. - if(bandwidths.length > 3) { - bandwidths = this.iqr(bandwidths.sort(this.ncmp)); - bandwidths_corrected = this.iqr(bandwidths_corrected.sort(this.ncmp)); - } else { - bandwidths = bandwidths.sort(this.ncmp); - bandwidths_corrected = bandwidths_corrected.sort(this.ncmp); - } - - BOOMR.debug('after iqr: ' + bandwidths, "bw"); - BOOMR.debug('corrected: ' + bandwidths_corrected, "bw"); - - // Now get the mean & median. - // Also get corrected values that eliminate latency - n = Math.max(bandwidths.length, bandwidths_corrected.length); - for(i=0; i= images.end-1 - || typeof this.results[this.nruns-run].r[i+1] !== "undefined" - ) { - BOOMR.debug(this.results[this.nruns-run], "bw"); - // First run is a pilot test to decide what the largest image - // that we can download is. All following runs only try to - // download this image - if(run === this.nruns) { - images.start = i; - } - this.defer(this.iterate); - } else { - this.load_img(i+1, run, this.img_loaded); - } - }, - - finish: function() - { - if(!this.latency) { - this.latency = this.calc_latency(); - } - var bw = this.calc_bw(), - o = { - bw: bw.median_corrected, - bw_err: parseFloat(bw.stderr_corrected, 10), - lat: this.latency.mean, - lat_err: parseFloat(this.latency.stderr, 10), - bw_time: Math.round(new Date().getTime()/1000) - }; - - BOOMR.addVar(o); - - // If we have an IP address we can make the BA cookie persistent for a while - // because we'll recalculate it if necessary (when the user's IP changes). - if(!isNaN(o.bw)) { - BOOMR.utils.setCookie(this.cookie, - { - ba: Math.round(o.bw), - be: o.bw_err, - l: o.lat, - le: o.lat_err, - ip: this.user_ip, - t: o.bw_time - }, - (this.user_ip ? this.cookie_exp : 0), - "/", - null - ); - } - - this.complete = true; - BOOMR.sendBeacon(); - this.running = false; - }, - - iterate: function() - { - if(this.aborted) { - return false; - } - - if(!this.runs_left) { - this.finish(); - } - else if(this.latency_runs) { - this.load_img('l', this.latency_runs--, this.lat_loaded); - } - else { - this.results.push({r:[]}); - this.load_img(images.start, this.runs_left--, this.img_loaded); - } - }, - - setVarsFromCookie: function(cookies) { - var ba = parseInt(cookies.ba, 10), - bw_e = parseFloat(cookies.be, 10), - lat = parseInt(cookies.l, 10) || 0, - lat_e = parseFloat(cookies.le, 10) || 0, - c_sn = cookies.ip.replace(/\.\d+$/, '0'), // Note this is IPv4 only - t = parseInt(cookies.t, 10), - p_sn = this.user_ip.replace(/\.\d+$/, '0'), - - // We use the subnet instead of the IP address because some people - // on DHCP with the same ISP may get different IPs on the same subnet - // every time they log in - - t_now = Math.round((new Date().getTime())/1000); // seconds - - // If the subnet changes or the cookie is more than 7 days old, - // then we recheck the bandwidth, else we just use what's in the cookie - if(c_sn === p_sn && t >= t_now - this.cookie_exp) { - this.complete = true; - BOOMR.addVar({ - 'bw': ba, - 'lat': lat, - 'bw_err': bw_e, - 'lat_err': lat_e - }); - - return true; - } - - return false; - } - -}; - -BOOMR.plugins.BW = { - init: function(config) { - var cookies; - - BOOMR.utils.pluginConfig(impl, config, "BW", - ["base_url", "timeout", "nruns", "cookie", "cookie_exp"]); - - if(config && config.user_ip) { - impl.user_ip = config.user_ip; - } - - images.start = 0; - impl.runs_left = impl.nruns; - impl.latency_runs = 10; - impl.results = []; - impl.latencies = []; - impl.latency = null; - impl.complete = false; - impl.aborted = false; - - BOOMR.removeVar('ba', 'ba_err', 'lat', 'lat_err'); - - cookies = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie(impl.cookie)); - - if(!cookies || !cookies.ba || !impl.setVarsFromCookie(cookies)) { - BOOMR.subscribe("page_ready", this.run, null, this); - } - - return this; - }, - - run: function() { - if(impl.running || impl.complete) { - return this; - } - - if(w.location.protocol === 'https:') { - // we don't run the test for https because SSL stuff will mess up b/w - // calculations we could run the test itself over HTTP, but then IE - // will complain about insecure resources, so the best is to just bail - // and hope that the user gets the cookie from some other page - - BOOMR.info("HTTPS detected, skipping bandwidth test", "bw"); - impl.complete = true; - BOOMR.sendBeacon(); - return this; - } - - impl.running = true; - - setTimeout(this.abort, impl.timeout); - - impl.defer(impl.iterate); - - return this; - }, - - abort: function() { - impl.aborted = true; - if (impl.running) { - impl.finish(); // we don't defer this call because it might be called from - // onunload and we want the entire chain to complete - // before we return - } - return this; - }, - - is_complete: function() { return impl.complete; } -}; - + var impl, boomr, d, createCustomEvent, dispatchEvent, visibilityState, visibilityChange, + orig_w = w; + + // If the window that boomerang is running in is not top level (ie, we're running in an iframe) + // and if this iframe contains a script node with an id of "boomr-if-as", + // Then that indicates that we are using the iframe loader, so the page we're trying to measure + // is w.parent + // + // Note that we use `document` rather than `w.document` because we're specifically interested in + // the document of the currently executing context rather than a passed in proxy. + // + // The only other place we do this is in `BOOMR.utils.getMyURL` below, for the same reason, we + // need the full URL of the currently executing (boomerang) script. + if (w.parent !== w && + document.getElementById("boomr-if-as") && + document.getElementById("boomr-if-as").nodeName.toLowerCase() === "script") { + w = w.parent; + } + + d = w.document; + + // Short namespace because I don't want to keep typing BOOMERANG + if (!w.BOOMR) { + w.BOOMR = {}; + } + + BOOMR = w.BOOMR; + + // don't allow this code to be included twice + if (BOOMR.version) { + return; + } + + /** + * Boomerang version, formatted as major.minor.patchlevel. + * + * This variable is replaced during build (`grunt build`). + * + * @type {string} + * + * @memberof BOOMR + */ + BOOMR.version = "%boomerang_version%"; + + /** + * The main document window. + * * If Boomerang was loaded in an IFRAME, this is the parent window + * * If Boomerang was loaded inline, this is the current window + * + * @type {Window} + * + * @memberof BOOMR + */ + BOOMR.window = w; + + /** + * The Boomerang frame: + * * If Boomerang was loaded in an IFRAME, this is the IFRAME + * * If Boomerang was loaded inline, this is the current window + * + * @type {Window} + * + * @memberof BOOMR + */ + BOOMR.boomerang_frame = orig_w; + + /** + * @class BOOMR.plugins + * @desc + * Boomerang plugin namespace. + * + * All plugins should add their plugin object to `BOOMR.plugins`. + * + * A plugin should have, at minimum, the following exported functions: + * * `init(config)` + * * `is_complete()` + * + * See {@tutorial creating-plugins} for details. + */ + if (!BOOMR.plugins) { + BOOMR.plugins = {}; + } + + // CustomEvent proxy for IE9 & 10 from https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent + (function() { + try { + if (new w.CustomEvent("CustomEvent") !== undefined) { + createCustomEvent = function(e_name, params) { + return new w.CustomEvent(e_name, params); + }; + } + } + catch (ignore) { + // empty + } + + try { + if (!createCustomEvent && d.createEvent && d.createEvent("CustomEvent")) { + createCustomEvent = function(e_name, params) { + var evt = d.createEvent("CustomEvent"); + + params = params || { cancelable: false, bubbles: false }; + evt.initCustomEvent(e_name, params.bubbles, params.cancelable, params.detail); + + return evt; + }; + } + } + catch (ignore) { + // empty + } + + if (!createCustomEvent && d.createEventObject) { + createCustomEvent = function(e_name, params) { + var evt = d.createEventObject(); + + evt.type = evt.propertyName = e_name; + evt.detail = params.detail; + + return evt; + }; + } + + if (!createCustomEvent) { + createCustomEvent = function() { + return undefined; + }; + } + }()); + + /** + * Dispatch a custom event to the browser + * @param {string} e_name The custom event name that consumers can subscribe to + * @param {object} e_data Any data passed to subscribers of the custom event via the `event.detail` property + * @param {boolean} async By default, custom events are dispatched immediately. + * Set to true if the event should be dispatched once the browser has finished its current + * JavaScript execution. + */ + dispatchEvent = function(e_name, e_data, async) { + var ev = createCustomEvent(e_name, {"detail": e_data}); + + if (!ev) { + return; + } + + function dispatch() { + try { + if (d.dispatchEvent) { + d.dispatchEvent(ev); + } + else if (d.fireEvent) { + d.fireEvent("onpropertychange", ev); + } + } + catch (e) { + BOOMR.debug("Error when dispatching " + e_name); + } + } + + if (async) { + BOOMR.setImmediate(dispatch); + } + else { + dispatch(); + } + }; + + // visibilitychange is useful to detect if the page loaded through prerender + // or if the page never became visible + // https://www.w3.org/TR/2011/WD-page-visibility-20110602/ + // https://www.nczonline.net/blog/2011/08/09/introduction-to-the-page-visibility-api/ + // https://developer.mozilla.org/en-US/docs/Web/Guide/User_experience/Using_the_Page_Visibility_API + + // Set the name of the hidden property and the change event for visibility + if (typeof d.hidden !== "undefined") { + visibilityState = "visibilityState"; + visibilityChange = "visibilitychange"; + } + else if (typeof d.mozHidden !== "undefined") { + visibilityState = "mozVisibilityState"; + visibilityChange = "mozvisibilitychange"; + } + else if (typeof d.msHidden !== "undefined") { + visibilityState = "msVisibilityState"; + visibilityChange = "msvisibilitychange"; + } + else if (typeof d.webkitHidden !== "undefined") { + visibilityState = "webkitVisibilityState"; + visibilityChange = "webkitvisibilitychange"; + } + + // + // Internal implementation (impl) + // + // impl is a private object not reachable from outside the BOOMR object. + // Users can set properties by passing in to the init() method. + // + impl = { + // + // Private Members + // + + // Beacon URL + beacon_url: "", + + // Forces protocol-relative URLs to HTTPS + beacon_url_force_https: true, + + // List of string regular expressions that must match the beacon_url. If + // not set, or the list is empty, all beacon URLs are allowed. + beacon_urls_allowed: [], + + // Beacon request method, either GET, POST or AUTO. AUTO will check the + // request size then use GET if the request URL is less than MAX_GET_LENGTH + // chars. Otherwise, it will fall back to a POST request. + beacon_type: "AUTO", + + // Beacon authorization key value. Most systems will use the 'Authentication' + // keyword, but some some services use keys like 'X-Auth-Token' or other + // custom keys. + beacon_auth_key: "Authorization", + + // Beacon authorization token. This is only needed if your are using a POST + // and the beacon requires an Authorization token to accept your data. This + // disables use of the browser sendBeacon() API. + beacon_auth_token: undefined, + + // Sends beacons with Credentials (applies to XHR beacons, not IMG or `sendBeacon()`). + // If you need this, you may want to enable `beacon_disable_sendbeacon` as + // `sendBeacon()` does not support credentials. + beacon_with_credentials: false, + + // Disables navigator.sendBeacon() support + beacon_disable_sendbeacon: false, + + // Strip out everything except last two parts of hostname. + // This doesn't work well for domains that end with a country tld, + // but we allow the developer to override site_domain for that. + // You can disable all cookies by setting site_domain to a falsy value. + site_domain: w.location.hostname. + replace(/.*?([^.]+\.[^.]+)\.?$/, "$1"). + toLowerCase(), + + // User's IP address determined on the server. Used for the BW cookie. + user_ip: "", + + // Whether or not to send beacons on page load + autorun: true, + + // Whether or not we've sent a page load beacon + hasSentPageLoadBeacon: false, + + // document.referrer + r: undefined, + + // Whether or not to strip the Query String + strip_query_string: false, + + // Whether or not the page's 'onload' event has fired + onloadFired: false, + + // Whether or not we've attached all of the page event handlers we want on startup + handlers_attached: false, + + // Whether or not we're waiting for configuration to initialize + waiting_for_config: false, + + // All Boomerang cookies will be created with SameSite=Lax by default + same_site_cookie: "Lax", + + // All Boomerang cookies will be without Secure attribute by default + secure_cookie: false, + + // Sometimes we would like to be able to set the SameSite=None from a Boomerang plugin + forced_same_site_cookie_none: false, + + // Navigator User Agent data object holding Architecture, Model and Platform information from Client Hints API + userAgentData: undefined, + + // Client Hints use for Architecture, Model and Platform detail is disabled by default + request_client_hints: false, + + // Disables all Unload handlers and Unload beacons + no_unload: false, + + // Number of page_unload or before_unload callbacks registered + unloadEventsCount: 0, + + // Number of page_unload or before_unload callbacks called + unloadEventCalled: 0, + + // Event listener callbacks + listenerCallbacks: {}, + + // Beacon variables + vars: {}, + + // Beacon variables for only the next beacon + singleBeaconVars: {}, + + /** + * Variable priority lists: + * -1 = first + * 1 = last + */ + varPriority: { + "-1": {}, + "1": {} + }, + + // Internal boomerang.js errors + errors: {}, + + // Plugins that are disabled + disabled_plugins: {}, + + // Whether or not localStorage is supported + localStorageSupported: false, + + // Prefix for localStorage + LOCAL_STORAGE_PREFIX: "_boomr_", + + // Native functions that were overwritten and should be restored when + // the Boomerang IFRAME is unloaded + nativeOverwrites: [], + + // Prerendered offset (via activationStart). null if not yet checked, + // false if Prerender is supported but did not occur, an integer if + // there was a Prerender (activationStart time). + prerenderedOffset: null, + + // (End Private Members) + + // + // Events (internal and public) + // + + /** + * Internal Events + */ + events: { + /** + * Boomerang event, subscribe via {@link BOOMR.subscribe}. + * + * Fired when the page is usable by the user. + * + * By default this is fired when `window.onload` fires, but if you + * set `autorun` to false when calling {@link BOOMR.init}, then you + * must explicitly fire this event by calling {@link BOOMR#event:page_ready}. + * + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onload} + * @event BOOMR#page_ready + * @property {Event} [event] Event triggering the page_ready + */ + "page_ready": [], + + /** + * Boomerang event, subscribe via {@link BOOMR.subscribe}. + * + * Fired just before the browser unloads the page. + * + * The first event of `window.pagehide`, `window.beforeunload`, + * or `window.unload` will trigger this. + * + * @see {@link https://developer.mozilla.org/en-US/docs/Web/Events/pagehide} + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onbeforeunload} + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onunload} + * @event BOOMR#page_unload + */ + "page_unload": [], + + /** + * Boomerang event, subscribe via {@link BOOMR.subscribe}. + * + * Fired before the document is about to be unloaded. + * + * `window.beforeunload` will trigger this. + * + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onbeforeunload} + * @event BOOMR#before_unload + */ + "before_unload": [], + + /** + * Boomerang event, subscribe via {@link BOOMR.subscribe}. + * + * Fired on `document.DOMContentLoaded`. + * + * The `DOMContentLoaded` event is fired when the initial HTML document + * has been completely loaded and parsed, without waiting for stylesheets, + * images, and subframes to finish loading + * + * @see {@link https://developer.mozilla.org/en-US/docs/Web/Events/DOMContentLoaded} + * @event BOOMR#dom_loaded + */ + "dom_loaded": [], + + /** + * Boomerang event, subscribe via {@link BOOMR.subscribe}. + * + * Fired on `document.visibilitychange`. + * + * The `visibilitychange` event is fired when the content of a tab has + * become visible or has been hidden. + * + * @see {@link https://developer.mozilla.org/en-US/docs/Web/Events/visibilitychange} + * @event BOOMR#visibility_changed + */ + "visibility_changed": [], + + /** + * Boomerang event, subscribe via {@link BOOMR.subscribe}. + * + * Fired when the `visibilityState` of the document has changed from + * `prerender` to `visible` + * + * @see {@link https://developer.mozilla.org/en-US/docs/Web/Events/visibilitychange} + * @event BOOMR#prerender_to_visible + */ + "prerender_to_visible": [], + + /** + * Boomerang event, subscribe via {@link BOOMR.subscribe}. + * + * Fired when a beacon is about to be sent. + * + * The subscriber can still add variables to the beacon at this point, + * either by modifying the `vars` paramter or calling {@link BOOMR.addVar}. + * + * @event BOOMR#before_beacon + * @property {object} vars Beacon variables + */ + "before_beacon": [], + + /** + * Boomerang event, subscribe via {@link BOOMR.subscribe}. + * + * Fired when a beacon was sent. + * + * The beacon variables cannot be modified at this point. Any calls + * to {@link BOOMR.addVar} or {@link BOOMR.removeVar} will apply to the + * next beacon. + * + * Also known as `onbeacon`. + * + * @event BOOMR#beacon + * @property {object} vars Beacon variables + */ + "beacon": [], + + /** + * Boomerang event, subscribe via {@link BOOMR.subscribe}. + * + * Fired when the page load beacon has been sent. + * + * This event should only happen once on a page. It does not apply + * to SPA soft navigations. + * + * @event BOOMR#page_load_beacon + * @property {object} vars Beacon variables + */ + "page_load_beacon": [], + + /** + * Boomerang event, subscribe via {@link BOOMR.subscribe}. + * + * Fired when an XMLHttpRequest has finished, or, if something calls + * {@link BOOMR.responseEnd}. + * + * @event BOOMR#xhr_load + * @property {object} data Event data + */ + "xhr_load": [], + + /** + * Boomerang event, subscribe via {@link BOOMR.subscribe}. + * + * Fired when the `click` event has happened on the `document`. + * + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onclick} + * @event BOOMR#click + */ + "click": [], + + /** + * Boomerang event, subscribe via {@link BOOMR.subscribe}. + * + * Fired when any `FORM` element is submitted. + * + * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit} + * @event BOOMR#form_submit + */ + "form_submit": [], + + /** + * Boomerang event, subscribe via {@link BOOMR.subscribe}. + * + * Fired whenever new configuration data is applied via {@link BOOMR.init}. + * + * Also known as `onconfig`. + * + * @event BOOMR#config + * @property {object} data Configuration data + */ + "config": [], + + /** + * Boomerang event, subscribe via {@link BOOMR.subscribe}. + * + * Fired whenever `XMLHttpRequest.open` is called. + * + * This event will only happen if {@link BOOMR.plugins.AutoXHR} is enabled. + * + * @event BOOMR#xhr_init + * @property {string} type XHR type ("xhr") + */ + "xhr_init": [], + + /** + * Boomerang event, subscribe via {@link BOOMR.subscribe}. + * + * Fired whenever a SPA plugin is about to track a new navigation. + * + * @event BOOMR#spa_init + * @property {object[]} parameters Navigation type (`spa` or `spa_hard`), URL and timings + */ + "spa_init": [], + + /** + * Boomerang event, subscribe via {@link BOOMR.subscribe}. + * + * Fired whenever a SPA navigation is complete. + * + * @event BOOMR#spa_navigation + * @property {object[]} parameters Timings + */ + "spa_navigation": [], + + /** + * Boomerang event, subscribe via {@link BOOMR.subscribe}. + * + * Fired whenever a SPA navigation is cancelled. + * + * @event BOOMR#spa_cancel + */ + "spa_cancel": [], + + /** + * Boomerang event, subscribe via {@link BOOMR.subscribe}. + * + * Fired whenever `XMLHttpRequest.send` is called. + * + * This event will only happen if {@link BOOMR.plugins.AutoXHR} is enabled. + * + * @event BOOMR#xhr_send + * @property {object} xhr `XMLHttpRequest` object + */ + "xhr_send": [], + + /** + * Boomerang event, subscribe via {@link BOOMR.subscribe}. + * + * Fired whenever and `XMLHttpRequest` has an error (if its `status` is + * set). + * + * This event will only happen if {@link BOOMR.plugins.AutoXHR} is enabled. + * + * Also known as `onxhrerror`. + * + * @event BOOMR#xhr_error + * @property {object} data XHR data + */ + "xhr_error": [], + + /** + * Boomerang event, subscribe via {@link BOOMR.subscribe}. + * + * Fired whenever a page error has happened. + * + * This event will only happen if {@link BOOMR.plugins.Errors} is enabled. + * + * Also known as `onerror`. + * + * @event BOOMR#error + * @property {object} err Error + */ + "error": [], + + /** + * Boomerang event, subscribe via {@link BOOMR.subscribe}. + * + * Fired whenever connection information changes via the + * Network Information API. + * + * This event will only happen if {@link BOOMR.plugins.Mobile} is enabled. + * + * @event BOOMR#netinfo + * @property {object} connection `navigator.connection` + */ + "netinfo": [], + + /** + * Boomerang event, subscribe via {@link BOOMR.subscribe}. + * + * Fired whenever a Rage Click is detected. + * + * This event will only happen if {@link BOOMR.plugins.Continuity} is enabled. + * + * @event BOOMR#rage_click + * @property {Event} e Event + */ + "rage_click": [], + + /** + * Boomerang event, subscribe via {@link BOOMR.subscribe}. + * + * Fired when an early beacon is about to be sent. + * + * The subscriber can still add variables to the early beacon at this point + * by calling {@link BOOMR.addVar}. + * + * This event will only happen if {@link BOOMR.plugins.Early} is enabled. + * + * @event BOOMR#before_early_beacon + * @property {object} data Event data + */ + "before_early_beacon": [], + + /** + * Boomerang event, subscribe via {@link BOOMR.subscribe}. + * + * Fired when an BFCache navigation occurs. + * + * The subscriber can still add variables to the BFCache beacon at this point + * by calling {@link BOOMR.addVar}. + * + * This event will only happen if {@link BOOMR.plugins.BFCache} is enabled. + * + * @event BOOMR#bfcache + * @property {object} data Event data + */ + "bfcache": [] + }, + + /** + * Public events + */ + public_events: { + /** + * Public event (fired on `document`), and can be subscribed via + * `document.addEventListener("onBeforeBoomerangBeacon", ...)` or + * `document.attachEvent("onpropertychange", ...)`. + * + * Maps to {@link BOOMR#event:before_beacon} + * + * @event document#onBeforeBoomerangBeacon + * @property {object} vars Beacon variables + */ + "before_beacon": "onBeforeBoomerangBeacon", + + /** + * Public event (fired on `document`), and can be subscribed via + * `document.addEventListener("onBoomerangBeacon", ...)` or + * `document.attachEvent("onpropertychange", ...)`. + * + * Maps to {@link BOOMR#event:before_beacon} + * + * @event document#onBoomerangBeacon + * @property {object} vars Beacon variables + */ + "beacon": "onBoomerangBeacon", + + /** + * Public event (fired on `document`), and can be subscribed via + * `document.addEventListener("onBoomerangLoaded", ...)` or + * `document.attachEvent("onpropertychange", ...)`. + * + * Fired when {@link BOOMR} has loaded and can be used. + * + * @event document#onBoomerangLoaded + */ + "onboomerangloaded": "onBoomerangLoaded" + }, + + /** + * Maps old event names to their updated name + */ + translate_events: { + "onbeacon": "beacon", + "onconfig": "config", + "onerror": "error", + "onxhrerror": "xhr_error" + }, + + // (End events) + + // + // Private Functions + // + + /** + * Creates a callback handler for the specified event type + * + * @param {string} type Event type + * @returns {function} Callback handler + */ + createCallbackHandler: function(type) { + return function(ev) { + var target; + + if (!ev) { + ev = w.event; + } + + if (ev.target) { + target = ev.target; + } + else if (ev.srcElement) { + target = ev.srcElement; + } + + if (target.nodeType === 3) { + // defeat Safari bug + target = target.parentNode; + } + + // don't capture events on flash objects + // because of context slowdowns in PepperFlash + if (target && + target.nodeName && + target.nodeName.toUpperCase() === "OBJECT" && + target.type === "application/x-shockwave-flash") { + return; + } + + impl.fireEvent(type, target); + }; + }, + + /** + * Clears all events + */ + clearEvents: function() { + var eventName; + + for (eventName in this.events) { + if (this.events.hasOwnProperty(eventName)) { + this.events[eventName] = []; + } + } + }, + + /** + * Clears all event listeners + */ + clearListeners: function() { + var type, i; + + for (type in impl.listenerCallbacks) { + if (impl.listenerCallbacks.hasOwnProperty(type)) { + // remove all callbacks -- removeListener is guaranteed + // to remove the element we're calling with + while (impl.listenerCallbacks[type].length) { + BOOMR.utils.removeListener( + impl.listenerCallbacks[type][0].el, + type, + impl.listenerCallbacks[type][0].fn); + } + } + } + + impl.listenerCallbacks = {}; + }, + + /** + * Fires the specified boomerang.js event. + * + * @param {string} e_name Event name + * @param {object} data Event data + */ + fireEvent: function(e_name, data) { + var i, handler, handlers, handlersLen; + + e_name = e_name.toLowerCase(); + + /* BEGIN_DEBUG */ + BOOMR.utils.mark("fire_event"); + BOOMR.utils.mark("fire_event:" + e_name + ":start"); + /* END_DEBUG */ + + // translate old names + if (this.translate_events[e_name]) { + e_name = this.translate_events[e_name]; + } + + if (!this.events.hasOwnProperty(e_name)) { + return; + } + + if (this.public_events.hasOwnProperty(e_name)) { + dispatchEvent(this.public_events[e_name], data); + } + + handlers = this.events[e_name]; + + // Before we fire any event listeners, let's call real_sendBeacon() to flush + // any beacon that is being held by the setImmediate. + if (e_name !== "before_beacon" && e_name !== "beacon" && e_name !== "before_early_beacon") { + BOOMR.real_sendBeacon(); + } + + // only call handlers at the time of fireEvent (and not handlers that are + // added during this callback to avoid an infinite loop) + handlersLen = handlers.length; + for (i = 0; i < handlersLen; i++) { + try { + handler = handlers[i]; + handler.fn.call(handler.scope, data, handler.cb_data); + } + catch (err) { + BOOMR.addError(err, "fireEvent." + e_name + "<" + i + ">"); + } + } + + // remove any 'once' handlers now that we've fired all of them + for (i = 0; i < handlersLen; i++) { + if (handlers[i].once) { + handlers.splice(i, 1); + handlersLen--; + i--; + } + } + + /* BEGIN_DEBUG */ + BOOMR.utils.mark("fire_event:" + e_name + ":end"); + BOOMR.utils.measure( + "fire_event:" + e_name, + "fire_event:" + e_name + ":start", + "fire_event:" + e_name + ":end"); + /* END_DEBUG */ + + return; + }, + + /** + * Notes when a SPA navigation has happened. + */ + spaNavigation: function() { + // a SPA navigation occured, force onloadfired to true + impl.onloadfired = true; + }, + + /** + * Determines whether a beacon URL is allowed based on + * `beacon_urls_allowed` config + * + * @param {string} url URL to test + * + */ + beaconUrlAllowed: function(url) { + if (!impl.beacon_urls_allowed || impl.beacon_urls_allowed.length === 0) { + return true; + } + + for (var i = 0; i < impl.beacon_urls_allowed.length; i++) { + var regEx = new RegExp(impl.beacon_urls_allowed[i]); + + if (regEx.exec(url)) { + return true; + } + } + + return false; + }, + + /** + * Checks browser for localStorage support + */ + checkLocalStorageSupport: function() { + var name = impl.LOCAL_STORAGE_PREFIX + "clss"; + + impl.localStorageSupported = false; + + // Browsers with cookies disabled or in private/incognito mode may throw an + // error when accessing the localStorage variable + try { + // we need JSON and localStorage support + if (!w.JSON || !w.localStorage) { + return; + } + + w.localStorage.setItem(name, name); + impl.localStorageSupported = (w.localStorage.getItem(name) === name); + } + catch (ignore) { + impl.localStorageSupported = false; + } + finally { + // If unsupported, then setItem threw and removeItem will also throw. + try { + if (w.localStorage) { + w.localStorage.removeItem(name); + } + } + catch (ignore) { + // empty + } + } + }, + + /** + * Fired when the Boomerang IFRAME is unloaded. + * + * If Boomerang was loaded into the root document, this code + * will not run. + */ + onFrameUnloaded: function() { + var i, prop; + + BOOMR.isUnloaded = true; + + // swap the original function back in for any overwrites + for (i = 0; i < impl.nativeOverwrites.length; i++) { + prop = impl.nativeOverwrites[i]; + + prop.obj[prop.functionName] = prop.origFn; + } + + impl.nativeOverwrites = []; + } + }; + + // + // Public BOOMR object + // + // We create a boomr object and then copy all its properties to BOOMR so that + // we don't overwrite anything additional that was added to BOOMR before this + // was called... for example, a plugin. + boomr = { + /** + * The timestamp when boomerang.js showed up on the page. + * + * This is the value of `BOOMR_start` we set earlier. + * @type {TimeStamp} + * + * @memberof BOOMR + */ + t_start: BOOMR_start, + + /** + * When the Boomerang plugins have all run. + * + * This value is generally set in zzz-last-plugin.js. + * @type {TimeStamp} + * + * @memberof BOOMR + */ + t_end: undefined, + + /** + * URL of boomerang.js. + * + * @type {string} + * + * @memberof BOOMR + */ + url: "", + + /** + * (Optional) URL of configuration file + * + * @type {string} + * + * @memberof BOOMR + */ + config_url: null, + + /** + * Whether or not Boomerang was loaded after the `onload` event. + * + * @type {boolean} + * + * @memberof BOOMR + */ + loadedLate: false, + + /** + * Current number of beacons sent. + * + * Will be incremented and added to outgoing beacon as `n`. + * + * @type {number} + * + */ + beaconsSent: 0, + + /** + * Whether or not Boomerang thinks it has been unloaded (if it was + * loaded in an IFRAME) + * + * @type {boolean} + */ + isUnloaded: false, + + /** + * Whether or not we're in the middle of building a beacon. + * + * If so, the code desiring to send a beacon should wait until the beacon + * event and try again. At that point, it should set this flag to true. + * + * @type {boolean} + */ + beaconInQueue: false, + + /* + * Cache of cookies set + */ + cookies: {}, + + /** + * Whether or not we've tested cookie setting + */ + testedCookies: false, + + /** + * Constants visible to the world + * @class BOOMR.constants + */ + constants: { + /** + * SPA beacon types + * + * @type {string[]} + * + * @memberof BOOMR.constants + */ + BEACON_TYPE_SPAS: ["spa", "spa_hard"], + + /** + * Maximum GET URL length. + * Using 2000 here as a de facto maximum URL length based on: + * https://stackoverflow.com/questions/417142/what-is-the-maximum-length-of-a-url-in-different-browsers + * + * @type {number} + * + * @memberof BOOMR.constants + */ + MAX_GET_LENGTH: 2000 + }, + + /** + * Session data + * @class BOOMR.session + */ + session: { + /** + * Session Domain. + * + * You can disable all cookies by setting site_domain to a falsy value. + * + * @type {string} + * + * @memberof BOOMR.session + */ + domain: impl.site_domain, + + /** + * Session ID. This will be randomly generated in the client but may + * be overwritten by the server if not set. + * + * @type {string} + * + * @memberof BOOMR.session + */ + ID: undefined, + + /** + * Session start time. + * + * @type {TimeStamp} + * + * @memberof BOOMR.session + */ + start: undefined, + + /** + * Session length (number of pages) + * + * @type {number} + * + * @memberof BOOMR.session + */ + length: 0, + + /** + * Session enabled (Are session cookies enabled?) + * + * @type {boolean} + * + * @memberof BOOMR.session + */ + enabled: true + }, + + /** + * @class BOOMR.utils + */ + utils: { + /** + * Determines whether or not the browser has `postMessage` support + * + * @returns {boolean} True if supported + */ + hasPostMessageSupport: function() { + if (!w.postMessage || typeof w.postMessage !== "function" && typeof w.postMessage !== "object") { + return false; + } + + return true; + }, + + /** + * Converts an object to a string. + * + * @param {object} o Object + * @param {string} separator Member separator + * @param {number} nest_level Number of levels to recurse + * + * @returns {string} String representation of the object + * + * @memberof BOOMR.utils + */ + objectToString: function(o, separator, nest_level) { + var value = [], + k; + + if (!o || typeof o !== "object") { + return o; + } + + if (separator === undefined) { + separator = "\n\t"; + } + + if (!nest_level) { + nest_level = 0; + } + + if (BOOMR.utils.isArray(o)) { + for (k = 0; k < o.length; k++) { + if (nest_level > 0 && o[k] !== null && typeof o[k] === "object") { + value.push( + this.objectToString( + o[k], + separator + (separator === "\n\t" ? "\t" : ""), + nest_level - 1 + ) + ); + } + else { + if (separator === "&") { + value.push(encodeURIComponent(o[k])); + } + else { + value.push(o[k]); + } + } + } + + separator = ","; + } + else { + for (k in o) { + if (Object.prototype.hasOwnProperty.call(o, k)) { + if (nest_level > 0 && o[k] !== null && typeof o[k] === "object") { + value.push(encodeURIComponent(k) + "=" + + this.objectToString( + o[k], + separator + (separator === "\n\t" ? "\t" : ""), + nest_level - 1 + ) + ); + } + else { + if (separator === "&") { + value.push(encodeURIComponent(k) + "=" + encodeURIComponent(o[k])); + } + else { + value.push(k + "=" + o[k]); + } + } + } + } + } + + return value.join(separator); + }, + + /** + * Gets the cached value of the cookie identified by `name`. + * + * @param {string} name Cookie name + * + * @returns {string|undefined} Cookie value, if set. + * + * @memberof BOOMR.utils + */ + getCookie: function(name) { + var cookieVal; + + if (!name) { + return null; + } + + /* BEGIN_DEBUG */ + BOOMR.utils.mark("get_cookie"); + /* END_DEBUG */ + + if (typeof BOOMR.cookies[name] !== "undefined") { + // a cached value of false indicates that the value doesn't exist, if so, + // return undefined per the API + return BOOMR.cookies[name] === false ? undefined : BOOMR.cookies[name]; + } + + // unknown value + cookieVal = this.getRawCookie(name); + + if (typeof cookieVal === "undefined") { + // set to false to indicate we've attempted to get this cookie + BOOMR.cookies[name] = false; + + // but return undefined per the API + return undefined; + } + + BOOMR.cookies[name] = cookieVal; + + return BOOMR.cookies[name]; + }, + + /** + * Gets the value of the cookie identified by `name`. + * + * @param {string} name Cookie name + * + * @returns {string|null} Cookie value, if set. + * + * @memberof BOOMR.utils + */ + getRawCookie: function(name) { + if (!name) { + return null; + } + + /* BEGIN_DEBUG */ + BOOMR.utils.mark("get_raw_cookie"); + /* END_DEBUG */ + + name = " " + name + "="; + + var i, cookies; + + cookies = " " + d.cookie + ";"; + + if ((i = cookies.indexOf(name)) >= 0) { + i += name.length; + + return cookies.substring(i, cookies.indexOf(";", i)).replace(/^"/, "").replace(/"$/, ""); + } + }, + + /** + * Sets the cookie named `name` to the serialized value of `subcookies`. + * + * @param {string} name The name of the cookie + * @param {object} subcookies Key/value pairs to write into the cookie. + * These will be serialized as an & separated list of URL encoded key=value pairs. + * @param {number} max_age Lifetime in seconds of the cookie. + * Set this to 0 to create a session cookie that expires when + * the browser is closed. If not set, defaults to 0. + * + * @returns {boolean} True if the cookie was set successfully + * + * @example + * BOOMR.utils.setCookie("RT", { s: t_start, r: url }); + * + * @memberof BOOMR.utils + */ + setCookie: function(name, subcookies, max_age) { + var value, nameval, savedval, c, exp; + + if (!name || !BOOMR.session.domain || typeof subcookies === "undefined") { + BOOMR.debug("Invalid parameters or site domain: " + name + "/" + subcookies + "/" + BOOMR.session.domain); + + BOOMR.addVar("nocookie", 1); + + return false; + } + + /* BEGIN_DEBUG */ + BOOMR.utils.mark("set_cookie"); + /* END_DEBUG */ + + value = this.objectToString(subcookies, "&"); + + if (value === BOOMR.cookies[name]) { + // no change + return true; + } + + nameval = name + "=\"" + value + "\""; + + if (nameval.length < 500) { + c = [nameval, "path=/", "domain=" + BOOMR.session.domain]; + + if (typeof max_age === "number") { + exp = new Date(); + exp.setTime(exp.getTime() + max_age * 1000); + exp = exp.toGMTString(); + c.push("expires=" + exp); + } + + var extraAttributes = this.getSameSiteAttributeParts(); + + /** + * 1. We check if the Secure attribute wasn't added already because SameSite=None will force adding it. + * 2. We check the current protocol because if we are on HTTP and we try to create a secure cookie with + * SameSite=Strict then a cookie will be created with SameSite=Lax. + */ + if (location.protocol === "https:" && + impl.secure_cookie === true && + extraAttributes.indexOf("Secure") === -1) { + extraAttributes.push("Secure"); + } + + // add extra attributes + c = c.concat(extraAttributes); + + /* BEGIN_DEBUG */ + BOOMR.utils.mark("set_cookie_real"); + /* END_DEBUG */ + + // set the cookie + d.cookie = c.join("; "); + + // we only need to test setting the cookie once + if (BOOMR.testedCookies) { + // only cache this cookie value if the expiry is in the future + if (typeof max_age !== "number" || max_age > 0) { + BOOMR.cookies[name] = value; + } + else { + // the cookie is going to expire right away, don't cache it + BOOMR.cookies[name] = undefined; + } + + return true; + } + + // unset the cached cookie value, in case the set doesn't work + BOOMR.cookies[name] = undefined; + + // confirm cookie was set (could be blocked by user's settings, etc.) + savedval = this.getRawCookie(name); + + // the saved cookie should be the same or undefined in the case of removeCookie + if (value === savedval || + (typeof savedval === "undefined" && typeof max_age === "number" && max_age <= 0)) { + // re-set the cached value + BOOMR.cookies[name] = value; + + // note we've saved successfully + BOOMR.testedCookies = true; + + BOOMR.removeVar("nocookie"); + + return true; + } + + BOOMR.warn("Saved cookie value doesn't match what we tried to set:\n" + value + "\n" + savedval); + } + else { + BOOMR.warn("Cookie too long: " + nameval.length + " " + nameval); + } + + BOOMR.addVar("nocookie", 1); + + return false; + }, + + /** + * Parse a cookie string returned by {@link BOOMR.utils.getCookie} and + * split it into its constituent subcookies. + * + * @param {string} cookie Cookie value + * + * @returns {object} On success, an object of key/value pairs of all + * sub cookies. Note that some subcookies may have empty values. + * `null` if `cookie` was not set or did not contain valid subcookies. + * + * @memberof BOOMR.utils + */ + getSubCookies: function(cookie) { + var cookies_a, + i, l, kv, + gotcookies = false, + cookies = {}; + + if (!cookie) { + return null; + } + + if (typeof cookie !== "string") { + BOOMR.debug("TypeError: cookie is not a string: " + typeof cookie); + + return null; + } + + cookies_a = cookie.split("&"); + + for (i = 0, l = cookies_a.length; i < l; i++) { + kv = cookies_a[i].split("="); + + if (kv[0]) { + // just in case there's no value + kv.push(""); + cookies[decodeURIComponent(kv[0])] = decodeURIComponent(kv[1]); + gotcookies = true; + } + } + + return gotcookies ? cookies : null; + }, + + /** + * Removes the cookie identified by `name` by nullifying its value, + * and making it a session cookie. + * + * @param {string} name Cookie name + * + * @memberof BOOMR.utils + */ + removeCookie: function(name) { + return this.setCookie(name, {}, -86400); + }, + + /** + * Depending on Boomerang configuration and checks of current protocol and + * compatible browsers the logic below will provide an array of cookie + * attributes that are needed for a successful creation of a cookie that + * contains the SameSite attribute. + * + * How it works: + * 1. We read the Boomerang configuration key `same_site_cookie` where + * one of the following values `None`, `Lax` or `Strict` is expected. + * 2. A configuration value of `same_site_cookie` will be read in case-insensitive + * manner. E.g. `Lax`, `lax` and `lAx` will produce same result - `SameSite=Lax`. + * 3. If a `same_site_cookie` configuration value is not specified a cookie + * will be created with `SameSite=Lax`. + * 4. If a `same_site_cookie` configuration value does't match any of + * `None`, `Lax` or `Strict` then a cookie will be created with `SameSite=Lax`. + * 5. The `Secure` cookie attribute will be added when a cookie is created + * with `SameSite=None`. + * 6. It's possible that a Boomerang plugin or external code may need cookies + * to be created with `SameSite=None`. In such cases we check a special + * flag `forced_same_site_cookie_none`. If the value of this flag is equal to `true` + * then the `same_site_cookie` value will be ignored and Boomerang cookies + * will be created with `SameSite=None`. + * + * SameSite=None - INCOMPATIBILITIES and EXCEPTIONS: + * + * There are known problems with older browsers where cookies created + * with `SameSite=None` are `dropped` or created with `SameSite=Strict`. + * Reference: https://www.chromium.org/updates/same-site/incompatible-clients + * + * 1. If we detect a browser that can't create safely a cookie with `SameSite=None` + * then Boomerang will create a cookie without the `SameSite` attribute. + * 2. A cookie with `SameSite=None` can be created only over `HTTPS` connection. + * If current connection is `HTTP` then a cookie will be created + * without the `SameSite` attribute. + * + * + * @returns {Array} of cookie attributes used for setting a cookie with SameSite attribute + * + * @memberof BOOMR.utils + */ + getSameSiteAttributeParts: function() { + var sameSiteMode = impl.same_site_cookie.toUpperCase(); + + if (impl.forced_same_site_cookie_none) { + sameSiteMode = "NONE"; + } + + if (sameSiteMode === "LAX") { + return ["SameSite=Lax"]; + } + + if (sameSiteMode === "NONE") { + if (location.protocol === "https:" && this.isCurrentUASameSiteNoneCompatible()) { + return ["SameSite=None", "Secure"]; + } + + // Fallback to browser's default + return []; + } + + if (sameSiteMode === "STRICT") { + return ["SameSite=Strict"]; + } + + return ["SameSite=Lax"]; + }, + + /** + * Retrieve items from localStorage + * + * @param {string} name Name of storage + * + * @returns {object|null} Returns object retrieved from localStorage. + * Returns undefined if not found or expired. + * Returns null if parameters are invalid or an error occured + * + * @memberof BOOMR.utils + */ + getLocalStorage: function(name) { + var value, data; + + if (!name || !impl.localStorageSupported) { + return null; + } + + /* BEGIN_DEBUG */ + BOOMR.utils.mark("get_local_storage"); + /* END_DEBUG */ + + try { + value = w.localStorage.getItem(impl.LOCAL_STORAGE_PREFIX + name); + + if (value === null) { + return undefined; + } + + data = w.JSON.parse(value); + } + catch (e) { + BOOMR.warn(e); + + return null; + } + + if (!data || typeof data.items !== "object") { + // Items are invalid + this.removeLocalStorage(name); + + return null; + } + + if (typeof data.expires === "number") { + if (BOOMR.now() >= data.expires) { + // Items are expired + this.removeLocalStorage(name); + + return undefined; + } + } + + return data.items; + }, + + /** + * Saves items in localStorage + * The value stored in localStorage will be a JSON string representation of {"items": items, "expiry": expiry} + * where items is the object we're saving and expiry is an optional epoch number of when the data is to be + * considered expired + * + * @param {string} name Name of storage + * @param {object} items Items to be saved + * @param {number} max_age Age in seconds before items are to be considered expired + * + * @returns {boolean} True if the localStorage was set successfully + * + * @memberof BOOMR.utils + */ + setLocalStorage: function(name, items, max_age) { + var data, value, savedval; + + if (!name || !impl.localStorageSupported || typeof items !== "object") { + return false; + } + + /* BEGIN_DEBUG */ + BOOMR.utils.mark("set_local_storage"); + /* END_DEBUG */ + + data = {"items": items}; + + if (typeof max_age === "number") { + data.expires = BOOMR.now() + (max_age * 1000); + } + + value = w.JSON.stringify(data); + + if (value.length < 50000) { + try { + w.localStorage.setItem(impl.LOCAL_STORAGE_PREFIX + name, value); + // confirm storage was set (could be blocked by user's settings, etc.) + savedval = w.localStorage.getItem(impl.LOCAL_STORAGE_PREFIX + name); + + if (value === savedval) { + return true; + } + } + catch (ignore) { + // Empty + } + + BOOMR.warn("Saved storage value doesn't match what we tried to set:\n" + value + "\n" + savedval); + } + else { + BOOMR.warn("Storage items too large: " + value.length + " " + value); + } + + return false; + }, + + /** + * Remove items from localStorage + * + * @param {string} name Name of storage + * + * @returns {boolean} True if item was removed from localStorage. + * + * @memberof BOOMR.utils + */ + removeLocalStorage: function(name) { + if (!name || !impl.localStorageSupported) { + return false; + } + try { + w.localStorage.removeItem(impl.LOCAL_STORAGE_PREFIX + name); + + return true; + } + catch (ignore) { + // Empty + } + + return false; + }, + + /** + * Cleans up a URL by removing the query string (if configured), and + * limits the URL to the specified size. + * + * @param {string} url URL to clean + * @param {number} urlLimit Maximum size, in characters, of the URL + * + * @returns {string} Cleaned up URL + * + * @memberof BOOMR.utils + */ + cleanupURL: function(url, urlLimit) { + if (!url || BOOMR.utils.isArray(url)) { + return ""; + } + + if (impl.strip_query_string) { + url = url.replace(/\?.*/, "?qs-redacted"); + } + + if (typeof urlLimit !== "undefined" && url && url.length > urlLimit) { + // We need to break this URL up. Try at the query string first. + var qsStart = url.indexOf("?"); + + if (qsStart !== -1 && qsStart < urlLimit) { + url = url.substr(0, qsStart) + "?..."; + } + else { + // No query string, just stop at the limit + url = url.substr(0, urlLimit - 3) + "..."; + } + } + + return url; + }, + + /** + * Gets the URL with the query string replaced with a hash of its contents. + * + * @param {string} url URL + * @param {boolean} stripHash Whether or not to strip the hash + * + * @returns {string} URL with query string hashed + * + * @memberof BOOMR.utils + */ + hashQueryString: function(url, stripHash) { + if (!url) { + return url; + } + + if (!url.match) { + BOOMR.addError("TypeError: Not a string", "hashQueryString", typeof url); + + return ""; + } + + if (url.match(/^\/\//)) { + url = location.protocol + url; + } + + if (!url.match(/^(https?|file):/)) { + BOOMR.error("Passed in URL is invalid: " + url); + + return ""; + } + + if (stripHash) { + url = url.replace(/#.*/, ""); + } + + return url.replace(/\?([^#]*)/, function(m0, m1) { + return "?" + (m1.length > 10 ? BOOMR.utils.hashString(m1) : m1); + }); + }, + + /** + * Sets the object's properties if anything in config matches + * one of the property names. + * + * @param {object} o The plugin's `impl` object within which it stores + * all its configuration and private properties + * @param {object} config The config object passed in to the plugin's + * `init()` method. + * @param {string} plugin_name The plugin's name in the {@link BOOMR.plugins} object. + * @param {string[]} properties An array containing a list of all configurable + * properties that this plugin has. + * + * @returns {boolean} True if a property was set + * + * @memberof BOOMR.utils + */ + pluginConfig: function(o, config, plugin_name, properties) { + var i, + props = 0; + + if (!config || !config[plugin_name]) { + return false; + } + + for (i = 0; i < properties.length; i++) { + if (config[plugin_name][properties[i]] !== undefined) { + o[properties[i]] = config[plugin_name][properties[i]]; + props++; + } + } + + return (props > 0); + }, + + /** + * `filter` for arrays + * + * @param {Array} array The array to iterate over. + * @param {Function} predicate The function invoked per iteration. + * + * @returns {Array} Returns the new filtered array. + * + * @memberof BOOMR.utils + */ + arrayFilter: function(array, predicate) { + var result = []; + + if (!(this.isArray(array) || (array && typeof array.length === "number")) || + typeof predicate !== "function") { + return result; + } + + if (typeof array.filter === "function") { + result = array.filter(predicate); + } + else { + var index = -1, + length = array.length, + value; + + while (++index < length) { + value = array[index]; + + if (predicate(value, index, array)) { + result[result.length] = value; + } + } + } + + return result; + }, + + /** + * `find` for Arrays + * + * @param {Array} array The array to iterate over + * @param {Function} predicate The function invoked per iteration + * + * @returns {Array} Returns the value of first element that satisfies + * the predicate + * + * @memberof BOOMR.utils + */ + arrayFind: function(array, predicate) { + if (!(this.isArray(array) || (array && typeof array.length === "number")) || + typeof predicate !== "function") { + return undefined; + } + + if (typeof array.find === "function") { + return array.find(predicate); + } + else { + var index = -1, + length = array.length, + value; + + while (++index < length) { + value = array[index]; + + if (predicate(value, index, array)) { + return value; + } + } + + return undefined; + } + }, + + /** + * MutationObserver feature detection + * + * Always returns false for IE 11 due several bugs in it's implementation that MS flagged as Won't Fix. + * In IE11, XHR responseXML might be malformed if MO is enabled (where extra newlines get added in nodes + * with UTF-8 content). + * + * Another IE 11 MO bug can cause the process to crash when certain mutations occur. + * + * For the process crash issue, see https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/8137215/ + * and + * https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/15167323/ + * + * @returns {boolean} Returns true if MutationObserver is supported. + * + * @memberof BOOMR.utils + */ + isMutationObserverSupported: function() { + // We can only detect IE 11 bugs by UA sniffing. + var ie11 = (w && + w.navigator && + !w.navigator.userAgentData && + w.navigator.userAgent && + w.navigator.userAgent.match(/Trident.*rv[ :]*11\./)); + + return (!ie11 && w && w.MutationObserver && typeof w.MutationObserver === "function"); + }, + + /** + * The callback function may return a falsy value to disconnect the + * observer after it returns, or a truthy value to keep watching for + * mutations. If the return value is numeric and greater than 0, then + * this will be the new timeout. If it is boolean instead, then the + * timeout will not fire any more so the caller MUST call disconnect() + * at some point. + * + * @callback BOOMR~addObserverCallback + * @param {object[]} mutations List of mutations detected by the observer or `undefined` if the observer timed out + * @param {object} callback_data Is the passed in `callback_data` parameter without modifications + */ + + /** + * Add a MutationObserver for a given element and terminate after `timeout`ms. + * + * @param {DOMElement} el DOM element to watch for mutations + * @param {MutationObserverInit} config MutationObserverInit object + * (https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver#MutationObserverInit) + * @param {number} timeout Number of milliseconds of no mutations after which the observer should be + * automatically disconnected. + * If set to a falsy value, the observer will wait indefinitely for Mutations. + * @param {BOOMR~addObserverCallback} callback Callback function to call either on timeout or if mutations + * are detected. + * @param {object} callback_data Any data to be passed to the callback function as its second parameter. + * @param {object} callback_ctx An object that represents the `this` object of the `callback` method. + * Leave unset the callback function is not a method of an object. + * + * @returns {object|null} + * - `null` if a MutationObserver could not be created OR + * - An object containing the observer and the timer object: + * `{ observer: , timer: }` + * - The caller can use this to disconnect the observer at any point + * by calling `retval.observer.disconnect()` + * - Note that the caller should first check to see if `retval.observer` + * is set before calling `disconnect()` as it may have been cleared automatically. + * + * @memberof BOOMR.utils + */ + addObserver: function(el, config, timeout, callback, callback_data, callback_ctx) { + var MO, zs, + o = {observer: null, timer: null}; + + /* BEGIN_DEBUG */ + BOOMR.utils.mark("add_observer"); + /* END_DEBUG */ + + if (!this.isMutationObserverSupported() || !callback || !el) { + return null; + } + + function done(mutations) { + var run_again = false; + + /* BEGIN_DEBUG */ + BOOMR.utils.mark("mutation_observer_callback"); + /* END_DEBUG */ + + if (o.timer) { + clearTimeout(o.timer); + o.timer = null; + } + + if (callback) { + run_again = callback.call(callback_ctx, mutations, callback_data); + + if (!run_again) { + callback = null; + } + } + + if (!run_again && o.observer) { + o.observer.disconnect(); + o.observer = null; + } + + if (typeof run_again === "number" && run_again > 0) { + o.timer = setTimeout(done, run_again); + } + } + + MO = w.MutationObserver; + + // if the site uses Zone.js then get the native MutationObserver + if (w.Zone && typeof w.Zone.__symbol__ === "function") { + zs = w.Zone.__symbol__("MutationObserver"); + + if (zs && typeof zs === "string" && w.hasOwnProperty(zs) && typeof w[zs] === "function") { + BOOMR.debug("Detected Zone.js, using native MutationObserver"); + MO = w[zs]; + } + } + + o.observer = new MO(done); + + if (timeout) { + o.timer = setTimeout(done, o.timeout); + } + + o.observer.observe(el, config); + + return o; + }, + + /** + * Adds an event listener. + * + * @param {DOMElement} el DOM element + * @param {string} type Event name + * @param {function} fn Callback function + * @param {boolean|object} passiveOrOpts Passive mode or Options object + * + * @memberof BOOMR.utils + */ + addListener: function(el, type, fn, passiveOrOpts) { + var opts = false; + + /* BEGIN_DEBUG */ + BOOMR.utils.mark("add_listener"); + /* END_DEBUG */ + + if (el.addEventListener) { + if (typeof passiveOrOpts === "object") { + opts = passiveOrOpts; + } + else if (typeof passiveOrOpts === "boolean" && passiveOrOpts && BOOMR.browser.supportsPassive()) { + opts = { + capture: false, + passive: true + }; + } + + el.addEventListener(type, fn, opts); + } + else if (el.attachEvent) { + el.attachEvent("on" + type, fn); + } + + // ensure the type arry exists + impl.listenerCallbacks[type] = impl.listenerCallbacks[type] || []; + + // save a reference to the target object and function + impl.listenerCallbacks[type].push({ el: el, fn: fn}); + }, + + /** + * Removes an event listener. + * + * @param {DOMElement} el DOM element + * @param {string} type Event name + * @param {function} fn Callback function + * + * @memberof BOOMR.utils + */ + removeListener: function(el, type, fn) { + var i; + + /* BEGIN_DEBUG */ + BOOMR.utils.mark("remove_listener"); + /* END_DEBUG */ + + if (el.removeEventListener) { + // NOTE: We don't need to match any other options (e.g. passive) + // from addEventListener, as removeEventListener only cares + // about captive. + el.removeEventListener(type, fn, false); + } + else if (el.detachEvent) { + el.detachEvent("on" + type, fn); + } + + if (impl.listenerCallbacks.hasOwnProperty(type)) { + for (var i = 0; i < impl.listenerCallbacks[type].length; i++) { + if (fn === impl.listenerCallbacks[type][i].fn && + el === impl.listenerCallbacks[type][i].el) { + impl.listenerCallbacks[type].splice(i, 1); + + return; + } + } + } + }, + + /** + * Determines if the specified object is an `Array` or not + * + * @param {object} ary Object in question + * + * @returns {boolean} True if the object is an `Array` + * + * @memberof BOOMR.utils + */ + isArray: function(ary) { + return Object.prototype.toString.call(ary) === "[object Array]"; + }, + + /** + * Determines if the specified value is in the array + * + * @param {object} val Value to check + * @param {object} ary Object in question + * + * @returns {boolean} True if the value is in the Array + * + * @memberof BOOMR.utils + */ + inArray: function(val, ary) { + var i; + + if (typeof val === "undefined" || typeof ary === "undefined" || !ary.length) { + return false; + } + + for (i = 0; i < ary.length; i++) { + if (ary[i] === val) { + return true; + } + } + + return false; + }, + + /** + * Get a query parameter value from a URL's query string + * + * @param {string} param Query parameter name + * @param {string|Object} [url] URL containing the query string, or a link object. + * Defaults to `BOOMR.window.location` + * + * @returns {string|null} URI decoded value or null if param isn't a query parameter + * + * @memberof BOOMR.utils + */ + getQueryParamValue: function(param, url) { + var l, params, i, kv; + + if (!param) { + return null; + } + + if (typeof url === "string") { + l = BOOMR.window.document.createElement("a"); + l.href = url; + } + else if (typeof url === "object" && typeof url.search === "string") { + l = url; + } + else { + l = BOOMR.window.location; + } + + // Now that we match, pull out all query string parameters + params = l.search.slice(1).split(/&/); + + for (i = 0; i < params.length; i++) { + if (params[i]) { + kv = params[i].split("="); + + if (kv.length && kv[0] === param) { + try { + return kv.length > 1 ? decodeURIComponent(kv.splice(1).join("=").replace(/\+/g, " ")) : ""; + } + catch (e) { + /** + * We have different messages for the same error in different browsers but + * we can look at the error name because it looks more consistent. + * + * Examples: + * - URIError: The URI to be encoded contains invalid character (Edge) + * - URIError: malformed URI sequence (Firefox) + * - URIError: URI malformed (Chrome) + * - URIError: URI error (Safari 13.0) / Missing on MDN but this is the result of my local tests. + * + * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Malformed_URI#Message + */ + if (e && typeof e.name === "string" && e.name.indexOf("URIError") !== -1) { + // NOP + } + else { + throw e; + } + } + } + } + } + + return null; + }, + + /** + * Generates a pseudo-random UUID (Version 4): + * https://en.wikipedia.org/wiki/Universally_unique_identifier + * + * @returns {string} UUID + * + * @memberof BOOMR.utils + */ + generateUUID: function() { + return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) { + var r = Math.random() * 16 | 0; + var v = c === "x" ? r : (r & 0x3 | 0x8); + + return v.toString(16); + }); + }, + + /** + * Generates a random ID based on the specified number of characters. Uses + * characters a-z0-9. + * + * @param {number} chars Number of characters (max 40) + * + * @returns {string} Random ID + * + * @memberof BOOMR.utils + */ + generateId: function(chars) { + return "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx".substr(0, chars || 40).replace(/x/g, function(c) { + var c = (Math.random() || 0.01).toString(36); + + // some implementations may return "0" for small numbers + if (c === "0") { + return "0"; + } + else { + return c.substr(2, 1); + } + }); + }, + + /** + * Attempt to serialize an object, preferring JSURL over JSON.stringify + * + * @param {Object} value Object to serialize + * @returns {string} serialized version of value, empty-string if not possible + */ + serializeForUrl: function(value) { + if (BOOMR.utils.Compression && BOOMR.utils.Compression.jsUrl) { + return BOOMR.utils.Compression.jsUrl(value); + } + + if (window.JSON) { + return JSON.stringify(value); + } + + // not supported + BOOMR.debug("JSON is not supported"); + + return ""; + }, + + /* BEGIN_DEBUG */ + /** + * Attempt to deserialize an object, preferring JSURL over JSON.parse + * + * @param {string} value String to deserialize + * + * @returns {object|undefined} Deserialized version of value, undefined if not possible + */ + deserializeForUrl: function(value) { + if (BOOMR.utils.Compression && BOOMR.utils.Compression.jsUrlDecompress) { + return BOOMR.utils.Compression.jsUrlDecompress(value); + } + + if (window.JSON) { + return JSON.parse(value); + } + + // not supported + BOOMR.debug("JSON is not supported"); + + return; + }, + /* END_DEBUG */ + + /** + * Attempt to identify the URL of boomerang itself using multiple methods for cross-browser support + * + * This method uses document.currentScript (which cannot be called from an event handler), + * script.readyState (IE6-10), + * and the stack property of a caught Error object. + * + * @returns {string} The URL of the currently executing boomerang script. + */ + getMyURL: function() { + var stack; + // document.currentScript works in all browsers except for IE: https://caniuse.com/#feat=document-currentscript + // #boomr-if-as works in all browsers if the page uses our standard iframe loader + // #boomr-scr-as works in all browsers if the page uses our preloader loader + // BOOMR_script will be undefined on IE for pages that do not use our standard loaders + + // Note that we do not use `w.document` or `d` here because we need the current execution context + var BOOMR_script = (document.currentScript || + document.getElementById("boomr-if-as") || + document.getElementById("boomr-scr-as")); + + if (BOOMR_script) { + return BOOMR_script.src; + } + + // For IE 6-10 users on pages not using the standard loader, we iterate through all scripts backwards + var scripts = document.getElementsByTagName("script"), + i; + + // i-- is both a decrement as well as a condition, ie, the loop will terminate when i goes from 0 to -1 + for (i = scripts.length; i--;) { + // We stop at the first script that has its readyState set to interactive indicating that it is + // currently executing + if (scripts[i].readyState === "interactive") { + return scripts[i].src; + } + } + + // For IE 11, we throw an Error and inspect its stack property in the catch block + // This also works on IE10, but throwing is disruptive so we try to avoid it and use + // the less disruptive script iterator above + try { + throw new Error(); + } + catch (e) { + if ("stack" in e) { + stack = this.arrayFilter(e.stack.split(/\n/), function(l) { + return l.match(/https?:\/\//); + }); + + if (stack && stack.length) { + return stack[0].replace(/.*(https?:\/\/.+?)(:\d+)+\D*$/m, "$1"); + } + } + // FWIW, on IE 8 & 9, the Error object does not contain a stack property, but if you have an uncaught error, + // and a `window.onerror` handler (not using addEventListener), then the second argument to that handler is + // the URL of the script that threw. The handler needs to `return true;` to prevent the default error handler + // This flow is asynchronous though (due to the event handler), so won't work in a function return scenario + // like this (we can't use promises because we would only need this hack in browsers that don't support + // promises). + } + + return ""; + }, + + /* + * Gets the Scroll x and y (rounded) for a page + * + * @returns {object} Scroll x and y coordinates + */ + scroll: function() { + // Adapted from: + // https://developer.mozilla.org/en-US/docs/Web/API/Window/scrollY + var supportPageOffset = w.pageXOffset !== undefined; + var isCSS1Compat = ((w.document.compatMode || "") === "CSS1Compat"); + + var ret = { + x: 0, + y: 0 + }; + + if (supportPageOffset) { + if (typeof w.pageXOffset === "function") { + ret.x = w.pageXOffset(); + ret.y = w.pageYOffset(); + } + else { + ret.x = w.pageXOffset; + ret.y = w.pageYOffset; + } + } + else if (isCSS1Compat) { + ret.x = w.document.documentElement.scrollLeft; + ret.y = w.document.documentElement.scrollTop; + } + else { + ret.x = w.document.body.scrollLeft; + ret.y = w.document.body.scrollTop; + } + + // round to full numbers + if (typeof ret.sx === "number") { + ret.sx = Math.round(ret.sx); + } + + if (typeof ret.sy === "number") { + ret.sy = Math.round(ret.sy); + } + + return ret; + }, + + /** + * Gets the window height + * + * @returns {number} Window height + */ + windowHeight: function() { + return w.innerHeight || w.document.documentElement.clientHeight || w.document.body.clientHeight; + }, + + /** + * Gets the window width + * + * @returns {number} Window width + */ + windowWidth: function() { + return w.innerWidth || w.document.documentElement.clientWidth || w.document.body.clientWidth; + }, + + /** + * Determines if the function is native or not + * + * @param {function} fn Function + * + * @returns {boolean} True when the function is native + */ + isNative: function(fn) { + return !!fn && + fn.toString && + !fn.hasOwnProperty("toString") && + /\[native code\]/.test(String(fn)); + }, + + /** + * Overwrites a function on the specified object. + * + * When the Boomerang IFRAME unloads, it will swap the old + * function back in, so calls to the functions are successful. + * + * If this isn't done, callers of the overwritten functions may still + * call into freed Boomerang code or the IFRAME that is partially unloaded, + * leading to "Freed script" errors or exceptions from accessing + * unloaded DOM properties. + * + * This tracking isn't needed if Boomerang is loaded in the root + * document, as everthing will be cleaned up along with Boomerang + * on unload. + * + * @param {object} obj Object whose property will be overwritten + * @param {string} functionName Function name + * @param {function} newFn New function + */ + overwriteNative: function(obj, functionName, newFn) { + // bail if the object doesn't exist + if (!obj || !newFn) { + return; + } + + // we only need to keep track if we're running Boomerang in + // an IFRAME + if (BOOMR.boomerang_frame !== BOOMR.window) { + // note we overwrote this + impl.nativeOverwrites.push({ + obj: obj, + functionName: functionName, + origFn: obj[functionName] + }); + } + + obj[functionName] = newFn; + }, + + /** + * Determines if the given input is an Integer. + * Relies on standard Number.isInteger() function that available + * is most browsers except IE. For IE, this relies on the polyfill + * provided by MDN: + * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isInteger#Polyfill + * + * @param {number} input dat + * + * @returns {string} Random ID + * + * @memberof BOOMR.utils + * + */ + isInteger: function(data) { + var isInt = Number.isInteger || function(value) { + return typeof value === "number" && + isFinite(value) && + Math.floor(value) === value; + }; + + return isInt(data); + }, + + /** + * Determines whether or not an Object is empty + * + * @param {object} data Data object + * + * @returns {boolean} True if the object has no properties + */ + isObjectEmpty: function(data) { + for (var propName in data) { + if (data.hasOwnProperty(propName)) { + return false; + } + } + + return true; + }, + + /** + * Calculates the FNV hash of the specified string. + * + * @param {string} string Input string + * + * @returns {string} FNV hash of the input string + * + */ + hashString: function(string) { + string = encodeURIComponent(string); + var hval = 0x811c9dc5; + + for (var i = 0; i < string.length; i++) { + hval = hval ^ string.charCodeAt(i); + hval += (hval << 1) + (hval << 4) + (hval << 7) + (hval << 8) + (hval << 24); + } + + var hash = (hval >>> 0).toString() + string.length; + + return parseInt(hash).toString(36); + }, + + /** + * Wrapper of isUASameSiteNoneCompatible() that ensures that we pass correct User Agent string + * + * @returns {boolean} True if a browser can safely create SameSite=None cookie + * + * @memberof BOOMR.utils + */ + isCurrentUASameSiteNoneCompatible: function() { + if (w && w.navigator && !w.navigator.userAgentData) { + if (w.navigator.userAgent && typeof w.navigator.userAgent === "string") { + return this.isUASameSiteNoneCompatible(w.navigator.userAgent); + } + } + + return true; + }, + + /** + * @param {string} uaString User agent string + * + * @returns {boolean} True if a browser can safely create SameSite=None cookie + * + * @memberof BOOMR.utils + */ + isUASameSiteNoneCompatible: function(uaString) { + /** + * 1. UCBrowser lower than 12.13.2 + */ + var result = uaString.match(/(UCBrowser)\/(\d+\.\d+)\.(\d+)/); + + if (result) { + var ucMajorMinorPart = parseFloat(result[2]); + var ucPatch = result[3]; + + if (ucMajorMinorPart === 12.13) { + if (ucPatch <= 2) { + return false; + } + + return true; + } + + if (ucMajorMinorPart < 12.13) { + return false; + } + + return true; + } + + /** + * 2. Chrome and Chromium version between 51 and 66 + * + * This the regex covers both because a Chromium AU contains "Chromium/65.0.3325.181 Chrome/65.0.3325.181" + */ + result = uaString.match(/(Chrome)\/(\d+)\.(\d+)\.(\d+)\.(\d+)/); + + if (result) { + var chromeMajor = result[2]; + + if (chromeMajor >= 51 && chromeMajor <= 66) { + return false; + } + + return true; + } + + /** + * 3. Mac OS 10.14.* check + */ + result = uaString.match(/(Macintosh;.*Mac OS X 10_14[_\d]*.*) AppleWebKit\//); + + if (result) { + // 3.2 Safari check + result = uaString.match(/Version\/.* Safari\//); + + if (result) { + // 3.2.1 Not Chrome based check + result = uaString.match(/Chrom(?:e|ium)/); + + if (result === null) { + return false; + } + } + + // 3.3 Mac OS embeded browser + // eslint-disable-next-line max-len + result = uaString.match(/^Mozilla\/\d+(?:\.\d+)* \(Macintosh;.*Mac OS X \d+(?:_\d+)*\) AppleWebKit\/\d+(?:\.\d+)* \(KHTML, like Gecko\)$/); + + if (result) { + return false; + } + + return true; + } + + /** + * 4. iOS and iPad OS 12 for all browsers + */ + result = uaString.match(/(iP.+; CPU .*OS 12(?:_\d+)*.*)/); + + if (result) { + return false; + } + + return true; + }, + + /** + * Given a HTML node, returns a Pseudo-CSS selector. + * + * Algorithm: + * * Starting at the node itself, gather its selector: `elementName#elementId.elementClasses` + * * If the ID or classes are missing, skip them, e.g. just `elementName` or `elementName.elementClasses` + * * Going up the parent tree via the `.parentNode`, stop at the first of: + * * The closest node (or itself) with an ID + * * The BODY node + * * For each node found, prepend the node's selector to the full selector string + * * Limit the number of selectors added to 5 + * * If it takes more than 5 to get to an element with an ID or BODY, collapse to a `*` until they are reached + * + * For example: + * * `

` -> `div span h1` + * * `
` -> `div#test span.first` + * * `
` -> `div#test span.first` + * * `
` -> `div#test span.first.second` + * + * @param {Node} elementNode Node to generate a Pseudo-CSS selector for + * + * @return {string} Pseudo-CSS selector + * + * @memberof BOOMR.utils + */ + makeSelector: function(elementNode) { + var cssSelectors = []; + var node = elementNode; + + // checks to see if a node is valid (element type, not null) + // NOTE: Also sets the funtion-local `node` as the next node to iterate over + function validateAndSetNode(nodeToCheck, isParent) { + var isValid = true; + + // node invalid if null + if (!nodeToCheck) { + isValid = false; + } + + // if node is not valid, see if there's a parent that is + else { + // nodeType 1 == Node.ELEMENT_NODE + while (nodeToCheck && nodeToCheck.nodeType !== 1) { + nodeToCheck = nodeToCheck.parentNode || nodeToCheck.parentElement; + } + + // BODY is also invalid + if (!nodeToCheck || nodeToCheck.tagName === "BODY") { + isValid = false; + } + } + + // + // If we're not just checking to see if a parent exists, + // set node to the nearest valid parent if there is one. + // + // If node itself is valid, it will stay itself + // (only changes if invalid but has a valid parent). + if (!isParent) { + node = nodeToCheck; + } + + return isValid; + } + + while (node) { + // if node isn't null but is the wrong type, will set it to nearest valid parent + var nodeIsValid = validateAndSetNode(node, false); + + if (!nodeIsValid || !node) { + // if node is invalid and no valid parent, stop iterating + break; + } + + // add tagname and ID to selector if ID exists + if (node.hasAttribute("id")) { + var nodeId = node.tagName.toLowerCase() + + "#" + node.getAttribute("id"); + + cssSelectors.unshift(nodeId); + + // selector ends if an ID is found + break; + } + + // check if parent of this node is valid + var parentIsValid = validateAndSetNode(node.parentNode, true); + + // if we don't have three tagnames in the selector yet, + // or we do but this is the last valid one, add class if exists + if (cssSelectors.length < 3 || (cssSelectors.length >= 3 && !parentIsValid)) { + var nodeClass = node.tagName.toLowerCase(); + + if (node.hasAttribute("class")) { + nodeClass += "." + node.getAttribute("class").replace(/ +/g, "."); + } + + cssSelectors.unshift(nodeClass); + } + // if > 3 tagnames in selector and parent is valid, + // add an asterick if there is not one already + else if (cssSelectors[0] !== "*") { + cssSelectors.unshift("*"); + } + + // go to next node in sequence + node = node.parentNode || node.parentElement; + } + + // return CSS selector + return cssSelectors.join(" "); + } + + /* BEGIN_DEBUG */ + /** + * DEBUG ONLY + * + * Loops over an array, running a function for each item + * + * @param {Array} array Array to iterate over + * @param {function} fn Function to execute + * @param {object} thisArg 'this' argument + */ + , forEach: function(array, fn, thisArg) { + if (!BOOMR.utils.isArray(array) || typeof fn !== "function") { + return; + } + + var length = array.length; + + for (var i = 0; i < length; i++) { + if (array.hasOwnProperty(i)) { + fn.call(thisArg, array[i], i, array); + } + } + }, + + /** + * DEBUG ONLY + * + * Logs a UserTiming mark + * + * @param {string} name Mark name (prefixed by boomr:) + */ + mark: function(name) { + var p = BOOMR.getPerformance(); + + if (p && typeof p.mark === "function" && !BOOMR.window.BOOMR_no_mark) { + p.mark("boomr:" + name); + } + }, + + /** + * DEBUG ONLY + * + * Logs a UserTiming measure + * + * @param {string} name Mark name (prefixed by boomr:) + */ + measure: function(measureName, startMarkName, endMarkName) { + var p = BOOMR.getPerformance(); + + if (p && typeof p.measure === "function" && !BOOMR.window.BOOMR_no_mark) { + p.measure("boomr:" + measureName, + startMarkName ? "boomr:" + startMarkName : undefined, + endMarkName ? "boomr:" + endMarkName : undefined); + } + } + /* END_DEBUG */ + + // closes `utils` + }, + + /** + * Browser feature detection flags. + * + * @class BOOMR.browser + */ + browser: { + results: {}, + + /** + * Whether or not the browser supports 'passive' mode for event + * listeners + * + * @returns {boolean} True if the browser supports passive mode + */ + supportsPassive: function() { + if (typeof BOOMR.browser.results.supportsPassive === "undefined") { + BOOMR.browser.results.supportsPassive = false; + + if (!Object.defineProperty) { + return false; + } + + try { + var opts = Object.defineProperty({}, "passive", { + get: function() { + BOOMR.browser.results.supportsPassive = true; + } + }); + + window.addEventListener("test", null, opts); + } + catch (e) { + // NOP + } + } + + return BOOMR.browser.results.supportsPassive; + } + }, + + /** + * Initializes Boomerang by applying the specified configuration. + * + * All plugins' `init()` functions will be called with the same config as well. + * + * @param {object} config Configuration object + * @param {boolean} [config.autorun] By default, boomerang runs automatically + * and attaches its `page_ready` handler to the `window.onload` event. + * If you set `autorun` to `false`, this will not happen and you will + * need to call {@link BOOMR.page_ready} yourself. + * @param {string} config.beacon_auth_key Beacon authorization key value + * @param {string} config.beacon_auth_token Beacon authorization token. + * @param {boolean} config.beacon_with_credentials Sends beacon with credentials + * @param {boolean} config.beacon_disable_sendbeacon Disables `navigator.sendBeacon()` support + * @param {string} config.beacon_url The URL to beacon results back to. + * If not set, no beacon will be sent. + * @param {boolean} config.beacon_url_force_https Forces protocol-relative Beacon URLs to HTTPS + * @param {string} config.beacon_type `GET`, `POST` or `AUTO` + * @param {string} [config.site_domain] The domain that all cookies should be set on + * Boomerang will try to auto-detect this, but unless your site is of the + * `foo.com` format, it will probably get it wrong. It's a good idea + * to set this to whatever part of your domain you'd like to share + * bandwidth and performance measurements across. + * Set this to a falsy value to disable all cookies. + * @param {boolean} [config.strip_query_string] Whether or not to strip query strings from all URLs + * (e.g. `u`, `pgu`, etc.) + * @param {string} [config.user_ip] Despite its name, this is really a free-form + * string used to uniquely identify the user's current internet + * connection. It's used primarily by the bandwidth test to determine + * whether it should re-measure the user's bandwidth or just use the + * value stored in the cookie. You may use IPv4, IPv6 or anything else + * that you think can be used to identify the user's network connection. + * @param {string} [config.same_site_cookie] Used for creating cookies with `SameSite` with one + * of the following values: `None`, `Lax` or `Strict`. + * @param {boolean} [config.secure_cookie] When `true` all cookies will be created with `Secure` flag. + * @param {boolean} [config.request_client_hints] When `true`, gather high entropy values for Architecture, + * Model and Platform data from navigator.userAgentData. + * @param {boolean} [config.no_unload] Disables all unload handlers and the Unload beacons + * @param {function} [config.log] Logger to use. Set to `null` to disable logging. + * @param {function} [] Each plugin has its own section + * + * @returns {BOOMR} Boomerang object + * + * @memberof BOOMR + */ + init: function(config) { + var i, k, + properties = [ + "autorun", + "beacon_auth_key", + "beacon_auth_token", + "beacon_with_credentials", + "beacon_disable_sendbeacon", + "beacon_url", + "beacon_url_force_https", + "beacon_type", + "site_domain", + "strip_query_string", + "user_ip", + "same_site_cookie", + "secure_cookie", + "request_client_hints", + "no_unload" + ]; + + /* BEGIN_DEBUG */ + BOOMR.utils.mark("init:start"); + /* END_DEBUG */ + + BOOMR_check_doc_domain(); + + if (!config) { + config = {}; + } + + // ensure logging is setup properly (or null'd out for production) + if (config.log !== undefined) { + this.log = config.log; + } + + if (!this.log) { + this.log = function() {}; + } + + if (!this.pageId) { + // generate a random page ID for this page's lifetime + this.pageId = BOOMR.utils.generateId(8); + BOOMR.debug("Generated PageID: " + this.pageId); + } + + if (config.primary && impl.handlers_attached) { + return this; + } + + if (typeof config.site_domain !== "undefined") { + if (/:/.test(config.site_domain)) { + // domains with : are not valid, fall back to the current hostname + config.site_domain = w.location.hostname.toLowerCase(); + } + + this.session.domain = config.site_domain; + } + + if (BOOMR.session.enabled && typeof BOOMR.session.ID === "undefined") { + BOOMR.session.ID = BOOMR.utils.generateUUID(); + } + + // Set autorun if in config right now, as plugins that listen for page_ready + // event may fire when they .init() if onload has already fired, and whether + // or not we should fire page_ready depends on config.autorun. + if (typeof config.autorun !== "undefined") { + impl.autorun = config.autorun; + } + + /* BEGIN_DEBUG */ + BOOMR.utils.mark("init:plugins:start"); + /* END_DEBUG */ + + for (k in this.plugins) { + if (this.plugins.hasOwnProperty(k)) { + // config[plugin].enabled has been set to false + if (config[k] && + config[k].hasOwnProperty("enabled") && + config[k].enabled === false) { + impl.disabled_plugins[k] = 1; + + if (typeof this.plugins[k].disable === "function") { + this.plugins[k].disable(); + } + + continue; + } + + // plugin was previously disabled + if (impl.disabled_plugins[k]) { + // and has not been explicitly re-enabled + if (!config[k] || + !config[k].hasOwnProperty("enabled") || + config[k].enabled !== true) { + continue; + } + + if (typeof this.plugins[k].enable === "function") { + this.plugins[k].enable(); + } + + // plugin is now enabled + delete impl.disabled_plugins[k]; + } + + // plugin exists and has an init method + if (typeof this.plugins[k].init === "function") { + try { + /* BEGIN_DEBUG */ + BOOMR.utils.mark("init:plugins:" + k + ":start"); + /* END_DEBUG */ + + this.plugins[k].init(config); + + /* BEGIN_DEBUG */ + BOOMR.utils.mark("init:plugins:" + k + ":end"); + BOOMR.utils.measure( + "init:plugins:" + k, + "init:plugins:" + k + ":start", + "init:plugins:" + k + ":end"); + /* END_DEBUG */ + } + catch (err) { + BOOMR.addError(err, k + ".init"); + } + } + } + } + + /* BEGIN_DEBUG */ + BOOMR.utils.mark("init:plugins:end"); + BOOMR.utils.measure( + "init:plugins", + "init:plugins:start", + "init:plugins:end"); + /* END_DEBUG */ + + for (i = 0; i < properties.length; i++) { + if (config[properties[i]] !== undefined) { + impl[properties[i]] = config[properties[i]]; + } + } + + // if client hints are requested, then squirrel away user agent data of architecture, model, and platform + // version on impl for later use on the beacon + if (impl.request_client_hints === true && + w && w.navigator && w.navigator.userAgentData && + typeof w.navigator.userAgentData.getHighEntropyValues === "function") { + w.navigator.userAgentData.getHighEntropyValues(["architecture", "model", "platformVersion"]).then(function(ua) { + impl.userAgentData = ua; + }); + } + + // if it's the first call to init (handlers aren't attached) and we're not asked to wait OR + // it's the second init call (handlers are attached) and we were previously waiting + // then we set up the page ready autorun functionality + if ((!impl.handlers_attached && !config.wait) || (impl.handlers_attached && impl.waiting_for_config)) { + // The developer can override onload by setting autorun to false + if (!impl.onloadfired && (impl.autorun === undefined || impl.autorun !== false)) { + if (BOOMR.hasBrowserOnloadFired()) { + BOOMR.loadedLate = true; + } + + BOOMR.attach_page_ready(BOOMR.page_ready_autorun); + } + + impl.waiting_for_config = false; + } + + // only attach handlers once + if (impl.handlers_attached) { + /* BEGIN_DEBUG */ + BOOMR.utils.mark("init:end"); + BOOMR.utils.measure( + "init", + "init:start", + "init:end"); + /* END_DEBUG */ + + return this; + } + + if (config.wait) { + impl.waiting_for_config = true; + } + + // + // Page handlers to attach once + // + + // Listen for pageshow/load for the main Page Load + BOOMR.attach_page_ready(function() { + // if we're not using the loader snippet, save the onload time for + // browsers that do not support NavigationTiming. + // This will be later than onload if boomerang arrives late on the + // page but it's the best we can do + if (!BOOMR.t_onload) { + BOOMR.t_onload = BOOMR.now(); + } + }); + + // Listen for DOMContentLoaded to fire the internal dom_loaded event + BOOMR.utils.addListener(w, "DOMContentLoaded", function() { + impl.fireEvent("dom_loaded"); + }); + + // Fire and Listen for config events to refresh config for us and plugins + BOOMR.fireEvent("config", config); + BOOMR.subscribe("config", function(beaconConfig) { + if (beaconConfig.beacon_url) { + impl.beacon_url = beaconConfig.beacon_url; + } + }); + + // Listen for SPA navigations + BOOMR.subscribe("spa_navigation", impl.spaNavigation, null, impl); + + // Listen for Visibility Change notifications + (function() { + var forms, iterator; + + if (visibilityChange !== undefined) { + BOOMR.utils.addListener(d, visibilityChange, function() { + impl.fireEvent("visibility_changed"); + }); + + // save the current visibility state + impl.lastVisibilityState = BOOMR.visibilityState(); + + BOOMR.subscribe("visibility_changed", function() { + var visState = BOOMR.visibilityState(); + + // record the last time each visibility state occurred + BOOMR.lastVisibilityEvent[visState] = BOOMR.now(); + BOOMR.debug("Visibility changed from " + impl.lastVisibilityState + " to " + visState); + + // if we transitioned from prerender to hidden or visible, fire the prerender_to_visible event + if (impl.lastVisibilityState === "prerender" && + visState !== "prerender") { + // note that we transitioned from prerender on the beacon for debugging + BOOMR.addVar("vis.pre", "1"); + + // let all listeners know + impl.fireEvent("prerender_to_visible"); + } + + impl.lastVisibilityState = visState; + }); + } + + // Listen for mouseup events + BOOMR.utils.addListener(d, "mouseup", impl.createCallbackHandler("click")); + + // Listen for FORM submissions + forms = d.getElementsByTagName("form"); + for (iterator = 0; iterator < forms.length; iterator++) { + BOOMR.utils.addListener(forms[iterator], "submit", impl.createCallbackHandler("form_submit")); + } + + // Listen for pagehide + if (!w.onpagehide && w.onpagehide !== null) { + // This must be the last one to fire + // We only clear w on browsers that don't support onpagehide because + // those that do are new enough to not have memory leak problems of + // some older browsers + BOOMR.utils.addListener(w, "unload", function() { + BOOMR.window = w = null; + }); + } + + // if we were loaded in an IFRAME, try to keep track if the IFRAME was unloaded + if (BOOMR.boomerang_frame !== BOOMR.window) { + BOOMR.utils.addListener(BOOMR.boomerang_frame, "unload", impl.onFrameUnloaded); + } + }()); + + impl.handlers_attached = true; + + /* BEGIN_DEBUG */ + BOOMR.utils.mark("init:end"); + BOOMR.utils.measure( + "init", + "init:start", + "init:end"); + /* END_DEBUG */ + + return this; + }, + + /** + * Attach a callback to the `pageshow` or `onload` event if `onload` has not + * been fired otherwise queue it to run immediately + * + * @param {function} cb Callback to run when `onload` fires or page is visible (`pageshow`) + * + * @memberof BOOMR + */ + attach_page_ready: function(cb) { + if (BOOMR.hasBrowserOnloadFired()) { + this.setImmediate(cb, null, null, BOOMR); + } + else { + // Use `pageshow` if available since it will fire even if page came from a back-forward page cache. + // Browsers that support `pageshow` will not fire `onload` if navigation was through a back/forward button + // and the page was retrieved from back-forward cache. + if (w.onpagehide || w.onpagehide === null) { + BOOMR.utils.addListener(w, "pageshow", cb); + } + else { + BOOMR.utils.addListener(w, "load", cb); + } + } + }, + + /** + * Sends the `page_ready` event only if `autorun` is still true after + * {@link BOOMR.init} is called. + * + * @param {Event} ev Event + * + * @memberof BOOMR + */ + page_ready_autorun: function(ev) { + if (impl.autorun) { + BOOMR.page_ready(ev, true); + } + }, + + /** + * Method that fires the {@link BOOMR#event:page_ready} event. Call this + * only if you've set `autorun` to `false` when calling the {@link BOOMR.init} + * method. You should call this method when you determine that your page + * is ready to be used by your user. This will be the end-time used in + * the page load time measurement. Optionally, you can pass a Unix Epoch + * timestamp as a parameter or set the global `BOOMR_page_ready` var that will + * be used as the end-time instead. + * + * @param {Event|number} [ev] Ready event or optional load event end timestamp if called manually + * @param {boolean} auto True if called by `page_ready_autorun` + * + * @returns {BOOMR} Boomerang object + * + * @example + * BOOMR.init({ autorun: false, ... }); + * // wait until the page is ready, i.e. your view has loaded + * BOOMR.page_ready(); + * + * @memberof BOOMR + */ + page_ready: function(ev, auto) { + var tm_page_ready; + + // a number can be passed as the first argument if called manually which + // will be used as the loadEventEnd time + if (!auto && typeof ev === "number") { + tm_page_ready = ev; + ev = null; + } + + if (!ev) { + ev = w.event; + } + + if (!ev) { + ev = { + name: "load" + }; + } + + // if we were called manually or global BOOMR_page_ready was set then + // add loadEventEnd and note this was 'pr' on the beacon + if (!auto) { + ev.timing = ev.timing || {}; + + // use timestamp parameter or global BOOMR_page_ready if set, otherwise use + // the current timestamp + if (tm_page_ready) { + ev.timing.loadEventEnd = tm_page_ready; + } + else if (typeof w.BOOMR_page_ready === "number") { + ev.timing.loadEventEnd = w.BOOMR_page_ready; + } + else { + ev.timing.loadEventEnd = BOOMR.now(); + } + + BOOMR.addVar("pr", 1, true); + } + else if (typeof w.BOOMR_page_ready === "number") { + ev.timing = ev.timing || {}; + // the global BOOMR_page_ready will override our loadEventEnd + ev.timing.loadEventEnd = w.BOOMR_page_ready; + + BOOMR.addVar("pr", 1, true); + } + + if (impl.onloadfired) { + return this; + } + + // if we're prerendering, wait until complete before firing + if (d.prerendering) { + BOOMR.utils.addListener(d, "prerenderingchange", function() { + // wait 500ms for other events (like LCP) to fire before we send the beacon + setTimeout(function() { + impl.fireEvent("page_ready", ev); + }, 500); + + impl.onloadfired = true; + }); + } + else { + // not prerendering + impl.fireEvent("page_ready", ev); + impl.onloadfired = true; + } + + return this; + }, + + /** + * Determines whether or not the page's `onload` event has fired + * + * @returns {boolean} True if page's onload was called + */ + hasBrowserOnloadFired: function() { + var p = BOOMR.getPerformance(); + // if the document is `complete` then the `onload` event has already occurred, we'll fire the callback + // immediately. + // + // When `document.write` is used to replace the contents of the page and inject boomerang, the document + // `readyState` will go from `complete` back to `loading` and then to `complete` again. The second transition + // to `complete` doesn't fire a second `pageshow` event in some browsers (e.g. Safari). We need to check if + // `performance.timing.loadEventStart` or `BOOMR_onload` has occurred to detect this scenario. Will not work for + // older Safari that doesn't have NavTiming + + return ((d.readyState && d.readyState === "complete") || + (p && p.timing && p.timing.loadEventStart > 0) || + w.BOOMR_onload > 0); + }, + + /** + * Determines whether or not the page's `onload` event has fired, or + * if `autorun` is false, whether {@link BOOMR.page_ready} was called. + * + * @returns {boolean} True if `onload` or {@link BOOMR.page_ready} were called + * + * @memberof BOOMR + */ + onloadFired: function() { + return impl.onloadfired; + }, + + /** + * The callback function may return a falsy value to disconnect the observer + * after it returns, or a truthy value to keep watching for mutations. If + * the return value is numeric and greater than 0, then this will be the new timeout. + * If it is boolean instead, then the timeout will not fire any more so + * the caller MUST call disconnect() at some point + * + * @callback BOOMR~setImmediateCallback + * @param {object} data The passed in `data` object + * @param {object} cb_data The passed in `cb_data` object + * @param {Error} callstack An Error object that holds the callstack for + * when `setImmediate` was called, used to determine what called the callback + */ + + /** + * Defer the function `fn` until the next instant the browser is free from + * user tasks. + * + * @param {BOOMR~setImmediateCallback} fn The callback function. + * @param {object} [data] Any data to pass to the callback function + * @param {object} [cb_data] Any passthrough data for the callback function. + * This differs from `data` when `setImmediate` is called via an event + * handler and `data` is the Event object + * @param {object} [cb_scope] The scope of the callback function if it is a method of an object + * + * @returns nothing + * + * @memberof BOOMR + */ + setImmediate: function(fn, data, cb_data, cb_scope) { + var cb, cstack; + + /* BEGIN_DEBUG */ + // DEBUG: This is to help debugging, we'll see where setImmediate calls were made from + if (typeof Error !== "undefined") { + cstack = new Error(); + cstack = cstack.stack ? cstack.stack.replace(/^Error/, "Called") : undefined; + } + /* END_DEBUG */ + + cb = function() { + fn.call(cb_scope || null, data, cb_data || {}, cstack); + cb = null; + }; + + if (w.requestIdleCallback) { + // set a timeout since rIC doesn't get called reliably in chrome headless + w.requestIdleCallback(cb, {timeout: 1000}); + } + else if (w.setImmediate) { + w.setImmediate(cb); + } + else { + setTimeout(cb, 10); + } + }, + + /** + * Gets the current time in milliseconds since the Unix Epoch (Jan 1 1970). + * + * In browsers that support `DOMHighResTimeStamp`, this will be replaced + * by a function that adds `performance.now()` to `navigationStart` + * (with milliseconds.microseconds resolution). + * + * @function + * + * @returns {TimeStamp} Milliseconds since Unix Epoch + * + * @memberof BOOMR + */ + now: (function() { + return Date.now || function() { + return new Date().getTime(); + }; + }()), + + /** + * Gets the `window.performance` object of the root window. + * + * Checks vendor prefixes for older browsers (e.g. IE9). + * + * @returns {Performance|undefined} `window.performance` if it exists + * + * @memberof BOOMR + */ + getPerformance: function() { + try { + if (BOOMR.window) { + if ("performance" in BOOMR.window && BOOMR.window.performance) { + return BOOMR.window.performance; + } + + // vendor-prefixed fallbacks + return BOOMR.window.msPerformance || + BOOMR.window.webkitPerformance || + BOOMR.window.mozPerformance; + } + } + catch (ignore) { + // empty + } + }, + + /** + * Allows us to force SameSite=None from a Boomerang plugin or a third party code. + * + * When this function is called then Boomerang won't honor "same_site_cookie" + * configuration key and won't attempt to return the default value of SameSite=Lax . + * + * @memberof BOOMR + */ + forceSameSiteCookieNone: function() { + impl.forced_same_site_cookie_none = true; + }, + + /** + * Get high resolution delta timestamp from time origin + * + * This function needs to approximate the time since the performance timeOrigin + * or Navigation Timing API's `navigationStart` time. + * If available, `performance.now()` can provide this value. + * If not we either get the navigation start time from the RT plugin or + * from `t_lstart` or `t_start`. Those values are subtracted from the current + * time to derive a time since `navigationStart` value. + * + * @returns {float} Exact or approximate time since the time origin. + */ + hrNow: function() { + var now, navigationStart, + p = BOOMR.getPerformance(); + + if (p && p.now) { + now = p.now(); + } + else { + navigationStart = (BOOMR.plugins.RT && BOOMR.plugins.RT.navigationStart && + BOOMR.plugins.RT.navigationStart()) || BOOMR.t_lstart || BOOMR.t_start; + + // if navigationStart is undefined, we'll be returning NaN + now = BOOMR.now() - navigationStart; + } + + return now; + }, + + /** + * Gets the `document.visibilityState`, or `visible` if Page Visibility + * is not supported. + * + * @function + * + * @returns {string} Visibility state + * + * @memberof BOOMR + */ + visibilityState: (visibilityState === undefined ? function() { + return "visible"; + } : function() { + return d[visibilityState]; + }), + + /** + * An mapping of visibliity event states to the latest time they happened + * + * @type {object} + * + * @memberof BOOMR + */ + lastVisibilityEvent: {}, + + /** + * Registers a Boomerang event. + * + * @param {string} e_name Event name + * + * @returns {BOOMR} Boomerang object + * + * @memberof BOOMR + */ + registerEvent: function(e_name) { + if (impl.events.hasOwnProperty(e_name)) { + // already registered + return this; + } + + // create a new queue of handlers + impl.events[e_name] = []; + + return this; + }, + + /** + * Disables boomerang from doing anything further: + * 1. Clears event handlers (such as onload) + * 2. Clears all event listeners + * + * @memberof BOOMR + */ + disable: function() { + impl.clearEvents(); + impl.clearListeners(); + }, + + /** + * Fires a Boomerang event + * + * @param {string} e_name Event name + * @param {object} data Event payload + * + * @returns {BOOMR} Boomerang object + * + * @memberof BOOMR + */ + fireEvent: function(e_name, data) { + return impl.fireEvent(e_name, data); + }, + + /** + * @callback BOOMR~subscribeCallback + * @param {object} eventData Event data + * @param {object} cb_data Callback data + */ + + /** + * Subscribes to a Boomerang event + * + * @param {string} e_name Event name, i.e. {@link BOOMR#event:page_ready}. + * @param {BOOMR~subscribeCallback} fn Callback function + * @param {object} cb_data Callback data, passed as the second parameter to the callback function + * @param {object} cb_scope Callback scope. If set to an object, then the + * callback function is called as a method of this object, and all + * references to `this` within the callback function will refer to `cb_scope`. + * @param {boolean} once Whether or not this callback should only be run once + * + * @returns {BOOMR} Boomerang object + * + * @memberof BOOMR + */ + subscribe: function(e_name, fn, cb_data, cb_scope, once) { + var i, handler, ev; + + e_name = e_name.toLowerCase(); + + /* BEGIN_DEBUG */ + BOOMR.utils.mark("subscribe"); + BOOMR.utils.mark("subscribe:" + e_name); + /* END_DEBUG */ + + // translate old names + if (impl.translate_events[e_name]) { + e_name = impl.translate_events[e_name]; + } + + if (!impl.events.hasOwnProperty(e_name)) { + // allow subscriptions before they're registered + impl.events[e_name] = []; + } + + ev = impl.events[e_name]; + + // don't allow a handler to be attached more than once to the same event + for (i = 0; i < ev.length; i++) { + handler = ev[i]; + + if (handler && handler.fn === fn && handler.cb_data === cb_data && handler.scope === cb_scope) { + return this; + } + } + + ev.push({ + fn: fn, + cb_data: cb_data || {}, + scope: cb_scope || null, + once: once || false + }); + + // attaching to page_ready after onload fires, so call soon + if (e_name === "page_ready" && impl.onloadfired && impl.autorun) { + this.setImmediate(fn, null, cb_data, cb_scope); + } + + // Note: If no_unload is set, don't listen to any unload-style events. + if (!impl.no_unload && (e_name === "page_unload" || e_name === "before_unload")) { + // Keep track of how many pagehide/unload/beforeunload handlers we're registering + impl.unloadEventsCount++; + + (function() { + var unloadHandler = function boomerangUnloadHandler(evt) { + if (impl.no_unload) { + // may have been set after the initial load + return; + } + + if (fn) { + fn.call(cb_scope, evt || w.event, cb_data); + } + + // clear so this doesn't run twice + fn = null; + + // If this was the last pagehide/unload/beforeunload handler, + // we'll try to send the beacon immediately after it is done. + // The beacon will only be sent if one of the handlers has queued it. + if (++impl.unloadEventCalled === impl.unloadEventsCount) { + BOOMR.real_sendBeacon(); + } + }; + + // + // For modern browsers that support pagehide, listen to that event, + // and do not listen to beforeunload/unload as they can break BFCache navigations. + // + if (w.onpagehide || w.onpagehide === null) { + BOOMR.utils.addListener(w, "pagehide", unloadHandler); + } + else { + // + // For before_unload event in older browsers, attach handlers directly + // to the unload and beforeunload events. Not all older browsers support + // beforeunload. The first of the two to fire will clear so that the + // second doesn't fire. + // + if (e_name === "page_unload") { + BOOMR.utils.addListener(w, "unload", unloadHandler); + } + + BOOMR.utils.addListener(w, "beforeunload", unloadHandler); + } + }()); + } + + return this; + }, + + /** + * Logs an internal Boomerang error. + * + * If the {@link BOOMR.plugins.Errors} plugin is enabled, this data will + * be compressed on the `err` beacon parameter. If not, it will be included + * in uncompressed form on the `errors` parameter. + * + * @param {string|object} err Error + * @param {string} [src] Source + * @param {object} [extra] Extra data + * + * @memberof BOOMR + */ + addError: function BOOMR_addError(err, src, extra) { + var str, + E = BOOMR.plugins.Errors; + + /* BEGIN_DEBUG */ + BOOMR.utils.mark("add_error"); + /* END_DEBUG */ + + BOOMR.error("Boomerang caught error: " + err + ", src: " + src + ", extra: " + extra); + + // + // Use the Errors plugin if it's enabled + // + if (E && E.is_supported()) { + if (typeof err === "string") { + E.send({ + message: err, + extra: extra, + functionName: src, + noStack: true + }, E.VIA_APP, E.SOURCE_BOOMERANG); + } + else { + if (typeof src === "string") { + err.functionName = src; + } + + if (typeof extra !== "undefined") { + err.extra = extra; + } + + E.send(err, E.VIA_APP, E.SOURCE_BOOMERANG); + } + + return; + } + + if (typeof err !== "string") { + str = String(err); + + if (str.match(/^\[object/)) { + str = err.name + ": " + (err.description || err.message).replace(/\r\n$/, ""); + } + + err = str; + } + + if (src !== undefined) { + err = "[" + src + ":" + BOOMR.now() + "] " + err; + } + + if (extra) { + err += ":: " + extra; + } + + if (impl.errors[err]) { + impl.errors[err]++; + } + else { + impl.errors[err] = 1; + } + }, + + /** + * Determines if the specified Error is a Cross-Origin error. + * + * @param {string|object} err Error + * + * @returns {boolean} True if the Error is a Cross-Origin error. + * + * @memberof BOOMR + */ + isCrossOriginError: function(err) { + // These are expected for cross-origin iframe access. + // For IE and Edge, we'll also check the error number for non-English browsers + return err.name === "SecurityError" || + (err.name === "TypeError" && err.message === "Permission denied") || + (err.name === "Error" && err.message && err.message.match(/^(Permission|Access is) denied/)) || + // IE/Edge error number for "Permission Denied" + err.number === -2146828218; + }, + + /** + * Add one or more parameters to the beacon. + * + * This method may either be called with a single object containing + * key/value pairs, or with two parameters, the first is the variable + * name and the second is its value. + * + * All names should be strings usable in a URL's query string. + * + * We recommend only using alphanumeric characters and underscores, but you + * can use anything you like. + * + * Values should be strings (or numbers), and have the same restrictions + * as names. + * + * Parameters will be on all subsequent beacons unless `singleBeacon` is + * set. Early beacons will not clear vars that were set with `singleBeacon`. + * + * @param {string|object} name Variable name + * @param {string|object} [val] Value. If the first parameter is an object, this + * becomes the singleBeacon parameter. + * @param {boolean} [singleBeacon=false] Whether or not to add to a single beacon + * or all beacons. + * + * @returns {BOOMR} Boomerang object + * + * @example + * BOOMR.addVar("page_id", 123); + * BOOMR.addVar({"page_id": 123, "user_id": "Person1"}); + * + * @memberof BOOMR + */ + addVar: function(name, value, singleBeacon) { + /* BEGIN_DEBUG */ + BOOMR.utils.mark("add_var"); + /* END_DEBUG */ + + if (typeof name === "string") { + impl.vars[name] = value; + + if (singleBeacon) { + impl.singleBeaconVars[name] = 1; + } + } + else if (typeof name === "object") { + var o = name, + k; + + for (k in o) { + if (o.hasOwnProperty(k)) { + impl.vars[k] = o[k]; + + // For object-set, the second parameter (or third) can be + // true to force singleBeacon. If so, remove this + // after the first beacon + if (value || singleBeacon) { + impl.singleBeaconVars[k] = 1; + } + } + } + } + + return this; + }, + + /** + * Appends data to a beacon. + * + * If the value already exists, a comma is added and the new data is applied. + * + * @param {string} name Variable name + * @param {string} val Value + * + * @returns {BOOMR} Boomerang object + * + * @memberof BOOMR + */ + appendVar: function(name, value) { + var existing = BOOMR.getVar(name) || ""; + + if (existing) { + existing += ","; + } + + BOOMR.addVar(name, existing + value); + + return this; + }, + + /** + * Removes one or more variables from the beacon URL. This is useful within + * a plugin to reset the values of parameters that it is about to set. + * + * Plugins can also use this in the {@link BOOMR#event:beacon} event to clear + * any variables that should only live on a single beacon. + * + * This method accepts either a list of variable names, or a single + * array containing a list of variable names. + * + * @param {string[]|string} name Variable name or list + * + * @returns {BOOMR} Boomerang object + * + * @memberof BOOMR + */ + removeVar: function(arg0) { + var i, params; + + if (!arguments.length) { + return this; + } + + if (arguments.length === 1 && BOOMR.utils.isArray(arg0)) { + params = arg0; + } + else { + params = arguments; + } + + for (i = 0; i < params.length; i++) { + if (impl.vars.hasOwnProperty(params[i])) { + delete impl.vars[params[i]]; + } + } + + return this; + }, + + /** + * Determines whether or not the beacon has the specified variable. + * + * @param {string} name Variable name + * + * @returns {boolean} True if the variable is set. + * + * @memberof BOOMR + */ + hasVar: function(name) { + return impl.vars.hasOwnProperty(name); + }, + + /** + * Gets the specified variable. + * + * @param {string} name Variable name + * + * @returns {object|undefined} Variable, or undefined if it isn't set + * + * @memberof BOOMR + */ + getVar: function(name) { + return impl.vars[name]; + }, + + /** + * Sets a variable's priority in the beacon URL. + * -1 = beginning of the URL + * 0 = middle of the URL (default) + * 1 = end of the URL + * + * @param {string} name Variable name + * @param {number} pri Priority (-1 or 1) + * + * @returns {BOOMR} Boomerang object + * + * @memberof BOOMR + */ + setVarPriority: function(name, pri) { + if (typeof pri !== "number" || Math.abs(pri) !== 1) { + return this; + } + + impl.varPriority[pri][name] = 1; + + return this; + }, + + /** + * Sets the Referrers variable. + * + * @param {string} r Referrer from the document.referrer + * + * @memberof BOOMR + */ + setReferrer: function(r) { + // document.referrer + impl.r = r; + }, + + /** + * Starts a timer for a dynamic request. + * + * Once the named request has completed, call `loaded()` to send a beacon + * with the duration. + * + * @example + * var timer = BOOMR.requestStart("my-timer"); + * // do stuff + * timer.loaded(); + * + * @param {string} name Timer name + * + * @returns {object} An object with a `.loaded()` function that you can call + * when the dynamic timer is complete. + * + * @memberof BOOMR + */ + requestStart: function(name) { + var t_start = BOOMR.now(); + + BOOMR.plugins.RT.startTimer("xhr_" + name, t_start); + + return { + loaded: function(data) { + BOOMR.responseEnd(name, t_start, data); + } + }; + }, + + /** + * Determines if Boomerang can send a beacon. + * + * Queryies all plugins to see if they implement `readyToSend()`, + * and if so, that they return `true`. + * + * If not, the beacon cannot be sent. + * + * @returns {boolean} True if Boomerang can send a beacon + * + * @memberof BOOMR + */ + readyToSend: function() { + var plugin; + + for (plugin in this.plugins) { + if (this.plugins.hasOwnProperty(plugin)) { + if (impl.disabled_plugins[plugin]) { + continue; + } + + if (typeof this.plugins[plugin].readyToSend === "function" && + this.plugins[plugin].readyToSend() === false) { + BOOMR.debug("Plugin " + plugin + " is not ready to send"); + + return false; + } + } + } + + return true; + }, + + /** + * Sends a beacon for a dynamic request. + * + * @param {string|object} name Timer name or timer object data. + * @param {string} [name.initiator] Initiator, such as `xhr` or `spa` + * @param {string} [name.url] URL of the request + * @param {TimeStamp} t_start Start time + * @param {object} data Request data + * @param {TimeStamp} t_end End time + * + * @memberof BOOMR + */ + responseEnd: function(name, t_start, data, t_end) { + // take the now timestamp for start and end, if unspecified, in case we delay this beacon + t_start = typeof t_start === "number" ? t_start : BOOMR.now(); + t_end = typeof t_end === "number" ? t_end : BOOMR.now(); + + // wait until all plugins are ready to send + if (!BOOMR.readyToSend()) { + BOOMR.debug("Attempted to call responseEnd before all plugins were Ready to Send, trying again..."); + + // try again later + setTimeout(function() { + BOOMR.responseEnd(name, t_start, data, t_end); + }, 1000); + + return; + } + + // Wait until we've sent the Page Load beacon first + if (!BOOMR.hasSentPageLoadBeacon() && + !BOOMR.utils.inArray(name.initiator, BOOMR.constants.BEACON_TYPE_SPAS)) { + // wait for a beacon, then try again + BOOMR.subscribe("page_load_beacon", function() { + BOOMR.responseEnd(name, t_start, data, t_end); + }, null, BOOMR, true); + + return; + } + + // Ensure we don't have two beacons trying to send data at the same time + if (impl.beaconInQueue) { + // wait until the beacon is sent, then try again + BOOMR.subscribe("beacon", function() { + BOOMR.responseEnd(name, t_start, data, t_end); + }, null, BOOMR, true); + + return; + } + + // Lock the beacon queue + impl.beaconInQueue = true; + + if (typeof name === "object") { + if (!name.url) { + BOOMR.debug("BOOMR.responseEnd: First argument must have a url property if it's an object"); + + return; + } + + impl.fireEvent("xhr_load", name); + } + else { + // flush out any queue'd beacons before we set the Page Group + // and timers + BOOMR.real_sendBeacon(); + + BOOMR.addVar("xhr.pg", name, true); + + BOOMR.plugins.RT.startTimer("xhr_" + name, t_start); + + impl.fireEvent("xhr_load", { + name: "xhr_" + name, + data: data, + timing: { + loadEventEnd: t_end + } + }); + } + }, + + // + // uninstrumentXHR, instrumentXHR, uninstrumentFetch and instrumentFetch + // are stubs that will be replaced by auto-xhr.js if active. + // + /** + * Undo XMLHttpRequest instrumentation and reset the original `XMLHttpRequest` + * object + * + * This is implemented in `plugins/auto-xhr.js` {@link BOOMR.plugins.AutoXHR}. + * + * @memberof BOOMR + */ + uninstrumentXHR: function() { }, + + /** + * Instrument all requests made via XMLHttpRequest to send beacons. + * + * This is implemented in `plugins/auto-xhr.js` {@link BOOMR.plugins.AutoXHR}. + * + * @memberof BOOMR + */ + instrumentXHR: function() { }, + + /** + * Undo fetch instrumentation and reset the original `fetch` + * function + * + * This is implemented in `plugins/auto-xhr.js` {@link BOOMR.plugins.AutoXHR}. + * + * @memberof BOOMR + */ + uninstrumentFetch: function() { }, + + /** + * Instrument all requests made via fetch to send beacons. + * + * This is implemented in `plugins/auto-xhr.js` {@link BOOMR.plugins.AutoXHR}. + * + * @memberof BOOMR + */ + instrumentFetch: function() { }, + + /** + * Request boomerang to send its beacon with all queued beacon data + * (via {@link BOOMR.addVar}). + * + * Boomerang may ignore this request. + * + * When this method is called, boomerang checks all plugins. If any + * plugin has not completed its checks (ie, the plugin's `is_complete()` + * method returns `false`, then this method does nothing. + * + * If all plugins have completed, then this method fires the + * {@link BOOMR#event:before_beacon} event with all variables that will be + * sent on the beacon. + * + * After all {@link BOOMR#event:before_beacon} handlers return, this method + * checks if a `beacon_url` has been configured and if there are any + * beacon parameters to be sent. If both are true, it fires the beacon. + * + * The {@link BOOMR#event:beacon} event is then fired. + * + * `sendBeacon()` should be called any time a plugin goes from + * `is_complete() = false` to `is_complete = true` so the beacon is + * sent. + * + * The actual beaconing is handled in {@link BOOMR.real_sendBeacon} after + * a short delay (via {@link BOOMR.setImmediate}). If other calls to + * `sendBeacon` happen before {@link BOOMR.real_sendBeacon} is called, + * those calls will be discarded (so it's OK to call this in quick + * succession). + * + * @param {string} [beacon_url_override] Beacon URL override + * + * @memberof BOOMR + */ + sendBeacon: function(beacon_url_override) { + // This plugin wants the beacon to go somewhere else, + // so update the location + if (beacon_url_override) { + impl.beacon_url_override = beacon_url_override; + } + + if (!impl.beaconQueued) { + impl.beaconQueued = true; + BOOMR.setImmediate(BOOMR.real_sendBeacon, null, null, BOOMR); + } + + return true; + }, + + /** + * Sends a beacon when the beacon queue is empty. + * + * @param {object} params Beacon parameters to set + * @param {function} callback Callback to run when the queue is ready + * @param {object} that Function to apply callback to + */ + sendBeaconWhenReady: function(params, callback, that) { + // If we're already sending a beacon, wait until the queue is empty + if (impl.beaconInQueue) { + // wait until the beacon is sent, then try again + BOOMR.subscribe("beacon", function() { + BOOMR.sendBeaconWhenReady(params, callback, that); + }, null, BOOMR, true); + + return; + } + + // Lock the beacon queue + impl.beaconInQueue = true; + + // add all parameters + for (var paramName in params) { + if (params.hasOwnProperty(paramName)) { + // add this data to a single beacon + BOOMR.addVar(paramName, params[paramName], true); + } + } + + // run the callback + if (typeof callback === "function" && typeof that !== "undefined") { + callback.apply(that); + } + + // send the beacon + BOOMR.sendBeacon(); + }, + + /** + * Sends all beacon data. + * + * This function should be called directly any time a "new" beacon is about + * to be constructed. For example, if you're creating a new XHR or other + * custom beacon, you should ensure the existing beacon data is flushed + * by calling `BOOMR.real_sendBeacon();` first. + * + * @memberof BOOMR + */ + real_sendBeacon: function() { + var k, form, url, + errors = [], + params = [], + paramsJoined, + varsSent = {}; + + if (!impl.beaconQueued) { + return false; + } + + /* BEGIN_DEBUG */ + BOOMR.utils.mark("send_beacon:start"); + /* END_DEBUG */ + + impl.beaconQueued = false; + + BOOMR.debug("Checking if we can send beacon"); + + // At this point someone is ready to send the beacon. We send + // the beacon only if all plugins have finished doing what they + // wanted to do + for (k in this.plugins) { + if (this.plugins.hasOwnProperty(k)) { + if (impl.disabled_plugins[k]) { + continue; + } + + if (!this.plugins[k].is_complete(impl.vars)) { + BOOMR.debug("Plugin " + k + " is not complete, deferring beacon send"); + // if an Early beacon is blocked, then we'll cancel it. + // By removing the `early` param, the beacon params will be merged + // with the following load beacon. + delete impl.vars.early; + + return false; + } + } + } + + // Sanity test that the browser is still available (and not shutting down) + if (!window || !window.Image || !window.navigator || !BOOMR.window) { + BOOMR.debug("DOM not fully available, not sending a beacon"); + + return false; + } + + // For SPA apps, don't strip hashtags as some SPA frameworks use #s for tracking routes + // instead of History pushState() APIs. Use d.URL instead of location.href because of a + // Safari bug. + var isSPA = BOOMR.utils.inArray(impl.vars["http.initiator"], BOOMR.constants.BEACON_TYPE_SPAS); + var isPageLoad = typeof impl.vars["http.initiator"] === "undefined" || isSPA; + + if (!impl.vars.pgu) { + impl.vars.pgu = isSPA ? d.URL : d.URL.replace(/#.*/, ""); + } + + impl.vars.pgu = BOOMR.utils.cleanupURL(impl.vars.pgu); + + // Use the current document.URL if it hasn't already been set, or for SPA apps, + // on each new beacon (since each SPA soft navigation might change the URL) + if (!impl.vars.u || isSPA) { + impl.vars.u = impl.vars.pgu; + } + + if (impl.vars.pgu === impl.vars.u) { + delete impl.vars.pgu; + } + + // Add cleaned-up referrer URLs to the beacon, if available + if (impl.r) { + impl.vars.r = BOOMR.utils.cleanupURL(impl.r); + } + else { + delete impl.vars.r; + } + + impl.vars.v = BOOMR.version; + + // Snippet version, if available + if (BOOMR.snippetVersion) { + impl.vars.sv = BOOMR.snippetVersion; + } + + // Snippet method is IFRAME if not specified (pre-v12 snippets) + impl.vars.sm = BOOMR.snippetMethod || "i"; + + if (BOOMR.session.enabled) { + impl.vars["rt.si"] = BOOMR.session.ID + "-" + Math.round(BOOMR.session.start / 1000).toString(36); + impl.vars["rt.ss"] = BOOMR.session.start; + + if (typeof impl.vars.early === "undefined") { + // make sure Session Length is always at least 1 for non-Early beacons + impl.vars["rt.sl"] = BOOMR.session.length >= 1 ? BOOMR.session.length : 1; + } + else { + impl.vars["rt.sl"] = BOOMR.session.length; + } + } + else { + BOOMR.removeVar("rt.si", "rt.ss", "rt.sl"); + } + + if (BOOMR.visibilityState()) { + impl.vars["vis.st"] = BOOMR.visibilityState(); + + if (BOOMR.lastVisibilityEvent.visible) { + impl.vars["vis.lv"] = BOOMR.now() - BOOMR.lastVisibilityEvent.visible; + } + + if (BOOMR.lastVisibilityEvent.hidden) { + impl.vars["vis.lh"] = BOOMR.now() - BOOMR.lastVisibilityEvent.hidden; + } + } + + var platform = ""; + + if (navigator.userAgentData && typeof navigator.userAgentData.platform === "string") { + platform = navigator.userAgentData.platform; + } + else { + platform = navigator.platform; + } + + impl.vars["ua.plt"] = platform; + impl.vars["ua.vnd"] = navigator.vendor; + + // if userAgentData exists, then store on the beacon + if (impl.userAgentData) { + impl.vars["ua.arch"] = impl.userAgentData.architecture; + impl.vars["ua.model"] = impl.userAgentData.model; + impl.vars["ua.pltv"] = impl.userAgentData.platformVersion; + } + + if (this.pageId) { + impl.vars.pid = this.pageId; + } + + // add beacon number + impl.vars.n = ++this.beaconsSent; + + if (w !== window) { + impl.vars["if"] = ""; + } + + for (k in impl.errors) { + if (impl.errors.hasOwnProperty(k)) { + errors.push(k + (impl.errors[k] > 1 ? " (*" + impl.errors[k] + ")" : "")); + } + } + + if (errors.length > 0) { + impl.vars.errors = errors.join("\n"); + } + + impl.errors = {}; + + // If we reach here, all plugins have completed + impl.fireEvent("before_beacon", impl.vars); + + // clone the vars object for two reasons: first, so all listeners of + // 'beacon' get an exact clone (in case listeners are doing + // BOOMR.removeVar), and second, to help build our priority list of vars. + for (k in impl.vars) { + if (impl.vars.hasOwnProperty(k)) { + varsSent[k] = impl.vars[k]; + } + } + + BOOMR.removeVar(["qt", "pgu"]); + + if (typeof impl.vars.early === "undefined") { + // remove any vars that should only be on a single beacon. + // Early beacons don't clear vars even if flagged as `singleBeacon` so + // that they can be re-sent on the next normal beacon + for (var singleVarName in impl.singleBeaconVars) { + if (impl.singleBeaconVars.hasOwnProperty(singleVarName)) { + BOOMR.removeVar(singleVarName); + } + } + + // clear single beacon vars list + impl.singleBeaconVars = {}; + + // keep track of page load beacons + if (!impl.hasSentPageLoadBeacon && isPageLoad) { + impl.hasSentPageLoadBeacon = true; + + // let this beacon go out first + BOOMR.setImmediate(function() { + impl.fireEvent("page_load_beacon", varsSent); + }); + } + } + + // Stop at this point if we are rate limited + if (BOOMR.session.rate_limited) { + BOOMR.debug("Skipping because we're rate limited"); + + return false; + } + + // mark that we're no longer sending a beacon now, as those + // paying attention to this will trigger at the beacon event + impl.beaconInQueue = false; + + // send the beacon data + BOOMR.sendBeaconData(varsSent); + + /* BEGIN_DEBUG */ + BOOMR.utils.mark("send_beacon:end"); + BOOMR.utils.measure( + "send_beacon", + "send_beacon:start", + "send_beacon:end"); + /* END_DEBUG */ + + return true; + }, + + /** + * Sends beacon data via the Beacon API, XHR or Image + * + * @param {object} data Data + */ + sendBeaconData: function(data) { + var urlFirst = [], + urlLast = [], + params, paramsJoined, + url, img, + useImg = true, + xhr, ret; + + BOOMR.debug("Ready to send beacon: " + BOOMR.utils.objectToString(data)); + + // Use the override URL if given + impl.beacon_url = impl.beacon_url_override || impl.beacon_url; + + // Check that the beacon_url was set first + if (!impl.beacon_url) { + BOOMR.debug("No beacon URL, so skipping."); + + return false; + } + + if (!impl.beaconUrlAllowed(impl.beacon_url)) { + BOOMR.debug("Beacon URL not allowed: " + impl.beacon_url); + + return false; + } + + // Check that we have data to send + if (BOOMR.utils.isObjectEmpty(data)) { + return false; + } + + // If we reach here, we've figured out all of the beacon data we'll send. + impl.fireEvent("beacon", data); + + // get high- and low-priority variables first, which remove any of + // those vars from data + urlFirst = this.getVarsOfPriority(data, -1); + urlLast = this.getVarsOfPriority(data, 1); + + // merge the 3 lists + params = urlFirst.concat(this.getVarsOfPriority(data, 0), urlLast); + paramsJoined = params.join("&"); + + // If beacon_url is protocol relative, make it https only + if (impl.beacon_url_force_https && impl.beacon_url.match(/^\/\//)) { + impl.beacon_url = "https:" + impl.beacon_url; + } + + // if there are already url parameters in the beacon url, + // change the first parameter prefix for the boomerang url parameters to & + url = impl.beacon_url + ((impl.beacon_url.indexOf("?") > -1) ? "&" : "?") + paramsJoined; + + // + // Try to send an IMG beacon if possible (which is the most compatible), + // otherwise send an XHR beacon if the URL length is longer than 2,000 bytes. + // + if (impl.beacon_type === "GET") { + useImg = true; + + if (url.length > BOOMR.constants.MAX_GET_LENGTH) { + ((window.console && (console.warn || console.log)) || function() {})( + "Boomerang: Warning: Beacon may not be sent via GET due to payload size > 2000 bytes" + ); + } + } + else if (impl.beacon_type === "POST" || url.length > BOOMR.constants.MAX_GET_LENGTH) { + // switch to a XHR beacon if the the user has specified a POST OR GET length is too long + useImg = false; + } + + // + // Try the sendBeacon API first. + // But if beacon_type is set to "GET", dont attempt + // sendBeacon API call + // + if (w && w.navigator && + typeof w.navigator.sendBeacon === "function" && + BOOMR.utils.isNative(w.navigator.sendBeacon) && + typeof w.Blob === "function" && + impl.beacon_type !== "GET" && + // As per W3C, The sendBeacon method does not provide ability to pass any + // header other than 'Content-Type'. So if we need to send data with + // 'Authorization' header, we need to fallback to good old xhr. + typeof impl.beacon_auth_token === "undefined" && + !impl.beacon_disable_sendbeacon) { + // note we're using sendBeacon with &sb=1 + var blobData = new w.Blob([paramsJoined + "&sb=1"], { + type: "application/x-www-form-urlencoded" + }); + + if (w.navigator.sendBeacon(impl.beacon_url, blobData)) { + return true; + } + + // sendBeacon was not successful, try Image or XHR beacons + } + + // If we don't have XHR available, force an image beacon and hope + // for the best + if (!BOOMR.orig_XMLHttpRequest && (!w || !w.XMLHttpRequest)) { + useImg = true; + } + + if (useImg) { + // + // Image beacon + // + + // just in case Image isn't a valid constructor + try { + img = new Image(); + } + catch (e) { + BOOMR.debug("Image is not a constructor, not sending a beacon"); + + return false; + } + + img.src = url; + } + else { + // + // XHR beacon + // + + // Send a form-encoded XHR POST beacon + xhr = new (BOOMR.window.orig_XMLHttpRequest || BOOMR.orig_XMLHttpRequest || BOOMR.window.XMLHttpRequest)(); + try { + this.sendXhrPostBeacon(xhr, paramsJoined); + } + catch (e) { + // if we had an exception with the window XHR object, try our IFRAME XHR + xhr = new BOOMR.boomerang_frame.XMLHttpRequest(); + this.sendXhrPostBeacon(xhr, paramsJoined); + } + } + + return true; + }, + + /** + * Determines whether or not a Page Load beacon has been sent. + * + * @returns {boolean} True if a Page Load beacon has been sent. + * + * @memberof BOOMR + */ + hasSentPageLoadBeacon: function() { + return impl.hasSentPageLoadBeacon; + }, + + /** + * Sends a beacon via XMLHttpRequest + * + * @param {object} xhr XMLHttpRequest object + * @param {object} [paramsJoined] XMLHttpRequest.send() argument + * + * @memberof BOOMR + */ + sendXhrPostBeacon: function(xhr, paramsJoined) { + xhr.open("POST", impl.beacon_url); + + xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); + + if (typeof impl.beacon_auth_token !== "undefined") { + if (typeof impl.beacon_auth_key === "undefined") { + impl.beacon_auth_key = "Authorization"; + } + + xhr.setRequestHeader(impl.beacon_auth_key, impl.beacon_auth_token); + } + + if (impl.beacon_with_credentials) { + xhr.withCredentials = true; + } + + xhr.send(paramsJoined); + }, + + /** + * Gets all variables of the specified priority + * + * @param {object} vars Variables (will be modified for pri -1 and 1) + * @param {number} pri Priority (-1, 0, or 1) + * + * @return {string[]} Array of URI-encoded vars + * + * @memberof BOOMR + */ + getVarsOfPriority: function(vars, pri) { + var name, + url = [], + // if we were given a priority, iterate over that list + // else iterate over vars + iterVars = (pri !== 0 ? impl.varPriority[pri] : vars); + + for (name in iterVars) { + // if this var is set, add it to our URL array + if (iterVars.hasOwnProperty(name) && vars.hasOwnProperty(name)) { + url.push(this.getUriEncodedVar(name, typeof vars[name] === "undefined" ? "" : vars[name])); + + // remove this name from vars so it isn't also added + // to the non-prioritized list when pri=0 is called + if (pri !== 0) { + delete vars[name]; + } + } + } + + return url; + }, + + /** + * Gets a URI-encoded name/value pair. + * + * @param {string} name Name + * @param {string} value Value + * + * @returns {string} URI-encoded string + * + * @memberof BOOMR + */ + getUriEncodedVar: function(name, value) { + if (value === undefined || value === null) { + value = ""; + } + + if (typeof value === "object") { + value = BOOMR.utils.serializeForUrl(value); + } + + var result = encodeURIComponent(name) + + "=" + encodeURIComponent(value); + + return result; + }, + + /** + * Gets the latest ResourceTiming entry for the specified URL. + * + * Default sort order is chronological startTime. + * + * @param {string} url Resource URL + * @param {function} [sort] Sort the entries before returning the last one + * @param {function} [filter] Filter the entries. Will be applied before sorting + * + * @returns {PerformanceEntry|undefined} Entry, or undefined if ResourceTiming is not + * supported or if the entry doesn't exist + * + * @memberof BOOMR + */ + getResourceTiming: function(url, sort, filter) { + var entries, + p = BOOMR.getPerformance(); + + try { + if (p && typeof p.getEntriesByName === "function") { + entries = p.getEntriesByName(url); + + if (!entries || !entries.length) { + return; + } + + if (typeof filter === "function") { + entries = BOOMR.utils.arrayFilter(entries, filter); + + if (!entries || !entries.length) { + return; + } + } + + if (entries.length > 1 && typeof sort === "function") { + entries.sort(sort); + } + + return entries[entries.length - 1]; + } + } + catch (e) { + BOOMR.warn("getResourceTiming:" + e); + } + }, + + /** + * Determines whether beacon data is for a Page Load beacon, or not. + * + * Page Load beacons include regular Page Loads, SPA Hard or SPA Soft beacons. + * + * We also consider an Aborted Load beacon a Page Load. + * + * @param {object} data Beacon Data + * @returns {boolean} True if beacon data is for a Page Load beacon + */ + isPageLoadBeacon: function(data) { + return (typeof data["rt.quit"] === "undefined" || typeof data["rt.abld"] !== "undefined") && + (typeof data["http.initiator"] === "undefined" || + BOOMR.utils.inArray(data["http.initiator"], BOOMR.constants.BEACON_TYPE_SPAS)); + }, + + /** + * Returns the timestamp offset by the Prerendered value, if it happened + * + * @param {number} ts Timestamp + * + * @returns {number} Offset timestamp if Prerendered, original timestamp if not + */ + getPrerenderedOffset: function(ts) { + var actSt = BOOMR.getActivationStart(); + + ts = Math.floor(ts); + + if (actSt === false) { + // Prerender not supported or did not happen, return original timestamp + return ts; + } + else if (actSt !== null) { + // integer offset, return the difference + var newTs = ts - actSt; + + // return the offset (at least 1ms) + return newTs >= 0 ? Math.max(1, newTs) : ts; + } + }, + + /** + * Gets the Activation Start time for Prerendered navigations. + * + * @returns {false|number} false if Prerender isn't supported, false if there was no Prerender, or a timestamp + * of the Activation if there was one + */ + getActivationStart: function() { + if (impl.prerenderedOffset !== null) { + // we've previously calculated, return the value + return impl.prerenderedOffset; + } + + // we're going to now check/set it, default to false (not supported or didn't happen) + impl.prerenderedOffset = false; + + // check if prerendering is supported first + if (typeof d.prerendering !== "boolean") { + // not supported, return false + return impl.prerenderedOffset; + } + + // check if there was an activation via NavigationTiming + var p = BOOMR.getPerformance(); + + if (p && typeof p.getEntriesByType === "function") { + // get the navigation entry + var navEntry = p.getEntriesByType("navigation")[0]; + + if (navEntry && navEntry.activationStart) { + impl.prerenderedOffset = Math.floor(navEntry.activationStart); + } + } + + return impl.prerenderedOffset; + } + + /* BEGIN_DEBUG */, + /** + * Sets the list of allowed Beacon URLs + * + * @param {string[]} urls List of string regular expressions + */ + setBeaconUrlsAllowed: function(urls) { + impl.beacon_urls_allowed = urls; + } + /* END_DEBUG */ + }; + + // if not already set already on BOOMR, determine the URL + if (!BOOMR.url) { + boomr.url = boomr.utils.getMyURL(); + } + else { + // canonicalize the URL + var a = BOOMR.window.document.createElement("a"); + + a.href = BOOMR.url; + boomr.url = a.href; + } + + delete BOOMR_start; + + /** + * @global + * @type {TimeStamp} + * @name BOOMR_lstart + * @desc + * This variable is added to the global scope (`window`) until Boomerang loads, + * at which point it is removed. + * + * Time the loader script started fetching boomerang.js (if the asynchronous + * loader snippet is used). + */ + if (typeof BOOMR_lstart === "number") { + /** + * Time the loader script started fetching boomerang.js (if using the + * asynchronous loader snippet) (`BOOMR_lstart`) + * @type {TimeStamp} + * + * @memberof BOOMR + */ + boomr.t_lstart = BOOMR_lstart; + delete BOOMR_lstart; + } + else if (typeof BOOMR.window.BOOMR_lstart === "number") { + boomr.t_lstart = BOOMR.window.BOOMR_lstart; + } + + /** + * This variable is added to the global scope (`window`). + * + * Time the `window.onload` event fired (if using the asynchronous loader snippet). + * + * This timestamp is logged in the case boomerang.js loads after the onload event + * for browsers that don't support NavigationTiming. + * + * @global + * @name BOOMR_onload + * @type {TimeStamp} + */ + if (typeof BOOMR.window.BOOMR_onload === "number") { + /** + * Time the `window.onload` event fired (if using the asynchronous loader snippet). + * + * This timestamp is logged in the case boomerang.js loads after the onload event + * for browsers that don't support NavigationTiming. + * + * @type {TimeStamp} + * @memberof BOOMR + */ + boomr.t_onload = BOOMR.window.BOOMR_onload; + } + + (function() { + var make_logger; + + if (typeof console === "object" && console.log !== undefined) { + /** + * Logs the message to the console + * + * @param {string} m Message + * @param {string} l Log level + * @param {string} [s] Source + * + * @function log + * + * @memberof BOOMR + */ + boomr.log = function(m, l, s) { + console.log("(" + BOOMR.now() + ") " + + "{" + BOOMR.pageId + "}" + + ": " + s + + ": [" + l + "] " + + m); + }; + } + else { + // NOP for browsers that don't support it + boomr.log = function() {}; + } + + make_logger = function(l) { + return function(m, s) { + this.log(m, l, "boomerang" + (s ? "." + s : "")); + + return this; + }; + }; + + /** + * Logs debug messages to the console + * + * Debug messages are stripped out of production builds. + * + * @param {string} m Message + * @param {string} [s] Source + * + * @function debug + * + * @memberof BOOMR + */ + boomr.debug = make_logger("debug"); + + /** + * Logs info messages to the console + * + * @param {string} m Message + * @param {string} [s] Source + * + * @function info + * + * @memberof BOOMR + */ + boomr.info = make_logger("info"); + + /** + * Logs warning messages to the console + * + * @param {string} m Message + * @param {string} [s] Source + * + * @function warn + * + * @memberof BOOMR + */ + boomr.warn = make_logger("warn"); + + /** + * Logs error messages to the console + * + * @param {string} m Message + * @param {string} [s] Source + * + * @function error + * + * @memberof BOOMR + */ + boomr.error = make_logger("error"); + }()); + + // If the browser supports performance.now(), swap that in for BOOMR.now + try { + var p = boomr.getPerformance(); + + if (p && + typeof p.now === "function" && + // #545 handle bogus performance.now from broken shims + /\[native code\]/.test(String(p.now)) && + p.timing && + p.timing.navigationStart) { + boomr.now = function() { + return Math.round(p.now() + p.timing.navigationStart); + }; + } + } + catch (ignore) { + // empty + } + + impl.checkLocalStorageSupport(); + + (function() { + var ident; + + for (ident in boomr) { + if (boomr.hasOwnProperty(ident)) { + BOOMR[ident] = boomr[ident]; + } + } + + if (!BOOMR.xhr_excludes) { + /** + * URLs to exclude from automatic `XMLHttpRequest` instrumentation. + * + * You can put any of the following in it: + * * A full URL + * * A hostname + * * A path + * + * @example + * BOOMR = window.BOOMR || {}; + * BOOMR.xhr_excludes = { + * "mysite.com": true, + * "/dashboard/": true, + * "https://mysite.com/dashboard/": true + * }; + * + * @memberof BOOMR + */ + BOOMR.xhr_excludes = {}; + } + }()); + + /* BEGIN_DEBUG */ + /* + * This block reports on overridden functions on `window` and properties on `document` using `BOOMR.warn()`. + * To enable, add `overridden` with a value of `true` to the query string. + */ + (function() { + /** + * Checks a window for overridden functions. + * + * @param {Window} win The window object under test + * + * @returns {Array} Array of overridden function names + */ + BOOMR.checkWindowOverrides = function(win) { + if (!Object.getOwnPropertyNames) { + return []; + } + + var freshWindow, objects, + overridden = []; + + function setup() { + var iframe = d.createElement("iframe"); + + iframe.style.display = "none"; + iframe.src = "javascript:false"; // eslint-disable-line no-script-url + d.getElementsByTagName("script")[0].parentNode.appendChild(iframe); + freshWindow = iframe.contentWindow; + objects = Object.getOwnPropertyNames(freshWindow); + } + + function teardown() { + iframe.parentNode.removeChild(iframe); + } + + function checkWindowObject(objectKey) { + if (isNonNative(objectKey)) { + overridden.push(objectKey); + } + } + + function isNonNative(key) { + var split = key.split("."), + fn = win, + results = []; + + while (fn && split.length) { + try { + fn = fn[split.shift()]; + } + catch (e) { + return false; + } + } + + return typeof fn === "function" && !isNativeFunction(fn, key); + } + + function isNativeFunction(fn, str) { + if (str === "console.assert" || + str === "Function.prototype" || + str.indexOf("onload") >= 0 || + str.indexOf("onbeforeunload") >= 0 || + str.indexOf("onerror") >= 0 || + str.indexOf("onload") >= 0 || + str.indexOf("NodeFilter") >= 0) { + return true; + } + + return fn.toString && + !fn.hasOwnProperty("toString") && + /\[native code\]/.test(String(fn)); + } + + setup(); + for (var objectIndex = 0; objectIndex < objects.length; objectIndex++) { + var objectKey = objects[objectIndex]; + + if (objectKey === "window" || + objectKey === "self" || + objectKey === "top" || + objectKey === "parent" || + objectKey === "frames") { + continue; + } + + if (freshWindow[objectKey] && + (typeof freshWindow[objectKey] === "object" || typeof freshWindow[objectKey] === "function")) { + checkWindowObject(objectKey); + + var propertyNames = []; + + try { + propertyNames = Object.getOwnPropertyNames(freshWindow[objectKey]); + } + catch (e) { + ; + } + + for (var i = 0; i < propertyNames.length; i++) { + checkWindowObject([objectKey, propertyNames[i]].join(".")); + } + + if (freshWindow[objectKey].prototype) { + propertyNames = Object.getOwnPropertyNames(freshWindow[objectKey].prototype); + for (var i = 0; i < propertyNames.length; i++) { + checkWindowObject([objectKey, "prototype", propertyNames[i]].join(".")); + } + } + } + } + + return overridden; + }; + + /** + * Checks a document for overridden properties. + * + * @param {HTMLDocument} doc The document object under test + * + * @returns {Array} Array of overridden properties names + */ + BOOMR.checkDocumentOverrides = function(doc) { + return BOOMR.utils.arrayFilter(["readyState", "domain", "hidden", "URL", "cookie"], function(key) { + return doc.hasOwnProperty(key); + }); + }; + + if (BOOMR.utils.getQueryParamValue("overridden") === "true" && w && w.Object && Object.getOwnPropertyNames) { + var overridden = [] + .concat(BOOMR.checkWindowOverrides(w)) + .concat(BOOMR.checkDocumentOverrides(d)); + + if (overridden.length > 0) { + BOOMR.warn("overridden: " + overridden.sort()); + } + } + })(); + /* END_DEBUG */ + + dispatchEvent("onBoomerangLoaded", { "BOOMR": BOOMR }, true); }(window)); -// End of BW plugin - - -/*jslint white: false, devel: true, onevar: true, browser: true, undef: true, nomen: true, regexp: false, continue: true, plusplus: false, bitwise: false, newcap: true, maxerr: 50, indent: 4 */ +// end of boomerang beaconing section diff --git a/bower.json b/bower.json new file mode 100644 index 000000000..69dbdcd2e --- /dev/null +++ b/bower.json @@ -0,0 +1,57 @@ +{ + "name": "boomerang", + "private": true, + "main": "boomerang.js", + "version": "1.687.0", + "homepage": "https://github.com/akamai/boomerang/", + "authors": [ + "Philip Tellis" + ], + "description": "End user oriented web performance testing and beaconing", + "keywords": [ + "boomerang", + "performance", + "NavigationTiming", + "ResourceTiming", + "UserTiming", + "PerformanceTimeline" + ], + "license": "BSD", + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests" + ], + "devDependencies": { + "angular-resource": "~1.3.16", + "angular-resource-1.4": "angular-resource#1.4.6", + "angular-resource-1.5": "angular-resource#1.5.0-beta.0", + "angular-route": "~1.3.16", + "angular-route-1.4": "angular-route#1.4.6", + "angular-route-1.5": "angular-route#1.5.0-beta.0", + "angular-ui-router": "~0.2.15", + "angular": "~1.3.16", + "angular-1.4": "angular#1.4.6", + "angular-1.5": "angular#1.5.0-beta.0", + "assertive-chai": "~2.0.0", + "backbone": "~1.3.3", + "ember": "~1.12.1", + "fetch": "^2.0.4", + "handlebars": "~3.0.3", + "jquery": "~2.1.4", + "json3": "~3.3.2", + "lodash": "~3.0.0", + "mocha": "~3.4.2", + "promise-polyfill": "^7.1.2", + "resourcetiming-compression": "^1.3.2", + "usertiming": "~0.1.8", + "usertiming-compression": "^0.1.8", + "zone": "^0.8.26", + "cookieconsent": "~3.1.1" + }, + "resolutions": { + "angular": "1.4.6" + } +} diff --git a/dns.js b/dns.js deleted file mode 100644 index dd29b66be..000000000 --- a/dns.js +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright (c) 2011, Yahoo! Inc. All rights reserved. - * Copyrights licensed under the BSD License. See the accompanying LICENSE.txt file for terms. - */ - -/** -\file dns.js -Plugin to measure DNS latency. -This code is based on Carlos Bueno's guide to DNS on the YDN blog: -http://developer.yahoo.net/blog/archives/2009/11/guide_to_dns.html -*/ - -// w is the window object -(function(w) { - -BOOMR = BOOMR || {}; -BOOMR.plugins = BOOMR.plugins || {}; - -var impl = { - complete: false, - base_url: "", - t_start: null, - t_dns: null, - t_http: null, - img: null, - - gen_url: "", - - start: function() { - var random = Math.floor(Math.random()*(2147483647)).toString(36), - cache_bust = "" + (new Date().getTime()) + (Math.random()); - - this.gen_url = this.base_url.replace(/\*/, random); - - impl.img = new Image(); - impl.img.onload = impl.A_loaded; - - impl.t_start = new Date().getTime(); - impl.img.src = this.gen_url + "image-l.gif?t=" + cache_bust; - }, - - A_loaded: function() { - var cache_bust; - impl.t_dns = new Date().getTime() - impl.t_start; - - cache_bust = "" + (new Date().getTime()) + (Math.random()); - - impl.img = new Image(); - impl.img.onload = impl.B_loaded; - - impl.t_start = new Date().getTime(); - impl.img.src = impl.gen_url + "image-l.gif?t=" + cache_bust; - }, - - B_loaded: function() { - impl.t_http = new Date().getTime() - impl.t_start; - - impl.img = null; - impl.done(); - }, - - done: function() { - // DNS time is the time to load the image with uncached DNS - // minus the time to load the image with cached DNS - - var dns = impl.t_dns - impl.t_http; - - BOOMR.addVar("dns", dns); - this.complete = true; - BOOMR.sendBeacon(); - }, - - read_timing_api: function(t) { - if(typeof t.domainLookupStart === "undefined" - || typeof t.domainLookupEnd === "undefined") { - return false; - } - - // This will be 0 if we read DNS from cache, but that's what - // we want because it's what the user experiences - BOOMR.addVar("dns", t.domainLookupEnd - t.domainLookupStart); - - impl.complete = true; - - return true; - } -}; - -BOOMR.plugins.DNS = { - init: function(config) { - BOOMR.utils.pluginConfig(impl, config, "DNS", ["base_url"]); - - // If this browser supports the WebTiming API, then we just - // use that and don't bother doing the test - if(w.performance && w.performance.timing) { - if(impl.read_timing_api(w.performance.timing)) { - return this; - } - } - - if(!impl.base_url) { - BOOMR.warn("DNS.base_url is not set. Cannot run DNS test.", "dns"); - impl.complete = true; // set to true so that is_complete doesn't - // block other plugins - return this; - } - - // make sure that dns test images use the same protocol as the host page - if(w.location.protocol === 'https:') { - impl.base_url = impl.base_url.replace(/^http:/, 'https:'); - } - else { - impl.base_url = impl.base_url.replace(/^https:/, 'http:'); - } - - BOOMR.subscribe("page_ready", impl.start, null, this); - - return this; - }, - - is_complete: function() { - return impl.complete; - } -}; - -}(window)); - diff --git a/doc-template/README.txt b/doc-template/README.txt new file mode 100644 index 000000000..990e632c6 --- /dev/null +++ b/doc-template/README.txt @@ -0,0 +1,24 @@ +Modified from docstrap: https://github.com/docstrap/docstrap + +Copyright (c) 2012-15 Terry Weiss & Contributors. All rights reserved. + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/doc-template/publish.js b/doc-template/publish.js new file mode 100644 index 000000000..99718ae54 --- /dev/null +++ b/doc-template/publish.js @@ -0,0 +1,881 @@ +"use strict"; + +/** + * @module template/publish + * @type {*} + */ +/*global env: true */ + +var template = require('jsdoc/template'), + doop = require('jsdoc/util/doop'), + fs = require('jsdoc/fs'), + _ = require('underscore'), + path = require('jsdoc/path'), + + taffy = require('taffydb').taffy, + handle = require('jsdoc/util/error').handle, + helper = require('jsdoc/util/templateHelper'), + moment = require("moment"), + htmlsafe = helper.htmlsafe, + sanitizeHtml = require('sanitize-html'), + linkto = helper.linkto, + resolveAuthorLinks = helper.resolveAuthorLinks, + scopeToPunc = helper.scopeToPunc, + hasOwnProp = Object.prototype.hasOwnProperty, + conf = env.conf.templates || {}, + data, + view, + outdir = env.opts.destination, + searchEnabled = conf.search !== false; + +var globalUrl = helper.getUniqueFilename('global'); +var indexUrl = helper.getUniqueFilename('index'); + +var navOptions = { + includeDate: conf.includeDate !== false, + logoFile: conf.logoFile, + systemName: conf.systemName || "Documentation", + navType: conf.navType || "vertical", + footer: conf.footer || "", + copyright: conf.copyright || "", + theme: conf.theme || "simplex", + syntaxTheme: conf.syntaxTheme || "default", + linenums: conf.linenums, + collapseSymbols: conf.collapseSymbols || false, + inverseNav: conf.inverseNav, + outputSourceFiles: conf.outputSourceFiles === true, + sourceRootPath: conf.sourceRootPath, + disablePackagePath: conf.disablePackagePath, + outputSourcePath: conf.outputSourcePath, + dateFormat: conf.dateFormat, + analytics: conf.analytics || null, + methodHeadingReturns: conf.methodHeadingReturns === true, + sort: conf.sort, + search: searchEnabled +}; +var searchableDocuments = {}; + +var navigationMaster = { + index: { + title: navOptions.systemName, + link: indexUrl, + members: [] + }, + namespace: { + title: "Namespaces", + link: helper.getUniqueFilename("namespaces.list"), + members: [] + }, + module: { + title: "Modules", + link: helper.getUniqueFilename("modules.list"), + members: [] + }, + class: { + title: "Classes", + link: helper.getUniqueFilename('classes.list'), + members: [] + }, + + mixin: { + title: "Mixins", + link: helper.getUniqueFilename("mixins.list"), + members: [] + }, + event: { + title: "Events", + link: helper.getUniqueFilename("events.list"), + members: [] + }, + interface: { + title: "Interfaces", + link: helper.getUniqueFilename("interfaces.list"), + members: [] + }, + tutorial: { + title: "Tutorials", + link: helper.getUniqueFilename("tutorials.list"), + members: [] + }, + global: { + title: "Global", + link: globalUrl, + members: [] + + }, + external: { + title: "Externals", + link: helper.getUniqueFilename("externals.list"), + members: [] + } +}; + +function find(spec) { + return helper.find(data, spec); +} + +function tutoriallink(tutorial) { + return helper.toTutorial(tutorial, null, { + tag: 'em', + classname: 'disabled', + prefix: 'Tutorial: ' + }); +} + +function getAncestorLinks(doclet) { + return helper.getAncestorLinks(data, doclet); +} + +function hashToLink(doclet, hash) { + if (!/^(#.+)/.test(hash)) { + return hash; + } + + var url = helper.createLink(doclet); + + url = url.replace(/(#.+|$)/, hash); + return '' + hash + ''; +} + +function needsSignature(doclet) { + var needsSig = false; + + // function and class definitions always get a signature + if (doclet.kind === 'function' || doclet.kind === 'class') { + needsSig = true; + } + // typedefs that contain functions get a signature, too + else if (doclet.kind === 'typedef' && doclet.type && doclet.type.names && + doclet.type.names.length) { + for (var i = 0, l = doclet.type.names.length; i < l; i++) { + if (doclet.type.names[i].toLowerCase() === 'function') { + needsSig = true; + break; + } + } + } + + return needsSig; +} + +function addSignatureParams(f) { + var optionalClass = 'optional'; + var params = helper.getSignatureParams(f, optionalClass); + + f.signature = (f.signature || '') + '('; + + for (var i = 0, l = params.length; i < l; i++) { + var element = params[i]; + var seperator = (i > 0) ? ', ' : ''; + + if (!new RegExp("class=[\"|']"+optionalClass+"[\"|']").test(element)) { + f.signature += seperator + element; + } else { + var regExp = new RegExp("(.*?)<\\/span>", "i"); + f.signature += element.replace(regExp, " $`["+seperator+"$1$']"); + } + + } + + f.signature += ')'; +} + +function addSignatureReturns(f) { + if (navOptions.methodHeadingReturns) { + var returnTypes = helper.getSignatureReturns(f); + + f.signature = '' + (f.signature || '') + '' + '' + (returnTypes.length ? ' → {' + returnTypes.join('|') + '}' : '') + ''; + } + else { + f.signature = f.signature || ''; + } +} + +function addSignatureTypes(f) { + var types = helper.getSignatureTypes(f); + + f.signature = (f.signature || '') + '' + (types.length ? ' :' + types.join('|') : '') + ''; +} + +function addAttribs(f) { + var attribs = helper.getAttribs(f); + + f.attribs = '' + htmlsafe(attribs.length ? '<' + attribs.join(', ') + '> ' : '') + ''; +} + +function shortenPaths(files, commonPrefix) { + Object.keys(files).forEach(function(file) { + files[file].shortened = files[file].resolved.replace(commonPrefix, '') + // always use forward slashes + .replace(/\\/g, '/'); + }); + + + return files; +} + +function getPathFromDoclet(doclet) { + if (!doclet.meta) { + return; + } + + return path.normalize(doclet.meta.path && doclet.meta.path !== 'null' ? + doclet.meta.path + '/' + doclet.meta.filename : + doclet.meta.filename); +} + +function searchData(html) { + var startOfContent = html.indexOf("
"); + if (startOfContent > 0) { + var startOfSecondContent = html.indexOf("
", startOfContent + 2); + if (startOfSecondContent > 0) { + startOfContent = startOfSecondContent; + } + html = html.slice(startOfContent); + } + var endOfContent = html.indexOf(""); + if (endOfContent > 0) { + html = html.substring(0, endOfContent); + } + var stripped = sanitizeHtml(html, {allowedTags: [], allowedAttributes: []}); + stripped = stripped.replace(/\s+/g, ' '); + return stripped; +} + +function generate(docType, title, docs, filename, resolveLinks) { + resolveLinks = resolveLinks === false ? false : true; + + var docData = { + title: title, + docs: docs, + docType: docType + }; + + var outpath = path.join(outdir, filename), + html = view.render('container.tmpl', docData); + + if (resolveLinks) { + html = helper.resolveLinks(html); // turn {@link foo} into foo + } + + if (searchEnabled) { + searchableDocuments[filename] = { + "id": filename, + "title": title, + "body": searchData(html) + }; + } + + fs.writeFileSync(outpath, html, 'utf8'); +} + +function generateSourceFiles(sourceFiles) { + Object.keys(sourceFiles).forEach(function(file) { + var source; + // links are keyed to the shortened path in each doclet's `meta.shortpath` property + var sourceOutfile = helper.getUniqueFilename(sourceFiles[file].shortened); + helper.registerLink(sourceFiles[file].shortened, sourceOutfile); + + try { + source = { + kind: 'source', + code: helper.htmlsafe(fs.readFileSync(sourceFiles[file].resolved, 'utf8')) + }; + } catch (e) { + handle(e); + } + + generate('source', 'Source: ' + sourceFiles[file].shortened, [source], sourceOutfile, + false); + }); +} + +/** + * Look for classes or functions with the same name as modules (which indicates that the module + * exports only that class or function), then attach the classes or functions to the `module` + * property of the appropriate module doclets. The name of each class or function is also updated + * for display purposes. This function mutates the original arrays. + * + * @private + * @param {Array.} doclets - The array of classes and functions to + * check. + * @param {Array.} modules - The array of module doclets to search. + */ +function attachModuleSymbols(doclets, modules) { + var symbols = {}; + + // build a lookup table + doclets.forEach(function(symbol) { + symbols[symbol.longname] = symbols[symbol.longname] || []; + symbols[symbol.longname].push(symbol); + }); + + return modules.map(function(module) { + if (symbols[module.longname]) { + module.modules = symbols[module.longname] + // Only show symbols that have a description. Make an exception for classes, because + // we want to show the constructor-signature heading no matter what. + .filter(function(symbol) { + return symbol.description || symbol.kind === 'class'; + }) + .map(function(symbol) { + symbol = doop(symbol); + + if (symbol.kind === 'class' || symbol.kind === 'function') { + symbol.name = symbol.name.replace('module:', '(require("') + '"))'; + } + + return symbol; + }); + } + }); +} + +/** + * Create the navigation sidebar. + * @param {object} members The members that will be used to create the sidebar. + * @param {array} members.classes + * @param {array} members.externals + * @param {array} members.globals + * @param {array} members.mixins + * @param {array} members.interfaces + * @param {array} members.modules + * @param {array} members.namespaces + * @param {array} members.tutorials + * @param {array} members.events + * @return {string} The HTML for the navigation sidebar. + */ +function buildNav(members) { + + var seen = {}; + var nav = navigationMaster; + if (members.modules.length) { + + members.modules.forEach(function(m) { + if (!hasOwnProp.call(seen, m.longname)) { + + nav.module.members.push(linkto(m.longname, m.longname.replace("module:", ""))); + } + seen[m.longname] = true; + }); + } + + if (members.externals.length) { + + members.externals.forEach(function(e) { + if (!hasOwnProp.call(seen, e.longname)) { + + nav.external.members.push(linkto(e.longname, e.name.replace(/(^"|"$)/g, ''))); + } + seen[e.longname] = true; + }); + } + + if (members.classes.length) { + + members.classes.forEach(function(c) { + if (!hasOwnProp.call(seen, c.longname)) { + + nav.class.members.push(linkto(c.longname, c.longname.replace("module:", ""))); + } + seen[c.longname] = true; + }); + + } + + if (members.events.length) { + + members.events.forEach(function(e) { + if (!hasOwnProp.call(seen, e.longname)) { + + nav.event.members.push(linkto(e.longname, e.longname.replace("module:", ""))); + } + seen[e.longname] = true; + }); + + } + + if (members.namespaces.length) { + + members.namespaces.forEach(function(n) { + if (!hasOwnProp.call(seen, n.longname)) { + + nav.namespace.members.push(linkto(n.longname, n.longname.replace("module:", ""))); + } + seen[n.longname] = true; + }); + + } + + if (members.mixins.length) { + + members.mixins.forEach(function(m) { + if (!hasOwnProp.call(seen, m.longname)) { + + nav.mixin.members.push(linkto(m.longname, m.longname.replace("module:", ""))); + } + seen[m.longname] = true; + }); + + } + + if (members.interfaces && members.interfaces.length) { + + members.interfaces.forEach(function(m) { + if (!hasOwnProp.call(seen, m.longname)) { + + nav.interface.members.push(linkto(m.longname, m.longname.replace("module:", ""))); + } + seen[m.longname] = true; + }); + + } + + if (members.tutorials.length) { + + members.tutorials.forEach(function(t) { + + nav.tutorial.members.push(tutoriallink(t.name)); + }); + + } + + if (members.globals.length) { + members.globals.forEach(function(g) { + if (g.kind !== 'typedef' && !hasOwnProp.call(seen, g.longname)) { + + nav.global.members.push(linkto(g.longname, g.longname.replace("module:", ""))); + } + seen[g.longname] = true; + }); + + // even if there are no links, provide a link to the global page. + if (nav.global.members.length === 0) { + nav.global.members.push(linkto("global", "Global")); + } + } + + var topLevelNav = []; + _.each(nav, function(entry, name) { + if (entry.members.length > 0 && name !== "index") { + topLevelNav.push({ + title: entry.title, + link: entry.link, + members: entry.members + }); + } + }); + nav.topLevelNav = topLevelNav; +} + +/** + @param {TAFFY} taffyData See . + @param {object} opts + @param {Tutorial} tutorials + */ +exports.publish = function(taffyData, opts, tutorials) { + data = taffyData; + + conf['default'] = conf['default'] || {}; + + var templatePath = opts.template; + view = new template.Template(templatePath + '/tmpl'); + + // claim some special filenames in advance, so the All-Powerful Overseer of Filename Uniqueness + // doesn't try to hand them out later + // var indexUrl = helper.getUniqueFilename( 'index' ); + // don't call registerLink() on this one! 'index' is also a valid longname + + // var globalUrl = helper.getUniqueFilename( 'global' ); + helper.registerLink('global', globalUrl); + + // set up templating + // set up templating + view.layout = conf['default'].layoutFile ? + path.getResourcePath(path.dirname(conf['default'].layoutFile), + path.basename(conf['default'].layoutFile) ) : 'layout.tmpl'; + + // set up tutorials for helper + helper.setTutorials(tutorials); + + data = helper.prune(data); + + var sortOption = navOptions.sort === undefined ? opts.sort : navOptions.sort; + sortOption = sortOption === undefined ? true : sortOption; + sortOption = sortOption === true ? 'longname, version, since' : sortOption; + if (sortOption) { + data.sort(sortOption); + } + helper.addEventListeners(data); + + var sourceFiles = {}; + var sourceFilePaths = []; + data().each(function(doclet) { + doclet.attribs = ''; + + if (doclet.examples) { + doclet.examples = doclet.examples.map(function(example) { + var caption, lang; + + // allow using a markdown parser on the examples captions (surrounded by useless HTML p tags) + if (example.match(/^\s*(

)?([\s\S]+?)<\/caption>(\s*)([\s\S]+?)(<\/p>)?$/i)) { + caption = RegExp.$2; + example = RegExp.$4 + (RegExp.$1 ? '' : RegExp.$5); + } + + var lang = /{@lang (.*?)}/.exec(example); + + if (lang && lang[1]) { + example = example.replace(lang[0], ""); + lang = lang[1]; + + } else { + lang = null; + } + + return { + caption: caption || '', + code: example, + lang: lang || "javascript" + }; + }); + } + if (doclet.see) { + doclet.see.forEach(function(seeItem, i) { + doclet.see[i] = hashToLink(doclet, seeItem); + }); + } + + // build a list of source files + var sourcePath; + if (doclet.meta) { + sourcePath = getPathFromDoclet(doclet); + sourceFiles[sourcePath] = { + resolved: sourcePath, + shortened: null + }; + + //Check to see if the array of source file paths already contains + // the source path, if not then add it + if (sourceFilePaths.indexOf(sourcePath) === -1) { + sourceFilePaths.push(sourcePath) + } + } + }); + + // update outdir if necessary, then create outdir + var packageInfo = (find({ + kind: 'package' + }) || [])[0]; + if (navOptions.disablePackagePath !== true && packageInfo && packageInfo.name) { + if (packageInfo.version) { + outdir = path.join(outdir, packageInfo.name, packageInfo.version); + } else { + outdir = path.join(outdir, packageInfo.name); + } + } + fs.mkPath(outdir); + + // copy the template's static files to outdir + var fromDir = path.join( templatePath, 'static' ); + var staticFiles = fs.ls( fromDir, 3 ); + staticFiles.forEach( function ( fileName ) { + var toFile = fileName.replace( fromDir, outdir ); + var toDir = fs.toDir( toFile ); + fs.mkPath( toDir ); + fs.copyFileSync( fileName, '', toFile ); + } ); + + // copy user-specified static files to outdir + var staticFilePaths; + var staticFileFilter; + var staticFileScanner; + if (conf.default.staticFiles) { + // The canonical property name is `include`. We accept `paths` for backwards compatibility + // with a bug in JSDoc 3.2.x. + staticFilePaths = conf.default.staticFiles.include || + conf.default.staticFiles.paths || + []; + staticFileFilter = new (require('jsdoc/src/filter')).Filter(conf.default.staticFiles); + staticFileScanner = new (require('jsdoc/src/scanner')).Scanner(); + + staticFilePaths.forEach(function(filePath) { + var extraStaticFiles = staticFileScanner.scan([filePath], 10, staticFileFilter); + + extraStaticFiles.forEach(function(fileName) { + var sourcePath = fs.toDir(filePath); + var toDir = fs.toDir( fileName.replace(sourcePath, outdir) ); + fs.mkPath(toDir); + fs.copyFileSync(fileName, toDir, fileName.replace(sourcePath, '')); + }); + }); + } + + if (sourceFilePaths.length) { + var payload = navOptions.sourceRootPath; + if (!payload) { + payload = path.commonPrefix(sourceFilePaths); + } + sourceFiles = shortenPaths(sourceFiles, payload); + } + data().each(function(doclet) { + var url = helper.createLink(doclet); + helper.registerLink(doclet.longname, url); + + // add a shortened version of the full path + var docletPath; + if (doclet.meta) { + docletPath = getPathFromDoclet(doclet); + if (!_.isEmpty(sourceFiles[docletPath])) { + docletPath = sourceFiles[docletPath].shortened; + if (docletPath) { + doclet.meta.shortpath = docletPath; + } + } + } + }); + + data().each(function(doclet) { + var url = helper.longnameToUrl[doclet.longname]; + + if (url.indexOf('#') > -1) { + doclet.id = helper.longnameToUrl[doclet.longname].split(/#/).pop(); + } else { + doclet.id = doclet.name; + } + + if (needsSignature(doclet)) { + addSignatureParams(doclet); + addSignatureReturns(doclet); + addAttribs(doclet); + } + }); + + // do this after the urls have all been generated + data().each(function(doclet) { + doclet.ancestors = getAncestorLinks(doclet); + + if (doclet.kind === 'member') { + addSignatureTypes(doclet); + addAttribs(doclet); + } + + if (doclet.kind === 'constant') { + addSignatureTypes(doclet); + addAttribs(doclet); + doclet.kind = 'member'; + } + }); + + var members = helper.getMembers(data); + members.tutorials = tutorials.children; + + // add template helpers + view.find = find; + view.linkto = linkto; + view.resolveAuthorLinks = resolveAuthorLinks; + view.tutoriallink = tutoriallink; + view.htmlsafe = htmlsafe; + view.moment = moment; + + // once for all + buildNav(members); + view.nav = navigationMaster; + view.navOptions = navOptions; + attachModuleSymbols(find({ + kind: ['class', 'function'], + longname: { + left: 'module:' + } + }), + members.modules); + + // only output pretty-printed source files if requested; do this before generating any other + // pages, so the other pages can link to the source files + if (navOptions.outputSourceFiles) { + generateSourceFiles(sourceFiles); + } + + if (members.globals.length) { + generate('global', 'Global', [{ + kind: 'globalobj' + }], globalUrl); + } + + // some browsers can't make the dropdown work + if (view.nav.module && view.nav.module.members.length) { + generate('module', view.nav.module.title, [{ + kind: 'sectionIndex', + contents: view.nav.module + }], navigationMaster.module.link); + } + + if (view.nav.class && view.nav.class.members.length) { + generate('class', view.nav.class.title, [{ + kind: 'sectionIndex', + contents: view.nav.class + }], navigationMaster.class.link); + } + + if (view.nav.namespace && view.nav.namespace.members.length) { + generate('namespace', view.nav.namespace.title, [{ + kind: 'sectionIndex', + contents: view.nav.namespace + }], navigationMaster.namespace.link); + } + + if (view.nav.mixin && view.nav.mixin.members.length) { + generate('mixin', view.nav.mixin.title, [{ + kind: 'sectionIndex', + contents: view.nav.mixin + }], navigationMaster.mixin.link); + } + + if (view.nav.interface && view.nav.interface.members.length) { + generate('interface', view.nav.interface.title, [{ + kind: 'sectionIndex', + contents: view.nav.interface + }], navigationMaster.interface.link); + } + + if (view.nav.external && view.nav.external.members.length) { + generate('external', view.nav.external.title, [{ + kind: 'sectionIndex', + contents: view.nav.external + }], navigationMaster.external.link); + } + + if (view.nav.tutorial && view.nav.tutorial.members.length) { + generate('tutorial', view.nav.tutorial.title, [{ + kind: 'sectionIndex', + contents: view.nav.tutorial + }], navigationMaster.tutorial.link); + } + + // index page displays information from package.json and lists files + var files = find({ + kind: 'file' + }), + packages = find({ + kind: 'package' + }); + + generate('index', 'Index', + packages.concat( + [{ + kind: 'mainpage', + readme: opts.readme, + longname: (opts.mainpagetitle) ? opts.mainpagetitle : 'Main Page' + }] + ).concat(files), + indexUrl); + + // set up the lists that we'll use to generate pages + var classes = taffy(members.classes); + var modules = taffy(members.modules); + var namespaces = taffy(members.namespaces); + var mixins = taffy(members.mixins); + var interfaces = taffy(members.interfaces); + var externals = taffy(members.externals); + + for (var longname in helper.longnameToUrl) { + if (hasOwnProp.call(helper.longnameToUrl, longname)) { + var myClasses = helper.find(classes, { + longname: longname + }); + if (myClasses.length) { + generate('class', 'Class: ' + myClasses[0].name, myClasses, helper.longnameToUrl[longname]); + } + + var myModules = helper.find(modules, { + longname: longname + }); + if (myModules.length) { + generate('module', 'Module: ' + myModules[0].name, myModules, helper.longnameToUrl[longname]); + } + + var myNamespaces = helper.find(namespaces, { + longname: longname + }); + if (myNamespaces.length) { + generate('namespace', 'Namespace: ' + myNamespaces[0].name, myNamespaces, helper.longnameToUrl[longname]); + } + + var myMixins = helper.find(mixins, { + longname: longname + }); + if (myMixins.length) { + generate('mixin', 'Mixin: ' + myMixins[0].name, myMixins, helper.longnameToUrl[longname]); + } + + var myInterfaces = helper.find(interfaces, { + longname: longname + }); + if (myInterfaces.length) { + generate('interface', 'Interface: ' + myInterfaces[0].name, myInterfaces, helper.longnameToUrl[longname]); + } + + var myExternals = helper.find(externals, { + longname: longname + }); + if (myExternals.length) { + generate('external', 'External: ' + myExternals[0].name, myExternals, helper.longnameToUrl[longname]); + } + } + } + + function generateTutorial(title, tutorial, filename) { + var tutorialData = { + title: title, + header: tutorial.title, + content: tutorial.parse(), + children: tutorial.children, + docs: null + }; + + var tutorialPath = path.join(outdir, filename), + html = view.render('tutorial.tmpl', tutorialData); + + // yes, you can use {@link} in tutorials too! + html = helper.resolveLinks(html); // turn {@link foo} into foo + + if (searchEnabled) { + searchableDocuments[filename] = { + "id": filename, + "title": title, + "body": searchData(html) + }; + } + + fs.writeFileSync(tutorialPath, html, 'utf8'); + } + + // tutorials can have only one parent so there is no risk for loops + function saveChildren(node) { + node.children.forEach(function(child) { + generateTutorial('Tutorial: ' + child.title, child, helper.tutorialToUrl(child.name)); + saveChildren(child); + }); + } + + function generateQuickTextSearch(templatePath, searchableDocuments, navOptions) { + var data = { + searchableDocuments: JSON.stringify(searchableDocuments), + navOptions: navOptions + }; + + var tmplString = fs.readFileSync(templatePath + "/quicksearch.tmpl").toString(), + tmpl = _.template(tmplString); + + var html = tmpl(data), + outpath = path.join(outdir, "quicksearch.html"); + + fs.writeFileSync(outpath, html, "utf8"); + } + + saveChildren(tutorials); + + if (searchEnabled) { + generateQuickTextSearch(templatePath + '/tmpl', searchableDocuments, navOptions); + } +}; diff --git a/doc-template/static/fonts/glyphicons-halflings-regular.eot b/doc-template/static/fonts/glyphicons-halflings-regular.eot new file mode 100644 index 000000000..b93a4953f Binary files /dev/null and b/doc-template/static/fonts/glyphicons-halflings-regular.eot differ diff --git a/doc-template/static/fonts/glyphicons-halflings-regular.svg b/doc-template/static/fonts/glyphicons-halflings-regular.svg new file mode 100644 index 000000000..94fb5490a --- /dev/null +++ b/doc-template/static/fonts/glyphicons-halflings-regular.svg @@ -0,0 +1,288 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc-template/static/fonts/glyphicons-halflings-regular.ttf b/doc-template/static/fonts/glyphicons-halflings-regular.ttf new file mode 100644 index 000000000..1413fc609 Binary files /dev/null and b/doc-template/static/fonts/glyphicons-halflings-regular.ttf differ diff --git a/doc-template/static/fonts/glyphicons-halflings-regular.woff b/doc-template/static/fonts/glyphicons-halflings-regular.woff new file mode 100644 index 000000000..9e612858f Binary files /dev/null and b/doc-template/static/fonts/glyphicons-halflings-regular.woff differ diff --git a/doc-template/static/fonts/glyphicons-halflings-regular.woff2 b/doc-template/static/fonts/glyphicons-halflings-regular.woff2 new file mode 100644 index 000000000..64539b54c Binary files /dev/null and b/doc-template/static/fonts/glyphicons-halflings-regular.woff2 differ diff --git a/doc-template/static/img/glyphicons-halflings-white.png b/doc-template/static/img/glyphicons-halflings-white.png new file mode 100644 index 000000000..3bf6484a2 Binary files /dev/null and b/doc-template/static/img/glyphicons-halflings-white.png differ diff --git a/doc-template/static/img/glyphicons-halflings.png b/doc-template/static/img/glyphicons-halflings.png new file mode 100644 index 000000000..a99699932 Binary files /dev/null and b/doc-template/static/img/glyphicons-halflings.png differ diff --git a/doc-template/static/scripts/docstrap.lib.js b/doc-template/static/scripts/docstrap.lib.js new file mode 100644 index 000000000..09d9272a0 --- /dev/null +++ b/doc-template/static/scripts/docstrap.lib.js @@ -0,0 +1,11 @@ +if(!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){function c(a){var b="length"in a&&a.length,c=_.type(a);return"function"!==c&&!_.isWindow(a)&&(!(1!==a.nodeType||!b)||("array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a))}function d(a,b,c){if(_.isFunction(b))return _.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return _.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(ha.test(b))return _.filter(b,a,c);b=_.filter(b,a)}return _.grep(a,function(a){return U.call(b,a)>=0!==c})}function e(a,b){for(;(a=a[b])&&1!==a.nodeType;);return a}function f(a){var b=oa[a]={};return _.each(a.match(na)||[],function(a,c){b[c]=!0}),b}function g(){Z.removeEventListener("DOMContentLoaded",g,!1),a.removeEventListener("load",g,!1),_.ready()}function h(){Object.defineProperty(this.cache={},0,{get:function(){return{}}}),this.expando=_.expando+h.uid++}function i(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(ua,"-$1").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c="true"===c||"false"!==c&&("null"===c?null:+c+""===c?+c:ta.test(c)?_.parseJSON(c):c)}catch(a){}sa.set(a,b,c)}else c=void 0;return c}function j(){return!0}function k(){return!1}function l(){try{return Z.activeElement}catch(a){}}function m(a,b){return _.nodeName(a,"table")&&_.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function n(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function o(a){var b=Ka.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function p(a,b){for(var c=0,d=a.length;d>c;c++)ra.set(a[c],"globalEval",!b||ra.get(b[c],"globalEval"))}function q(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(ra.hasData(a)&&(f=ra.access(a),g=ra.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;d>c;c++)_.event.add(b,e,j[e][c])}sa.hasData(a)&&(h=sa.access(a),i=_.extend({},h),sa.set(b,i))}}function r(a,b){var c=a.getElementsByTagName?a.getElementsByTagName(b||"*"):a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&_.nodeName(a,b)?_.merge([a],c):c}function s(a,b){var c=b.nodeName.toLowerCase();"input"===c&&ya.test(a.type)?b.checked=a.checked:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}function t(b,c){var d,e=_(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:_.css(e[0],"display");return e.detach(),f}function u(a){var b=Z,c=Oa[a];return c||(c=t(a,b),"none"!==c&&c||(Na=(Na||_("\n"; + } + + // if the .js file exists, copy that too + if (grunt.file.exists(jsFile)) { + grunt.log.ok(jsFileName); + + grunt.file.copy(jsFile, jsFileDest); + } + }); + + grunt.log.ok(indexFileName); + grunt.file.write(indexFile, indexHtml); + + cb2(); + }); + }, function(err) { + // write root index + grunt.log.ok("index.html"); + grunt.file.write(rootIndexFile, rootIndexHtml); + var testConfiguration = { + server: { + main: boomerangE2ETestDomain, + second: boomerangE2ESecondDomain, + scheme: boomerangE2ETestScheme + }, + ports: { + main: boomerangE2ETestPort, + second: boomerangE2ETestPort + 1 + }, + tests: testDefinitions + }; + + // test definitions + grunt.file.write(e2eJsonPath, JSON.stringify(testConfiguration, null, 2)); + + cb(err, opts); + }); + } + ], function(err) { + if (err) { + console.log(err); + } + + done(); + }); +}; diff --git a/tests/e2e/e2e-debug.js b/tests/e2e/e2e-debug.js new file mode 100644 index 000000000..c363afb08 --- /dev/null +++ b/tests/e2e/e2e-debug.js @@ -0,0 +1,91 @@ +/* eslint-env mocha,node */ +/* global browser,by */ + +// +// Imports +// +var chai = require("chai"); +var assert = chai.assert; +var path = require("path"); + +var testsFile = path.join(__dirname, "e2e-debug.json"); +var tests = require(testsFile).tests; +var servers = require(testsFile).server; +var ports = require(testsFile).ports; + +// +// Functions +// +function run(testPath, file) { + describe(testPath, function() { + var fileName = file + ".html"; + + it("Should pass " + testPath + "/" + fileName, function(done) { + var url = servers.scheme + "://" + servers.main + ":" + ports.main + "/pages/" + testPath + "/" + fileName; + + if (typeof browser.waitForAngularEnabled === "function") { + browser.waitForAngularEnabled(false); + } + + console.log( + "Navigating to", + url + ); + + browser.driver.executeScript("return navigator.userAgent;").then(function(ua) { + console.log("User-Agent:", ua); + }); + + browser.driver.get(url); + + // poll every 100ms for new logs or the test framework to note we're complete + (function poll() { + // get browser logs + browser.manage().logs().get("browser").then(function(browserLog) { + browserLog.forEach(function(log) { + console.log("[" + new Date(log.timestamp).toLocaleTimeString() + "] " + log.message); + }); + }); + + // check if our element is there + browser.isElementPresent(by.css("#BOOMR_test_complete")) + .then(function(present) { + if (!present) { + setTimeout(poll, 100); + + return; + } + + // get error messages + browser.driver.executeScript("return BOOMR_test.isComplete()").then(function(complete) { + assert.equal(complete, true, "BOOMR_test.isComplete()"); + + console.log("Navigation complete"); + + browser.driver.executeScript("return BOOMR_test.getTestFailureMessages()").then(function(testFailures) { + if (testFailures.length > 0) { + throw new Error(testFailures); + } + + done(); + }); + }); + }); + })(); + }); + }); +} + +// NOTE: To use this, create a file e2e-debug.json (in this directory), the same format +// as e2e.json for the tests you want to run + +// +// Run the tests in e2e-debug.json +// +for (var i = 0; i < tests.length; i++) { + var data = tests[i]; + + console.log("Running " + data.path + "/" + data.file); + + run(data.path, data.file); +} diff --git a/tests/e2e/e2e.disabled.json b/tests/e2e/e2e.disabled.json new file mode 100644 index 000000000..0d4f101c7 --- /dev/null +++ b/tests/e2e/e2e.disabled.json @@ -0,0 +1,2 @@ +[ +] diff --git a/tests/e2e/e2e.js b/tests/e2e/e2e.js new file mode 100644 index 000000000..1fc3706c2 --- /dev/null +++ b/tests/e2e/e2e.js @@ -0,0 +1,87 @@ +/* eslint-env mocha,node */ +/* global browser,by */ + +// +// Imports +// +var chai = require("chai"); +var assert = chai.assert; +var grunt = require("grunt"); + +var testsFile = require("./e2e.json"); +var tests = testsFile.tests; +var servers = testsFile.server; +var ports = testsFile.ports; + +var disabledTests = require("./e2e.disabled.json"); +var buildFlavor = (grunt.option("build-flavor") || "") || process.env.BUILD_FLAVOR; + +// +// Functions +// +function run(i, testPath, file, flavor) { + describe(testPath, function() { + var fileName = file + ".html"; + + it(file + (buildFlavor ? ("." + buildFlavor) : ""), function(done) { + var url = servers.scheme + "://" + servers.main + ":" + ports.main + "/pages/" + testPath + "/" + fileName; + + if (typeof browser.waitForAngularEnabled === "function") { + browser.waitForAngularEnabled(false); + } + + console.log( + i, + "Navigating to", + url + ); + + browser.driver.get(url); + + browser.driver.wait(function() { + return element(by.css("#BOOMR_test_complete")).isPresent(); + }); + + browser.driver.executeScript("return BOOMR_test.isComplete()").then(function(complete) { + assert.equal(complete, true, "BOOMR_test.isComplete()"); + + browser.driver.executeScript("return BOOMR_test.getTestFailureMessages()").then(function(testFailures) { + if (testFailures.length > 0) { + throw new Error(testFailures); + } + + done(); + }); + }); + }); + }); +} + +var disabledTestLookup = {}; + +for (var i = 0; i < disabledTests.length; i++) { + var key = disabledTests[i].path + "-" + disabledTests[i].file; + + disabledTestLookup[key] = 1; +} + +// +// Run the tests in e2e.json +// +var start = parseInt(process.env.CI_NODE_INDEX) || 0; +var steps = parseInt(process.env.CI_NODE_TOTAL) || 1; + +console.log("START: " + start); +console.log("STEPS: " + steps); + +for (i = start; i < tests.length; i += steps) { + var data = tests[i]; + + key = data.path + "-" + data.file; + + if (disabledTestLookup[key]) { + continue; + } + + run(i, data.path, data.file, buildFlavor); +} diff --git a/tests/e2e/unit.js b/tests/e2e/unit.js new file mode 100644 index 000000000..aad235199 --- /dev/null +++ b/tests/e2e/unit.js @@ -0,0 +1,28 @@ +/* eslint-env mocha,node */ +/* global browser,by */ + +var chai = require("chai"); +var assert = chai.assert; + +describe("BOOMR Basic Integration test", function() { + it("Should pass the Mocha unit tests", function(done) { + browser.driver.get("http://localhost:4002/unit/index.html"); + + browser.driver.wait(function() { + return browser.driver.isElementPresent(by.css("#BOOMR_test_complete")); + }); + + browser.driver.executeScript("return BOOMR_test.isComplete()").then(function(complete){ + assert.equal(complete, true, "BOOMR_test.isComplete()"); + browser.driver.executeScript("return BOOMR_test.getTestFailureMessages()").then(function(testFailures){ + // log testFailures only if they exist + if (testFailures.length > 0) { + console.log("BOOMR_test.getTestFailures():\n" + testFailures); + } + + assert.equal(testFailures.length, 0); + done(); + }); + }); + }); +}); diff --git a/tests/index.html b/tests/index.html index 191a319b4..1660159b3 100644 --- a/tests/index.html +++ b/tests/index.html @@ -1,14 +1,10 @@ -

- - - + + + Boomerang Tests + + +

Boomerang Tests

+

Unit tests

+

E2E

+ + diff --git a/tests/karma.config.js b/tests/karma.config.js new file mode 100644 index 000000000..af37e8fc9 --- /dev/null +++ b/tests/karma.config.js @@ -0,0 +1,90 @@ +/* eslint-env node */ + +var url = require("url"); + +module.exports = function(config) { + var remoteSelenium = false, + u, webdriverConfig; + + var tapFileName = "results/unit" + (process.env.BUILD_FLAVOR ? ("-" + process.env.BUILD_FLAVOR) : "") + ".tap"; + + if (config.SELENIUM_ADDRESS) { + remoteSelenium = true; + u = url.parse(config.SELENIUM_ADDRESS, true); + webdriverConfig = { + hostname: u.hostname, + port: u.port + }; + + if (u.auth) { + webdriverConfig.user = u.auth.split(":")[0]; + webdriverConfig.pwd = u.auth.split(":")[1]; + } + } + + config.set({ + basePath: "./", + + port: 4000, + + logLevel: config.LOG_INFO, + + colors: true, + autoWatch: false, + + frameworks: ["mocha"], + reporters: ["progress", "tap"], + plugins: [ + "karma-mocha", + + // reporters + "karma-tap-reporter", + "karma-mocha-reporter" + ], + + tapReporter: { + outputFile: tapFileName + } + }); + + if (!remoteSelenium) { + config.runnerPort = 4001; + + // launchers + config.plugins.push( + "karma-chrome-launcher", + "karma-firefox-launcher", + "karma-ie-launcher", + "karma-opera-launcher", + "karma-safari-launcher"); + } + else { + // launchers + config.plugins.push("karma-webdriver-launcher"); + + config.set({ + customLaunchers: { + "ChromeHeadless": { + base: "WebDriver", + config: webdriverConfig, + browserName: "chrome", + flags: ["--headless", "--disable-gpu", "--window-size=1024,768"], + platform: "ANY", + version: "ANY" + }, + "FirefoxHeadless": { + base: "WebDriver", + config: webdriverConfig, + browserName: "firefox", + "moz:firefoxOptions": { + args: [ "--headless" ] + }, + platform: "ANY", + version: "ANY" + } + }, + + browsers: ["ChromeHeadless", "FirefoxHeadless"] + }); + } +}; diff --git a/tests/karma.coverage.config.js b/tests/karma.coverage.config.js new file mode 100644 index 000000000..1402cf910 --- /dev/null +++ b/tests/karma.coverage.config.js @@ -0,0 +1,27 @@ +/* eslint-env node */ + +var baseConfig = require("./karma.config.js"); + +module.exports = function(config) { + baseConfig(config); + config.reporters.push("coverage"); + config.plugins.push("karma-coverage"); + + config.set({ + preprocessors: { + "build/boomerang-latest-debug.js": ["coverage"] + }, + coverageReporter: { + dir: "coverage/unit/", + instrumenterOptions: { + istanbul: {esModules: false} // Not an ES6 module. + }, + reporters: [ + { type: "html", subdir: "html"}, + { type: "text", subdir: ".", file: "text.txt" }, + { type: "text-summary", subdir: ".", file: "text-summary.txt" } + ] + } + }); +}; + diff --git a/tests/page-template-snippets/boomerangAfterOnloadSnippetNoScript.tpl b/tests/page-template-snippets/boomerangAfterOnloadSnippetNoScript.tpl new file mode 100644 index 000000000..eaf70d2a7 --- /dev/null +++ b/tests/page-template-snippets/boomerangAfterOnloadSnippetNoScript.tpl @@ -0,0 +1,215 @@ +/* eslint-disable no-script-url */ +(function() { + // Boomerang Loader Snippet version 15 + if (window.BOOMR && (window.BOOMR.version || window.BOOMR.snippetExecuted)) { + return; + } + + window.BOOMR = window.BOOMR || {}; + window.BOOMR.snippetStart = new Date().getTime(); + window.BOOMR.snippetExecuted = true; + window.BOOMR.snippetVersion = 15; + + // NOTE: Set Boomerang URL here + window.BOOMR.url = ""; + + // document.currentScript is supported in all browsers other than IE + var where = document.currentScript || document.getElementsByTagName("script")[0], + // Parent element of the script we inject + parentNode = where.parentNode, + // Whether or not Preload method has worked + promoted = false, + // How long to wait for Preload to work before falling back to iframe method + LOADER_TIMEOUT = 3000; + + // Tells the browser to execute the Preloaded script by adding it to the DOM + function promote() { + if (promoted) { + return; + } + + var script = document.createElement("script"); + + script.id = "boomr-scr-as"; + script.src = window.BOOMR.url; + + // Not really needed since dynamic scripts are async by default and the script is already in cache at this point, + // but some naive parsers will see a missing async attribute and think we're not async + script.async = true; + + parentNode.appendChild(script); + + promoted = true; + } + + // Non-blocking iframe loader (fallback for non-Preload scenarios) for all recent browsers. + // For IE 6/7/8, falls back to dynamic script node. + function iframeLoader(wasFallback) { + promoted = true; + + var dom, + doc = document, + bootstrap, iframe, iframeStyle, + win = window; + + window.BOOMR.snippetMethod = wasFallback ? "if" : "i"; + + // Adds Boomerang within the iframe + bootstrap = function(parent, scriptId) { + var script = doc.createElement("script"); + + script.id = scriptId || "boomr-if-as"; + script.src = window.BOOMR.url; + + BOOMR_lstart = new Date().getTime(); + + parent = parent || doc.body; + parent.appendChild(script); + }; + + // For IE 6/7/8, we'll just load the script in the current frame: + // * IE 6/7 don't support 'about:blank' for an iframe src (it triggers warnings on secure sites) + // * IE 8 required a doc write call for it to work, which is bad practice + // This means loading on IE 6/7/8 may cause SPoF. + if (!window.addEventListener && window.attachEvent && navigator.userAgent.match(/MSIE [678]\./)) { + window.BOOMR.snippetMethod = "s"; + + bootstrap(parentNode, "boomr-async"); + + return; + } + + // The rest of this function is for browsers that don't support Preload hints but will work with CSP & iframes + iframe = document.createElement("IFRAME"); + + // An empty frame + iframe.src = "about:blank"; + + // We set title and role appropriately to play nicely with screen readers and other assistive technologies + iframe.title = ""; + iframe.role = "presentation"; + + // Ensure we're not loaded lazily + iframe.loading = "eager"; + + // Hide the iframe + iframeStyle = (iframe.frameElement || iframe).style; + iframeStyle.width = 0; + iframeStyle.height = 0; + iframeStyle.border = 0; + iframeStyle.display = "none"; + + // Append to the end of the current block + parentNode.appendChild(iframe); + + // Try to get the iframe's document object + try { + win = iframe.contentWindow; + doc = win.document.open(); + } + catch (e) { + // document.domain has been changed and we're on an old version of IE, so we got an access denied. + // Note: the only browsers that have this problem also do not have CSP support. + + // Get document.domain of the parent window + dom = document.domain; + + // Set the src of the iframe to a JavaScript URL that will immediately set its document.domain + // to match the parent. + // This lets us access the iframe document long enough to inject our script. + // Our script may need to do more domain massaging later. + iframe.src = "javascript:var d=document.open();d.domain='" + dom + "';void 0;"; + win = iframe.contentWindow; + + doc = win.document.open(); + } + + // document.domain hasn't changed, regular method should be OK + win._boomrl = function() { + bootstrap(); + }; + + if (win.addEventListener) { + win.addEventListener("load", win._boomrl, false); + } + else if (win.attachEvent) { + win.attachEvent("onload", win._boomrl); + } + + // Finish the document + doc.close(); + } + + function boomerangLoad() { + // See if Preload is supported or not + var link = document.createElement("link"); + + if (link.relList && + typeof link.relList.supports === "function" && + link.relList.supports("preload") && + ("as" in link)) { + window.BOOMR.snippetMethod = "p"; + + // Set attributes to trigger a Preload + link.href = window.BOOMR.url; + link.rel = "preload"; + link.as = "script"; + + // Add our script tag if successful, fallback to iframe if not + link.addEventListener("load", promote); + link.addEventListener("error", function() { + iframeLoader(true); + }); + + // Have a fallback in case Preload does nothing or is slow + setTimeout(function() { + if (!promoted) { + iframeLoader(true); + } + }, LOADER_TIMEOUT); + + // Note the timestamp we started trying to Preload + BOOMR_lstart = new Date().getTime(); + + // Append our link tag + parentNode.appendChild(link); + } + else { + // No Preload support, use iframe loader + iframeLoader(false); + } + } + + // Save when the onload event happened, in case this is a non-NavigationTiming browser + function boomerangSaveLoadTime(e) { + window.BOOMR_onload = (e && e.timeStamp) || new Date().getTime(); + } + + if (window.addEventListener) { + window.addEventListener("load", boomerangSaveLoadTime, false); + } + else if (window.attachEvent) { + window.attachEvent("onload", boomerangSaveLoadTime); + } + + // Run at onload + function windowOnLoad(e) { + boomerangSaveLoadTime(e); + setTimeout(boomerangLoad, 0); + } + + // If we think the load event has already fired, load Boomerang now + if (("performance" in win && win.performance && win.performance.timing && win.performance.timing.loadEventStart) || + (document.readyState === "complete")) { + boomerangLoad(); + } + else { + // Wait until onload + if (win.addEventListener) { + win.addEventListener("load", windowOnLoad, false); + } + else if (win.attachEvent) { + win.attachEvent("onload", windowOnLoad); + } + } +})(); diff --git a/tests/page-template-snippets/boomerangDelayedSnippet.tpl b/tests/page-template-snippets/boomerangDelayedSnippet.tpl new file mode 100644 index 000000000..8710bc753 --- /dev/null +++ b/tests/page-template-snippets/boomerangDelayedSnippet.tpl @@ -0,0 +1,3 @@ + diff --git a/tests/page-template-snippets/boomerangDelayedSnippetNoScript.tpl b/tests/page-template-snippets/boomerangDelayedSnippetNoScript.tpl new file mode 100644 index 000000000..f083114ce --- /dev/null +++ b/tests/page-template-snippets/boomerangDelayedSnippetNoScript.tpl @@ -0,0 +1,3 @@ +window.BOOMR_script_delay = true; + +<%= boomerangSnippetNoScript %> diff --git a/tests/page-template-snippets/boomerangScript.tpl b/tests/page-template-snippets/boomerangScript.tpl new file mode 100644 index 000000000..50d90be29 --- /dev/null +++ b/tests/page-template-snippets/boomerangScript.tpl @@ -0,0 +1 @@ + diff --git a/tests/page-template-snippets/boomerangScriptMin.tpl b/tests/page-template-snippets/boomerangScriptMin.tpl new file mode 100644 index 000000000..e871d06c4 --- /dev/null +++ b/tests/page-template-snippets/boomerangScriptMin.tpl @@ -0,0 +1 @@ + diff --git a/tests/page-template-snippets/boomerangScriptPerf.tpl b/tests/page-template-snippets/boomerangScriptPerf.tpl new file mode 100644 index 000000000..9f36efce6 --- /dev/null +++ b/tests/page-template-snippets/boomerangScriptPerf.tpl @@ -0,0 +1 @@ + diff --git a/tests/page-template-snippets/boomerangSnippet.tpl b/tests/page-template-snippets/boomerangSnippet.tpl new file mode 100644 index 000000000..0b8d808a9 --- /dev/null +++ b/tests/page-template-snippets/boomerangSnippet.tpl @@ -0,0 +1,3 @@ + diff --git a/tests/page-template-snippets/boomerangSnippetMin.tpl b/tests/page-template-snippets/boomerangSnippetMin.tpl new file mode 100644 index 000000000..03aac5186 --- /dev/null +++ b/tests/page-template-snippets/boomerangSnippetMin.tpl @@ -0,0 +1,3 @@ + diff --git a/tests/page-template-snippets/boomerangSnippetMinNoScript.tpl b/tests/page-template-snippets/boomerangSnippetMinNoScript.tpl new file mode 100644 index 000000000..159e7fdf1 --- /dev/null +++ b/tests/page-template-snippets/boomerangSnippetMinNoScript.tpl @@ -0,0 +1,3 @@ +window.BOOMR_script_minified = true; + +<%= boomerangSnippetNoScript %> diff --git a/tests/page-template-snippets/boomerangSnippetNoScript.tpl b/tests/page-template-snippets/boomerangSnippetNoScript.tpl new file mode 100644 index 000000000..34970fc17 --- /dev/null +++ b/tests/page-template-snippets/boomerangSnippetNoScript.tpl @@ -0,0 +1,213 @@ +/* eslint-disable no-script-url */ +(function() { + // Boomerang Loader Snippet version 15 + if (window.BOOMR && (window.BOOMR.version || window.BOOMR.snippetExecuted)) { + return; + } + + window.BOOMR = window.BOOMR || {}; + window.BOOMR.snippetStart = new Date().getTime(); + window.BOOMR.snippetExecuted = true; + window.BOOMR.snippetVersion = 15; + + // NOTE: Set Boomerang URL here + window.BOOMR.url = ""; + + // document.currentScript is supported in all browsers other than IE + var where = document.currentScript || document.getElementsByTagName("script")[0], + // Parent element of the script we inject + parentNode = where.parentNode, + // Whether or not Preload method has worked + promoted = false, + // How long to wait for Preload to work before falling back to iframe method + LOADER_TIMEOUT = 3000; + + /* BEGIN_TEST_CODE */ + var prefix, suffix; + + if (window.BOOMR_script_delay) { + prefix = "/delay?delay=3000&file=build/"; + suffix = "&rnd=" + Math.random(); + } + else { + prefix = "../../build/"; + suffix = ""; + } + + if (/\/support\//.test(window.location.pathname)) { + prefix = prefix.replace("build/", "../build/"); + } + + window.BOOMR.url = prefix + + (window.BOOMR_script_minified ? "boomerang-latest-debug.min.js" : "boomerang-latest-debug.js") + + suffix; + + /* END_TEST_CODE */ + // Tells the browser to execute the Preloaded script by adding it to the DOM + function promote() { + if (promoted) { + return; + } + + var script = document.createElement("script"); + + script.id = "boomr-scr-as"; + script.src = window.BOOMR.url; + + // Not really needed since dynamic scripts are async by default and the script is already in cache at this point, + // but some naive parsers will see a missing async attribute and think we're not async + script.async = true; + + parentNode.appendChild(script); + + promoted = true; + } + + // Non-blocking iframe loader (fallback for non-Preload scenarios) for all recent browsers. + // For IE 6/7/8, falls back to dynamic script node. + function iframeLoader(wasFallback) { + promoted = true; + + var dom, + doc = document, + bootstrap, iframe, iframeStyle, + win = window; + + window.BOOMR.snippetMethod = wasFallback ? "if" : "i"; + + // Adds Boomerang within the iframe + bootstrap = function(parent, scriptId) { + var script = doc.createElement("script"); + + script.id = scriptId || "boomr-if-as"; + script.src = window.BOOMR.url; + + BOOMR_lstart = new Date().getTime(); + + parent = parent || doc.body; + parent.appendChild(script); + }; + + // For IE 6/7/8, we'll just load the script in the current frame: + // * IE 6/7 don't support 'about:blank' for an iframe src (it triggers warnings on secure sites) + // * IE 8 required a doc write call for it to work, which is bad practice + // This means loading on IE 6/7/8 may cause SPoF. + if (!window.addEventListener && window.attachEvent && navigator.userAgent.match(/MSIE [678]\./)) { + window.BOOMR.snippetMethod = "s"; + + bootstrap(parentNode, "boomr-async"); + + return; + } + + // The rest of this function is for browsers that don't support Preload hints but will work with CSP & iframes + iframe = document.createElement("IFRAME"); + + // An empty frame + iframe.src = "about:blank"; + + // We set title and role appropriately to play nicely with screen readers and other assistive technologies + iframe.title = ""; + iframe.role = "presentation"; + + // Ensure we're not loaded lazily + iframe.loading = "eager"; + + // Hide the iframe + iframeStyle = (iframe.frameElement || iframe).style; + iframeStyle.width = 0; + iframeStyle.height = 0; + iframeStyle.border = 0; + iframeStyle.display = "none"; + + // Append to the end of the current block + parentNode.appendChild(iframe); + + // Try to get the iframe's document object + try { + win = iframe.contentWindow; + doc = win.document.open(); + } + catch (e) { + // document.domain has been changed and we're on an old version of IE, so we got an access denied. + // Note: the only browsers that have this problem also do not have CSP support. + + // Get document.domain of the parent window + dom = document.domain; + + // Set the src of the iframe to a JavaScript URL that will immediately set its document.domain + // to match the parent. + // This lets us access the iframe document long enough to inject our script. + // Our script may need to do more domain massaging later. + iframe.src = "javascript:var d=document.open();d.domain='" + dom + "';void 0;"; + win = iframe.contentWindow; + + doc = win.document.open(); + } + + // document.domain hasn't changed, regular method should be OK + win._boomrl = function() { + bootstrap(); + }; + + if (win.addEventListener) { + win.addEventListener("load", win._boomrl, false); + } + else if (win.attachEvent) { + win.attachEvent("onload", win._boomrl); + } + + // Finish the document + doc.close(); + } + + // See if Preload is supported or not + var link = document.createElement("link"); + + if (link.relList && + typeof link.relList.supports === "function" && + link.relList.supports("preload") && + ("as" in link)) { + window.BOOMR.snippetMethod = "p"; + + // Set attributes to trigger a Preload + link.href = window.BOOMR.url; + link.rel = "preload"; + link.as = "script"; + + // Add our script tag if successful, fallback to iframe if not + link.addEventListener("load", promote); + link.addEventListener("error", function() { + iframeLoader(true); + }); + + // Have a fallback in case Preload does nothing or is slow + setTimeout(function() { + if (!promoted) { + iframeLoader(true); + } + }, LOADER_TIMEOUT); + + // Note the timestamp we started trying to Preload + BOOMR_lstart = new Date().getTime(); + + // Append our link tag + parentNode.appendChild(link); + } + else { + // No Preload support, use iframe loader + iframeLoader(false); + } + + // Save when the onload event happened, in case this is a non-NavigationTiming browser + function boomerangSaveLoadTime(e) { + window.BOOMR_onload = (e && e.timeStamp) || new Date().getTime(); + } + + if (window.addEventListener) { + window.addEventListener("load", boomerangSaveLoadTime, false); + } + else if (window.attachEvent) { + window.attachEvent("onload", boomerangSaveLoadTime); + } +})(); diff --git a/tests/page-template-snippets/captureErrorsSnippet.tpl b/tests/page-template-snippets/captureErrorsSnippet.tpl new file mode 100644 index 000000000..7dfc7e4aa --- /dev/null +++ b/tests/page-template-snippets/captureErrorsSnippet.tpl @@ -0,0 +1,3 @@ + diff --git a/tests/page-template-snippets/captureErrorsSnippetNoScript.tpl b/tests/page-template-snippets/captureErrorsSnippetNoScript.tpl new file mode 100644 index 000000000..a6ffff566 --- /dev/null +++ b/tests/page-template-snippets/captureErrorsSnippetNoScript.tpl @@ -0,0 +1,62 @@ +(function(w){ + w.BOOMR = w.BOOMR || {}; + + w.BOOMR.globalOnErrorOrig = w.BOOMR.globalOnError = w.onerror; + w.BOOMR.globalErrors = []; + + // Gathers a high-resolution timestamp (when available), or falls back to Date.getTime() + var now = (function() { + try { + if ("performance" in w && w.performance.timing) { + return function() { + return Math.round(w.performance.now() + performance.timing.navigationStart); + }; + } + } + catch (ignore) { + // NOP + } + + return Date.now || function() { + return new Date().getTime(); + }; + })(); + + // Overwrite the global onerror to listen for errors, but forward all messages to the original one if it exists + w.onerror = function BOOMR_plugins_errors_onerror(message, fileName, lineNumber, columnNumber, error) { + if (w.BOOMR.version) { + // If Boomerang has already loaded, the only reason this function would still be alive would be if + // we're in the chain from another handler that overwrote window.onerror. In that case, we should + // run globalOnErrorOrig which presumably hasn't been overwritten by Boomerang. + if (typeof w.BOOMR.globalOnErrorOrig === "function") { + w.BOOMR.globalOnErrorOrig.apply(w, arguments); + } + + return; + } + + // Save this error for when Boomerang loads + if (typeof error !== "undefined" && error !== null) { + error.timestamp = now(); + w.BOOMR.globalErrors.push(error); + } + else { + w.BOOMR.globalErrors.push({ + message: message, + fileName: fileName, + lineNumber: lineNumber, + columnNumber: columnNumber, + noStack: true, + timestamp: now() + }); + } + + // Call the original window.onerror + if (typeof w.BOOMR.globalOnError === "function") { + w.BOOMR.globalOnError.apply(w, arguments); + } + }; + + // make it easier to detect this is our wrapped handler + w.onerror._bmr = true; +})(window); diff --git a/tests/page-template-snippets/consentInlinePlugin.tpl b/tests/page-template-snippets/consentInlinePlugin.tpl new file mode 100644 index 000000000..2d16eb8db --- /dev/null +++ b/tests/page-template-snippets/consentInlinePlugin.tpl @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/tests/page-template-snippets/consentInlinePluginNoScriptMin.tpl b/tests/page-template-snippets/consentInlinePluginNoScriptMin.tpl new file mode 100644 index 000000000..f31c84711 --- /dev/null +++ b/tests/page-template-snippets/consentInlinePluginNoScriptMin.tpl @@ -0,0 +1 @@ +!function(o){"use strict";if(void 0!==o.BOOMR_CONSENT_CONFIG&&!0===o.BOOMR_CONSENT_CONFIG.enabled){o.BOOMR=void 0!==o.BOOMR?o.BOOMR:{};var n=o.BOOMR;n.plugins=void 0!==n.plugins?n.plugins:{};if(!n.plugins.ConsentInlinedPlugin){var i={v:"2",OPT_COOKIE:"BOOMR_CONSENT",OPT_IN_COOKIE_VAL:"opted-in",OPT_OUT_COOKIE_VAL:"opted-out",COOKIE_EXP:31536e3,complete:!1,enabled:!0,firedPageReady:!1,deferredOptIn:!1,deferredOptOut:!1,rtCookieFromConfig:!1,bwCookieFromConfig:!1,optOut:function(){if(!n.utils.setCookie(i.OPT_COOKIE,i.OPT_OUT_COOKIE_VAL,i.COOKIE_EXP)){n.error("Can not set Opt Out cookie","ConsentInlinedPlugin");return!1}"function"==typeof n.disable&&n.disable();i.complete=!1;i.removeBoomerangCookies();return!0},optIn:function(){if(!0===i.complete)return!0;if(!n.utils.setCookie(i.OPT_COOKIE,i.OPT_IN_COOKIE_VAL,i.COOKIE_EXP)){n.error("Can not set Opt In value","ConsentInlinedPlugin");return!1}"function"==typeof n.wakeUp&&n.wakeUp();i.complete=!0;n.addVar("cip.in","1",!0);n.addVar("cip.v",i.v,!0);n.sendBeacon();return!0},removeBoomerangCookies:function(){var e=i.rtCookieFromConfig||"RT",o=i.bwCookieFromConfig||"BA";n.utils.removeCookie(e);n.utils.removeCookie(o)},onPageReady:function(){if(!i.firedPageReady){i.firedPageReady=!0;if(i.deferredOptIn){i.optIn();i.deferredOptIn=!1}if(i.deferredOptOut){i.optOut();i.deferredOptOut=!1}}}};o.BOOMR_OPT_OUT=function(){i.firedPageReady?i.optOut():i.deferredOptOut=!0};o.BOOMR_OPT_IN=function(){i.firedPageReady?i.optIn():i.deferredOptIn=!0};n.plugins.ConsentInlinedPlugin={init:function(e){void 0!==e.RT&&void 0!==e.RT.cookie&&(i.rtCookieFromConfig=e.RT.cookie);void 0!==e.BW&&void 0!==e.BW.cookie&&(i.bwCookieFromConfig=e.BW.cookie);n.subscribe("page_ready",i.onPageReady,null,i);n.subscribe("spa_navigation",i.onPageReady,null,i);if(o.BOOMR_CONSENT_CONFIG.optInRequired&&n.utils.getCookie(i.OPT_COOKIE)!==i.OPT_IN_COOKIE_VAL){i.complete=!1;return this}if(n.utils.getCookie(i.OPT_COOKIE)===i.OPT_OUT_COOKIE_VAL){void 0===e.RT&&(e.RT={});e.RT.cookie="";void 0===e.BW&&(e.BW={});e.BW.cookie="";i.complete=!1;return this}i.complete=!0;return this},is_complete:function(){return i.complete},debug:{wasPageReadyFired:function(){return i.firedPageReady},getDeferredOptInFlag:function(){return i.deferredOptIn},getDeferredOptOutFlag:function(){return i.deferredOptOut},getRtCookieFromConfig:function(){return i.rtCookieFromConfig},getBwCookieFromConfig:function(){return i.bwCookieFromConfig},isOptedIn:function(){return!(!o.BOOMR_CONSENT_CONFIG.optInRequired||-1===document.cookie.indexOf(i.OPT_COOKIE+'="'+i.OPT_IN_COOKIE_VAL+'"'))},isOptedOut:function(){return-1!==document.cookie.indexOf(i.OPT_COOKIE+'="'+i.OPT_OUT_COOKIE_VAL+'"')}}}}}}(window); \ No newline at end of file diff --git a/tests/page-template-snippets/continuitySnippet.tpl b/tests/page-template-snippets/continuitySnippet.tpl new file mode 100644 index 000000000..b5f191b22 --- /dev/null +++ b/tests/page-template-snippets/continuitySnippet.tpl @@ -0,0 +1,3 @@ + diff --git a/tests/page-template-snippets/continuitySnippetNoScript.tpl b/tests/page-template-snippets/continuitySnippetNoScript.tpl new file mode 100644 index 000000000..1a13e7106 --- /dev/null +++ b/tests/page-template-snippets/continuitySnippetNoScript.tpl @@ -0,0 +1,22 @@ +(function() { + if (window && window.requestAnimationFrame) { + window.BOOMR = window.BOOMR || {}; + window.BOOMR.fpsLog = []; + + function frame(now) { + // window.BOOMR.fpsLog will get deleted once Boomerang has loaded + if (window.BOOMR.fpsLog) { + window.BOOMR.fpsLog.push(Math.round(now)); + + // if we've added more than 30 seconds of data, stop + if (window.BOOMR.fpsLog.length > 30 * 60) { + return; + } + + window.requestAnimationFrame(frame); + } + } + + window.requestAnimationFrame(frame); + } +})(); diff --git a/tests/page-template-snippets/disableSendBeacon.tpl b/tests/page-template-snippets/disableSendBeacon.tpl new file mode 100644 index 000000000..b2f691de7 --- /dev/null +++ b/tests/page-template-snippets/disableSendBeacon.tpl @@ -0,0 +1,7 @@ + diff --git a/tests/page-template-snippets/footer.tpl b/tests/page-template-snippets/footer.tpl new file mode 100644 index 000000000..308b1d01b --- /dev/null +++ b/tests/page-template-snippets/footer.tpl @@ -0,0 +1,2 @@ + + diff --git a/tests/page-template-snippets/header.tpl b/tests/page-template-snippets/header.tpl new file mode 100644 index 000000000..84579934f --- /dev/null +++ b/tests/page-template-snippets/header.tpl @@ -0,0 +1,43 @@ + + + + + + + Boomerang Test <%= fileName %> + + + + + + + + + + + + +
+ + diff --git a/tests/page-template-snippets/headerPerf.tpl b/tests/page-template-snippets/headerPerf.tpl new file mode 100644 index 000000000..1513aec04 --- /dev/null +++ b/tests/page-template-snippets/headerPerf.tpl @@ -0,0 +1,8 @@ + + + + Boomerang Performance Test <%= fileName %> + + + + diff --git a/tests/page-template-snippets/instrumentXHRSnippet.tpl b/tests/page-template-snippets/instrumentXHRSnippet.tpl new file mode 100644 index 000000000..833a27faa --- /dev/null +++ b/tests/page-template-snippets/instrumentXHRSnippet.tpl @@ -0,0 +1,3 @@ + diff --git a/tests/page-template-snippets/instrumentXHRSnippetNoScript.tpl b/tests/page-template-snippets/instrumentXHRSnippetNoScript.tpl new file mode 100644 index 000000000..9be983c35 --- /dev/null +++ b/tests/page-template-snippets/instrumentXHRSnippetNoScript.tpl @@ -0,0 +1,160 @@ +(function(w){ + if (!w.XMLHttpRequest || !(new w.XMLHttpRequest()).addEventListener) { + return; + } + + var a = document.createElement("A"), + xhrNative = w.XMLHttpRequest, + resources = [], + sendResource, + readyStateMap = ["uninitialized", "open", "responseStart", "domInteractive", "responseEnd"]; + + w.BOOMR = w.BOOMR || {}; + + // xhr object is what the AutoXHR plugin will interact with once it's loaded + BOOMR.xhr = { + /** + * Stops XHR collection and forwards any additional reporting to Boomerang + * + * @param {function} sendResourceCallback Callback for any ongoing monitoring + * + * @returns {object} Array of XHRs + */ + stop: function(sendResourceCallback) { + sendResource = sendResourceCallback; + + // swap back in the native XHR function + w.XMLHttpRequest = xhrNative; + + delete BOOMR.xhr; + + // clear our queue after a moment + setTimeout(function(){ + resources = []; + }, 10); + + return resources; + } + }; + + // Gathers a high-resolution timestamp (when available), or falls back to Date.getTime() + var now = (function() { + try { + if ("performance" in w && w.performance.timing) { + return function() { + return Math.round(w.performance.now() + performance.timing.navigationStart); + }; + } + } + catch (ignore) { + // NOP + } + + return Date.now || function() { + return new Date().getTime(); + }; + })(); + + // Overwrite the native XHR with our monitored object + w.XMLHttpRequest = function() { + var xhr = new xhrNative(), + open = xhr.open; + + xhr.open = function(method, url, async) { + // Normalize the URL + a.href = url; + + var resource = { + timing: {}, + url: a.href, + method: method + }; + + // Callback when the XHR is finished + function loadFinished() { + if (!resource.timing.loadEventEnd) { + resource.timing.loadEventEnd = now(); + + if ("performance" in w && w.performance && typeof w.performance.getEntriesByName === "function") { + var entries = w.performance.getEntriesByName(resource.url); + + var entry = entries && entries.length && entries[entries.length - 1]; + + if (entry) { + var navSt = w.performance.timing.navigationStart; + + if (entry.responseEnd !== 0) { + resource.timing.responseEnd = Math.round(navSt + entry.responseEnd); + } + + if (entry.responseStart !== 0) { + resource.timing.responseStart = Math.round(navSt + entry.responseStart); + } + + if (entry.startTime !== 0) { + resource.timing.requestStart = Math.round(navSt + entry.startTime); + } + } + } + + if (sendResource) { + sendResource(resource); + } + else { + resources.push(resource); + } + } + } + + function addListener(ename, stat) { + xhr.addEventListener( + ename, + function() { + if (ename === "readystatechange") { + resource.timing[readyStateMap[xhr.readyState]] = now(); + + if (xhr.readyState === 4) { + loadFinished(); + } + } + else { + resource.status = (stat === undefined ? xhr.status : stat); + loadFinished(); + } + }, + false); + } + + if (async === true) { + addListener("readystatechange"); + } + else { + resource.synchronous = true; + } + + addListener("load"); + addListener("timeout", -1001); + addListener("error", -998); + addListener("abort", -999); + + try { + open.apply(xhr, arguments); + + var send = xhr.send; + + xhr.send = function() { + resource.timing.requestStart = now(); + + send.apply(xhr, arguments); + }; + } + catch (e) { + resource.status = -997; + + loadFinished(); + } + }; + + return xhr; + }; +})(window); diff --git a/tests/page-templates/00-basic/00-onload.html b/tests/page-templates/00-basic/00-onload.html new file mode 100644 index 000000000..f22bf2a7e --- /dev/null +++ b/tests/page-templates/00-basic/00-onload.html @@ -0,0 +1,13 @@ +<%= header %> +<%= boomerangSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/00-basic/00-onload.js b/tests/page-templates/00-basic/00-onload.js new file mode 100644 index 000000000..4cfb83b29 --- /dev/null +++ b/tests/page-templates/00-basic/00-onload.js @@ -0,0 +1,94 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/00-basic/00-onload", function() { + var tf = BOOMR.plugins.TestFramework; + + it("Should have sent a beacon", function() { + // ensure we fired a beacon ('beacon') + assert.isTrue(tf.fired_onbeacon); + }); + + it("Should have fired 'beacon' with a beacon payload", function() { + // ensure the data was sent to 'beacon' + assert.isObject(tf.lastBeacon()); + }); + + it("Should have set basic beacon properties", function() { + assert.isString(tf.lastBeacon().v); + }); + + it("Should have set dom.* properties", function() { + assert.isNumber(tf.lastBeacon()["dom.img"], "dom.img"); + assert.isNumber(tf.lastBeacon()["dom.ln"], "dom.ln"); + assert.isNumber(tf.lastBeacon()["dom.script"], "dom.script"); + assert.isNumber(tf.lastBeacon()["dom.sz"], "dom.sz"); + + if (BOOMR_test.isResourceTimingSupported()) { + assert.isNumber(tf.lastBeacon()["dom.doms"], "dom.doms"); + assert.isNumber(tf.lastBeacon()["dom.res"], "dom.res"); + } + }); + + it("Should have set mem.* properties", function() { + if ((window.performance && window.performance.memory) || + (window.console && window.console.memory)) { + assert.isNumber(tf.lastBeacon()["mem.total"], "mem.total"); + assert.isNumber(tf.lastBeacon()["mem.used"], "mem.used"); + + // Might not exist except recent builds + if (tf.lastBeacon()["mem.limit"]) { + assert.isNumber(tf.lastBeacon()["mem.limit"], "mem.limit"); + } + } + }); + + it("Should have set RT properties", function() { + assert.isString(tf.lastBeacon().u, "u"); + + assert.isNumber(tf.lastBeacon()["rt.bstart"], "rt.bstart"); + assert.isNumber(tf.lastBeacon()["rt.end"], "rt.end"); + assert.isString(tf.lastBeacon()["rt.si"], "rt.si"); + assert.isNumber(tf.lastBeacon()["rt.sl"], "rt.sl"); + assert.isNumber(tf.lastBeacon()["rt.ss"], "rt.ss"); + assert.isString(tf.lastBeacon()["rt.start"], "rt.start"); + + // optional + if (typeof tf.lastBeacon()["rt.tstart"] !== "undefined") { + assert.isNumber(tf.lastBeacon()["rt.tstart"], "rt.tstart"); + } + }); + + it("Should have set scr.* properties", function() { + var s = window.screen; + + assert.isString(tf.lastBeacon()["scr.bpp"], "scr.bpp"); + assert.isString(tf.lastBeacon()["scr.xy"], "scr.xy"); + + // only if we have orientation + if (s && s.orientation) { + assert.isString(tf.lastBeacon()["scr.orn"], "scr.orn"); + } + + // only if we have pixel ratio + if (window.devicePixelRatio && window.devicePixelRatio > 1) { + assert.isNumber(tf.lastBeacon()["scr.dpx"], "scr.dpx"); + } + }); + + it("Should have set LOGN / SOASTA properties", function() { + assert.isString(tf.lastBeacon()["h.key"], "h.key"); + }); + + it("Should have set vis.* properties", function() { + assert.isString(tf.lastBeacon()["vis.st"], "vis.st"); + }); + + it("Should have set Page ID (pid)", function() { + assert.isString(tf.lastBeacon().pid, "pid"); + }); + + it("Should have not set nocookie", function() { + assert.isUndefined(tf.lastBeacon().nocookie); + }); +}); diff --git a/tests/page-templates/00-basic/01-onunload.html b/tests/page-templates/00-basic/01-onunload.html new file mode 100644 index 000000000..af774a9ba --- /dev/null +++ b/tests/page-templates/00-basic/01-onunload.html @@ -0,0 +1,14 @@ +<%= header %> +<%= boomerangScript %> + + + +<%= footer %> diff --git a/tests/page-templates/00-basic/01-onunload.js b/tests/page-templates/00-basic/01-onunload.js new file mode 100644 index 000000000..4259e0cf6 --- /dev/null +++ b/tests/page-templates/00-basic/01-onunload.js @@ -0,0 +1,23 @@ +/* eslint-env mocha */ +/* global assert */ + +describe("e2e/00-basic/01-onunload", function() { + var beaconData; + + it("Should have sent an unload beacon", function(done) { + var unloadBeaconHandler = function(data) { + beaconData = data; + assert.isString(beaconData["rt.quit"]); + done(); + }; + + var testFrame = document.getElementById("boomer_test_frame"); + + testFrame.contentWindow.BOOMR.subscribe("beacon", unloadBeaconHandler, null, this); + testFrame.src = "about:blank"; + }); + + it("Should have run all unload plugins for the unload beacon", function() { + assert.equal(beaconData.onunloadtest, 1); + }); +}); diff --git a/tests/page-templates/00-basic/02-events.html b/tests/page-templates/00-basic/02-events.html new file mode 100644 index 000000000..71fad8154 --- /dev/null +++ b/tests/page-templates/00-basic/02-events.html @@ -0,0 +1,18 @@ +<%= header %> +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/00-basic/02-events.js b/tests/page-templates/00-basic/02-events.js new file mode 100644 index 000000000..2f877e5d8 --- /dev/null +++ b/tests/page-templates/00-basic/02-events.js @@ -0,0 +1,13 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["myevent"]); + +describe("e2e/00-basic/02-events", function() { + var tf = BOOMR.plugins.TestFramework; + + it("Should have fired myevent with the correct data", function() { + assert.equal("a", window.myevent); + }); +}); diff --git a/tests/page-templates/00-basic/03-referrer.html b/tests/page-templates/00-basic/03-referrer.html new file mode 100644 index 000000000..35ec1673d --- /dev/null +++ b/tests/page-templates/00-basic/03-referrer.html @@ -0,0 +1,21 @@ +<%= header %> +<%= boomerangScript %> + + + +<%= footer %> diff --git a/tests/page-templates/00-basic/03-referrer.js b/tests/page-templates/00-basic/03-referrer.js new file mode 100644 index 000000000..2e3f79195 --- /dev/null +++ b/tests/page-templates/00-basic/03-referrer.js @@ -0,0 +1,14 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["beaconHandler", "testReferrer", "lastReferrer"]); + +describe("e2e/00-basic/03-referrer", function() { + var tf = BOOMR.plugins.TestFramework; + + it("The referrer should have been set to this window's location", function() { + // ensure there was a referrer on the IFRAME beacon + chai.assert.equal(window.lastReferrer, window.location.href); + }); +}); diff --git a/tests/page-templates/00-basic/05-responseend.html b/tests/page-templates/00-basic/05-responseend.html new file mode 100644 index 000000000..2d9389ef2 --- /dev/null +++ b/tests/page-templates/00-basic/05-responseend.html @@ -0,0 +1,13 @@ +<%= header %> +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/00-basic/05-responseend.js b/tests/page-templates/00-basic/05-responseend.js new file mode 100644 index 000000000..556128c46 --- /dev/null +++ b/tests/page-templates/00-basic/05-responseend.js @@ -0,0 +1,52 @@ +/* eslint-env mocha */ +/* global assert */ + +describe("e2e/00-basic/05-responseend", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should have sent two beacons", function(done) { + t.ensureBeaconCount(done, 2); + }); + + it("Should have the first beacon be a page load beacon", function() { + if (t.isNavigationTimingSupported()) { + assert.equal(tf.beacons[0]["rt.start"], "navigation"); + } + else { + assert.equal(tf.beacons[0]["rt.start"], "none"); + } + }); + + it("Should have the first beacon not have a http.initiator", function() { + assert.isUndefined(tf.beacons[0]["http.initiator"]); + }); + + it("Should have the first beacon have the URL of the page", function() { + assert.equal(tf.beacons[0].u, window.location.href); + }); + + it("Should have the second beacon be a manual beacon", function() { + assert.equal(tf.beacons[1]["rt.start"], "manual"); + }); + + it("Should have the second beacon be have the specified page group in xhr.pg", function() { + assert.equal(tf.beacons[1]["xhr.pg"], "foo"); + }); + + it("Should have the second beacon have a t_done of 0", function() { + assert.closeTo(tf.beacons[1].t_done, 0, 1); + }); + + it("Should have the second beacon have a rt.tstart", function() { + assert.operator(tf.beacons[1]["rt.tstart"], ">", 0); + }); + + it("Should have the second beacon have a rt.end", function() { + assert.operator(tf.beacons[1]["rt.end"], ">", 0); + }); + + it("Should have the second beacon have a rt.tstart and rt.end be equal", function() { + assert.closeTo(tf.beacons[1]["rt.tstart"], tf.beacons[1]["rt.end"], 1); + }); +}); diff --git a/tests/page-templates/00-basic/06-responseend-before-pageload-beacon.html b/tests/page-templates/00-basic/06-responseend-before-pageload-beacon.html new file mode 100644 index 000000000..a8e80574b --- /dev/null +++ b/tests/page-templates/00-basic/06-responseend-before-pageload-beacon.html @@ -0,0 +1,20 @@ +<%= header %> +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/00-basic/06-responseend-before-pageload-beacon.js b/tests/page-templates/00-basic/06-responseend-before-pageload-beacon.js new file mode 100644 index 000000000..8b6584f03 --- /dev/null +++ b/tests/page-templates/00-basic/06-responseend-before-pageload-beacon.js @@ -0,0 +1,52 @@ +/* eslint-env mocha */ +/* global assert */ + +describe("e2e/00-basic/06-responseend-before-pageload-beacon", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should have sent two beacons", function(done) { + t.ensureBeaconCount(done, 2); + }); + + it("Should have the first beacon be a page load beacon", function() { + if (t.isNavigationTimingSupported()) { + assert.equal(tf.beacons[0]["rt.start"], "navigation"); + } + else { + assert.equal(tf.beacons[0]["rt.start"], "none"); + } + }); + + it("Should have the first beacon not have a http.initiator", function() { + assert.isUndefined(tf.beacons[0]["http.initiator"]); + }); + + it("Should have the first beacon have the URL of the page", function() { + assert.equal(tf.beacons[0].u, window.location.href); + }); + + it("Should have the second beacon be a manual beacon", function() { + assert.equal(tf.beacons[1]["rt.start"], "manual"); + }); + + it("Should have the second beacon be have the specified page group in xhr.pg", function() { + assert.equal(tf.beacons[1]["xhr.pg"], "foo"); + }); + + it("Should have the second beacon have a t_done of 0", function() { + assert.closeTo(tf.beacons[1].t_done, 0, 1); + }); + + it("Should have the second beacon have a rt.tstart", function() { + assert.operator(tf.beacons[1]["rt.tstart"], ">", 0); + }); + + it("Should have the second beacon have a rt.end", function() { + assert.operator(tf.beacons[1]["rt.end"], ">", 0); + }); + + it("Should have the second beacon have a rt.tstart and rt.end be equal", function() { + assert.closeTo(tf.beacons[1]["rt.tstart"], tf.beacons[1]["rt.end"], 1); + }); +}); diff --git a/tests/page-templates/00-basic/07-responseend-with-start.html b/tests/page-templates/00-basic/07-responseend-with-start.html new file mode 100644 index 000000000..f19561691 --- /dev/null +++ b/tests/page-templates/00-basic/07-responseend-with-start.html @@ -0,0 +1,13 @@ +<%= header %> +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/00-basic/07-responseend-with-start.js b/tests/page-templates/00-basic/07-responseend-with-start.js new file mode 100644 index 000000000..c26d005ae --- /dev/null +++ b/tests/page-templates/00-basic/07-responseend-with-start.js @@ -0,0 +1,52 @@ +/* eslint-env mocha */ +/* global assert */ + +describe("e2e/00-basic/07-responseend-with-start", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should have sent two beacons", function(done) { + t.ensureBeaconCount(done, 2); + }); + + it("Should have the first beacon be a page load beacon", function() { + if (t.isNavigationTimingSupported()) { + assert.equal(tf.beacons[0]["rt.start"], "navigation"); + } + else { + assert.equal(tf.beacons[0]["rt.start"], "none"); + } + }); + + it("Should have the first beacon not have a http.initiator", function() { + assert.isUndefined(tf.beacons[0]["http.initiator"]); + }); + + it("Should have the first beacon have the URL of the page", function() { + assert.equal(tf.beacons[0].u, window.location.href); + }); + + it("Should have the second beacon be a manual beacon", function() { + assert.equal(tf.beacons[1]["rt.start"], "manual"); + }); + + it("Should have the second beacon be have the specified page group in xhr.pg", function() { + assert.equal(tf.beacons[1]["xhr.pg"], "foo"); + }); + + it("Should have the second beacon have a t_done of ~10", function() { + assert.closeTo(tf.beacons[1].t_done, 10, 2); + }); + + it("Should have the second beacon have a rt.tstart", function() { + assert.operator(tf.beacons[1]["rt.tstart"], ">", 0); + }); + + it("Should have the second beacon have a rt.end", function() { + assert.operator(tf.beacons[1]["rt.end"], ">", 0); + }); + + it("Should have the second beacon have a rt.tstart and rt.end difference close to 10", function() { + assert.closeTo(parseInt(tf.beacons[1]["rt.end"], 10) - parseInt(tf.beacons[1]["rt.tstart"], 10), 10, 2); + }); +}); diff --git a/tests/page-templates/00-basic/08-responseend-with-end.html b/tests/page-templates/00-basic/08-responseend-with-end.html new file mode 100644 index 000000000..b2aa68b2a --- /dev/null +++ b/tests/page-templates/00-basic/08-responseend-with-end.html @@ -0,0 +1,16 @@ +<%= header %> +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/00-basic/08-responseend-with-end.js b/tests/page-templates/00-basic/08-responseend-with-end.js new file mode 100644 index 000000000..b6ffe25bc --- /dev/null +++ b/tests/page-templates/00-basic/08-responseend-with-end.js @@ -0,0 +1,52 @@ +/* eslint-env mocha */ +/* global assert */ + +describe("e2e/00-basic/08-responseend-with-end", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should have sent two beacons", function(done) { + t.ensureBeaconCount(done, 2); + }); + + it("Should have the first beacon be a page load beacon", function() { + if (t.isNavigationTimingSupported()) { + assert.equal(tf.beacons[0]["rt.start"], "navigation"); + } + else { + assert.equal(tf.beacons[0]["rt.start"], "none"); + } + }); + + it("Should have the first beacon not have a http.initiator", function() { + assert.isUndefined(tf.beacons[0]["http.initiator"]); + }); + + it("Should have the first beacon have the URL of the page", function() { + assert.equal(tf.beacons[0].u, window.location.href); + }); + + it("Should have the second beacon be a manual beacon", function() { + assert.equal(tf.beacons[1]["rt.start"], "manual"); + }); + + it("Should have the second beacon be have the specified page group in xhr.pg", function() { + assert.equal(tf.beacons[1]["xhr.pg"], "foo"); + }); + + it("Should have the second beacon have a t_done of 1000", function() { + assert.equal(tf.beacons[1].t_done, 1000); + }); + + it("Should have the second beacon have a rt.tstart", function() { + assert.operator(tf.beacons[1]["rt.tstart"], ">", 0); + }); + + it("Should have the second beacon have a rt.end", function() { + assert.operator(tf.beacons[1]["rt.end"], ">", 0); + }); + + it("Should have the second beacon have a rt.tstart and rt.end difference equal to 1000", function() { + assert.equal(parseInt(tf.beacons[1]["rt.end"], 10) - parseInt(tf.beacons[1]["rt.tstart"], 10), 1000); + }); +}); diff --git a/tests/page-templates/00-basic/09-overrides.html b/tests/page-templates/00-basic/09-overrides.html new file mode 100644 index 000000000..cf8555d07 --- /dev/null +++ b/tests/page-templates/00-basic/09-overrides.html @@ -0,0 +1,21 @@ +<%= header %> +<%= boomerangSnippet %> + + + + + +<%= footer %> diff --git a/tests/page-templates/00-basic/09-overrides.js b/tests/page-templates/00-basic/09-overrides.js new file mode 100644 index 000000000..3f0585b41 --- /dev/null +++ b/tests/page-templates/00-basic/09-overrides.js @@ -0,0 +1,156 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/00-basic/09-overrides", function() { + var tf = BOOMR.plugins.TestFramework; + var windowUnderTest = window; + + // overrides which are commonly seen in browsers or extensions + var KNOWN_OVERRIDES = [ + "webkitRTCPeerConnection.prototype.setConfiguration", // Chrome + "RTCPeerConnection.prototype.setConfiguration", // Chrome + "WebAssembly.Module", // Edge + "WebAssembly.Instance" // Edge + ]; + + function cleanOverridesList(overrides) { + var result = []; + + for (var i = 0; i < overrides.length; i++) { + if (overrides[i].indexOf("content.") === 0) { + // seen in Firefox + continue; + } + + if (KNOWN_OVERRIDES.indexOf(overrides[i]) === -1) { + result.push(overrides[i]); + } + } + + return result; + } + + it("Should have sent a beacon", function() { + assert.isTrue(tf.fired_onbeacon); + }); + + var hasGetOwnPropertyName = Object.getOwnPropertyNames; + var hasReadyState = document.hasOwnProperty && !document.hasOwnProperty("readyState"); + var hasDocumentAll = !((typeof document.all) !== "function" && (typeof document.all) === "function"); + + // if window is already hijacked (phantomjs, i'm looking at you), punt + describe("clean window", function() { + it("Should return an empty array", function() { + if (!hasGetOwnPropertyName || !hasDocumentAll) { + return this.skip(); + } + + var overrides = cleanOverridesList(BOOMR.checkWindowOverrides(windowUnderTest)); + + assert.isTrue(BOOMR.utils.isArray(overrides)); + assert.lengthOf(overrides, 0); + }); + }); + + describe("window with overrides", function() { + var _ = {}; + var testMethods = [ + "EventTarget.prototype.addEventListener", + "XMLHttpRequest.prototype.open" + ]; + + var validTestMethods = []; + + before(function() { + if (!hasGetOwnPropertyName || !hasDocumentAll || !Object.defineProperty) { + return; + } + + BOOMR.utils.forEach(testMethods, function(method) { + try { + _[method] = eval(method); // eslint-disable-line no-eval + eval(method + " = function() {};"); // eslint-disable-line no-eval + validTestMethods.push(method); + } + catch (e) { + // ignore + } + }); + }); + + after(function() { + if (!hasGetOwnPropertyName || !hasDocumentAll || !Object.defineProperty) { + return; + } + + BOOMR.utils.forEach(validTestMethods, function(method) { + eval(method + " = _[method]"); // eslint-disable-line no-eval + }); + }); + + it("Should identify non-native methods found starting at `window`", function() { + if (!hasGetOwnPropertyName || !hasDocumentAll || !Object.defineProperty) { + return this.skip(); + } + + var overrides = cleanOverridesList(BOOMR.checkWindowOverrides(windowUnderTest)); + + assert.isTrue(BOOMR.utils.isArray(overrides)); + assert.lengthOf(overrides, validTestMethods.length); + assert.includeMembers(overrides, validTestMethods); + }); + }); + + // if document is already hijacked (phantomjs, i'm looking at you), punt + describe("clean document", function() { + it("Should return an empty array", function() { + if (!hasGetOwnPropertyName || !hasReadyState) { + return this.skip(); + } + + var overrides = BOOMR.checkDocumentOverrides(document); + + assert.isTrue(BOOMR.utils.isArray(overrides)); + assert.lengthOf(overrides, 0); + }); + }); + + describe("document with overrides", function() { + var _ = {}; + + before(function() { + if (!hasGetOwnPropertyName || !hasReadyState) { + return; + } + + BOOMR.utils.forEach(["readyState", "domain", "hidden", "URL", "cookie"], function(prop) { + _[prop] = document[prop]; + Object.defineProperty(document, prop, { + value: "foo" + }); + }); + }); + + after(function() { + if (!hasGetOwnPropertyName || !hasReadyState) { + return; + } + + BOOMR.utils.forEach(Object.keys(_), function(prop) { + document[prop] = _[prop]; + }); + }); + + it("Should identify non-native properties on `document`", function() { + if (!hasGetOwnPropertyName || !hasReadyState) { + return this.skip(); + } + + var overrides = BOOMR.checkDocumentOverrides(document); + + assert.isTrue(BOOMR.utils.isArray(overrides)); + assert.lengthOf(overrides, Object.keys(_).length); + assert.includeMembers(overrides, Object.keys(_)); + }); + }); +}); diff --git a/tests/page-templates/00-basic/10-method-queue.html b/tests/page-templates/00-basic/10-method-queue.html new file mode 100644 index 000000000..90f0811a3 --- /dev/null +++ b/tests/page-templates/00-basic/10-method-queue.html @@ -0,0 +1,22 @@ +<%= header %> + +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/00-basic/10-method-queue.js b/tests/page-templates/00-basic/10-method-queue.js new file mode 100644 index 000000000..8b725752f --- /dev/null +++ b/tests/page-templates/00-basic/10-method-queue.js @@ -0,0 +1,24 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/00-basic/10-method-queue", function() { + var tf = BOOMR.plugins.TestFramework; + + it("Should have sent a beacon", function() { + assert.isTrue(tf.fired_onbeacon); + }); + + it("Should support entries queued up before boomerang loaded", function() { + var b = tf.lastBeacon(); + + assert.equal(b.var1, "value1"); + assert.equal(b.var2, "value2"); + }); + + it("Should support calls to `push()` after boomerang loaded", function() { + var b = tf.lastBeacon(); + + assert.equal(b.var3, "value3"); + assert.equal(b.var4, "value4"); + }); +}); diff --git a/tests/page-templates/00-basic/11-local-domains.html b/tests/page-templates/00-basic/11-local-domains.html new file mode 100644 index 000000000..91aac618f --- /dev/null +++ b/tests/page-templates/00-basic/11-local-domains.html @@ -0,0 +1,28 @@ +<%= header %> +<%= boomerangSnippet %> + + + +
  Why is this test here?
+  ======================
+  
+  Some tests require us to have a proper domain and port assigned and requesting resources from those domains+ports 
+  as this is the only way we can ensure a cookie is created and session details can be validated. 
+
+  In this set of tests we ensure that both the port and domain are setup as expected in our environment.
+
+ + + +<%= footer %> diff --git a/tests/page-templates/00-basic/11-local-domains.js b/tests/page-templates/00-basic/11-local-domains.js new file mode 100644 index 000000000..df7668b0a --- /dev/null +++ b/tests/page-templates/00-basic/11-local-domains.js @@ -0,0 +1,47 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,describe,it */ + +/* + Why is this test here? + ====================== + + Some tests require us to have a proper domain and port assigned and requesting resources from those domains+ports + as this is the only way we can ensure a cookie is created and session details can be validated. + + In this set of tests we ensure that both the port and domain are setup as expected in our environment. + */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["mainServer", "secondaryServer", "mainPort", "secondaryPort"]); + +describe("e2e/00-basic/11-local-domains", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have successfully requested a resource from it's main server domain (--main-domain in grunt)", function(done){ + var xhr1 = new XMLHttpRequest(); + + xhr1.open("GET", "//" + window.mainServer + ":" + (window.mainPort) + "/pages/00-basic/support/generic.html"); + xhr1.send(null); + + xhr1.addEventListener("load", function() { + if (xhr1.readyState === XMLHttpRequest.DONE) { + done(); + } + }); + }); + + it("Should have successfully requested a resource from it's secondary server domain (--secondary-domain in grunt)", function(done){ + var xhr2 = new XMLHttpRequest(); + + xhr2.open("GET", "//" + window.secondaryServer + ":" + (window.secondaryPort) + "/pages/00-basic/support/generic.html"); + xhr2.send(null); + + xhr2.addEventListener("load", function() { + if (xhr2.readyState === XMLHttpRequest.DONE) { + done(); + } + }); + }); +}); + diff --git a/tests/page-templates/00-basic/12-addvar.html b/tests/page-templates/00-basic/12-addvar.html new file mode 100644 index 000000000..674f8638d --- /dev/null +++ b/tests/page-templates/00-basic/12-addvar.html @@ -0,0 +1,54 @@ +<%= header %> +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/00-basic/12-addvar.js b/tests/page-templates/00-basic/12-addvar.js new file mode 100644 index 000000000..8e96d9875 --- /dev/null +++ b/tests/page-templates/00-basic/12-addvar.js @@ -0,0 +1,351 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/00-basic/12-addvar", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + function getBeaconDataByName(name, url) { + var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"); + var results = regex.exec(url); + + if (!results) { + return; + } + + if (!results[2]) { + return ""; + } + + return decodeURIComponent(results[2].replace(/\+/g, " ")); + } + + function getBeaconData(n, param) { + // get all ResourceTiming entries + var entries = window.performance.getEntriesByType("resource"); + + // filter to just beacons + entries = BOOMR.utils.arrayFilter(entries, function(e) { + return e.name.indexOf(BOOMR_test.BEACON_URL) !== -1; + }); + + if (!entries[n]) { + return; + } + + return getBeaconDataByName(param, entries[n].name); + } + + it("Should have sent 3 beacons", function(done) { + t.ensureBeaconCount(done, 3); + }); + + // + // Tests of impl.vars (internal data) + // + describe("impl.vars data", function() { + it("Should have added var1 = 1 to impl.vars on all beacons", function() { + for (var i = 0; i < tf.beacons.length; i++) { + var b = tf.beacons[i]; + + assert.strictEqual(b.var1, 1); + } + }); + + it("Should have added var2 = abc to impl.vars on all beacons", function() { + for (var i = 0; i < tf.beacons.length; i++) { + var b = tf.beacons[i]; + + assert.strictEqual(b.var2, "abc"); + } + }); + + it("Should have added var3 = 10 to impl.vars on all beacons", function() { + for (var i = 0; i < tf.beacons.length; i++) { + var b = tf.beacons[i]; + + assert.strictEqual(b.var3, 0); + } + }); + + it("Should have added var4 = undefined to impl.vars on all beacons", function() { + for (var i = 0; i < tf.beacons.length; i++) { + var b = tf.beacons[i]; + + assert.isUndefined(b.var4); + } + }); + + it("Should have added var5 = undefined to impl.vars on all beacons", function() { + for (var i = 0; i < tf.beacons.length; i++) { + var b = tf.beacons[i]; + + assert.isUndefined(b.var5, ""); + } + }); + + it("Should have added var6 = null to impl.vars on all beacons", function() { + for (var i = 0; i < tf.beacons.length; i++) { + var b = tf.beacons[i]; + + assert.strictEqual(b.var6, null); + } + }); + + it("Should have added var7 = '' to impl.vars on all beacons", function() { + for (var i = 0; i < tf.beacons.length; i++) { + var b = tf.beacons[i]; + + assert.strictEqual(b.var7, ""); + } + }); + + it("Should have added var8 = { a: 1 } to impl.vars on all beacons", function() { + for (var i = 0; i < tf.beacons.length; i++) { + var b = tf.beacons[i]; + + assert.deepEqual(b.var8, { a: 1}); + } + }); + + it("Should have added var9 = 1.1111111 to impl.vars on all beacons", function() { + for (var i = 0; i < tf.beacons.length; i++) { + var b = tf.beacons[i]; + + assert.strictEqual(b.var9, 1.1111111); + } + }); + + it("Should have added var10 = 2 to impl.vars on all beacons", function() { + for (var i = 0; i < tf.beacons.length; i++) { + var b = tf.beacons[i]; + + assert.strictEqual(b.var10, 2); + } + }); + + it("Should have added var11 = single on the first beacon", function() { + var b = tf.beacons[0]; + + assert.strictEqual(b.var11, "single"); + }); + + it("Should not have added var11 = single on the second beacon", function() { + var b = tf.beacons[1]; + + assert.isUndefined(b.var11); + }); + + it("Should not have added var11 = single on the third beacon", function() { + var b = tf.beacons[1]; + + assert.isUndefined(b.var11); + }); + + it("Should not have added var12 to impl.vars on all beacons", function() { + for (var i = 0; i < tf.beacons.length; i++) { + var b = tf.beacons[i]; + + assert.isUndefined(b.var12); + } + }); + + it("Should have added var13 = 1 to impl.vars on all beacons", function() { + for (var i = 0; i < tf.beacons.length; i++) { + var b = tf.beacons[i]; + + assert.strictEqual(b.var13, 1); + } + }); + + it("Should have added var14 = 2 to impl.vars on all beacons", function() { + for (var i = 0; i < tf.beacons.length; i++) { + var b = tf.beacons[i]; + + assert.strictEqual(b.var14, 2); + } + }); + + it("Should not have added var15 to impl.vars on all beacons", function() { + for (var i = 0; i < tf.beacons.length; i++) { + var b = tf.beacons[i]; + + assert.isUndefined(b.var15); + } + }); + }); + + // + // Tests of what strings end up on the beacon URL + // + describe("Beacon URL data", function() { + it("Should have set var1 = 1 in the beacon data on all beacons", function() { + if (!t.isResourceTimingSupported()) { + return this.skip(); + } + + for (var i = 0; i < tf.beacons.length; i++) { + assert.strictEqual(getBeaconData(i, "var1"), "1"); + } + }); + + it("Should have set var2 = abc in the beacon data on all beacons", function() { + if (!t.isResourceTimingSupported()) { + return this.skip(); + } + + for (var i = 0; i < tf.beacons.length; i++) { + assert.strictEqual(getBeaconData(i, "var2"), "abc"); + } + }); + + it("Should have set var3 = 10 in the beacon data on all beacons", function() { + if (!t.isResourceTimingSupported()) { + return this.skip(); + } + + for (var i = 0; i < tf.beacons.length; i++) { + assert.strictEqual(getBeaconData(i, "var3"), "0"); + } + }); + + it("Should have set var4 = '' in the beacon data on all beacons", function() { + if (!t.isResourceTimingSupported()) { + return this.skip(); + } + + for (var i = 0; i < tf.beacons.length; i++) { + assert.strictEqual(getBeaconData(i, "var4"), ""); + } + }); + + it("Should have set var5 = '' in the beacon data on all beacons", function() { + if (!t.isResourceTimingSupported()) { + return this.skip(); + } + + for (var i = 0; i < tf.beacons.length; i++) { + assert.strictEqual(getBeaconData(i, "var5"), ""); + } + }); + + it("Should have set var6 = '' in the beacon data on all beacons", function() { + if (!t.isResourceTimingSupported()) { + return this.skip(); + } + + for (var i = 0; i < tf.beacons.length; i++) { + assert.strictEqual(getBeaconData(i, "var6"), ""); + } + }); + + it("Should have set var7 = '' in the beacon data on all beacons", function() { + if (!t.isResourceTimingSupported()) { + return this.skip(); + } + + for (var i = 0; i < tf.beacons.length; i++) { + assert.strictEqual(getBeaconData(i, "var7"), ""); + } + }); + + it("Should have set var8 = ~(a~1) in the beacon data on all beacons", function() { + if (!t.isResourceTimingSupported()) { + return this.skip(); + } + + for (var i = 0; i < tf.beacons.length; i++) { + if (BOOMR.utils.Compression && BOOMR.utils.Compression.jsUrl) { + assert.strictEqual(getBeaconData(i, "var8"), "~(a~1)"); + } + else { + assert.strictEqual(getBeaconData(i, "var8"), "{\"a\":1}"); + } + } + }); + + it("Should have set var9 = 1.1111111 in the beacon data on all beacons", function() { + if (!t.isResourceTimingSupported()) { + return this.skip(); + } + + for (var i = 0; i < tf.beacons.length; i++) { + assert.strictEqual(getBeaconData(i, "var9"), "1.1111111"); + } + }); + + it("Should have set var10 = 2 in the beacon data on all beacons", function() { + if (!t.isResourceTimingSupported()) { + return this.skip(); + } + + for (var i = 0; i < tf.beacons.length; i++) { + assert.strictEqual(getBeaconData(i, "var10"), "2"); + } + }); + + it("Should have set var11 = single on the first beacon", function() { + if (!t.isResourceTimingSupported()) { + return this.skip(); + } + + assert.strictEqual(getBeaconData(0, "var11"), "single"); + }); + + it("Should not have set var11 = single on the second beacon", function() { + if (!t.isResourceTimingSupported()) { + return this.skip(); + } + + assert.isUndefined(getBeaconData(1, "var11")); + }); + + it("Should not have set var11 = single on the third beacon", function() { + if (!t.isResourceTimingSupported()) { + return this.skip(); + } + + assert.isUndefined(getBeaconData(2, "var11")); + }); + + it("Should not have set var12 in the beacon data on all beacons", function() { + if (!t.isResourceTimingSupported()) { + return this.skip(); + } + + for (var i = 0; i < tf.beacons.length; i++) { + assert.isUndefined(getBeaconData(i, "var12")); + } + }); + + it("Should have set var13 = 1 in the beacon data on all beacons", function() { + if (!t.isResourceTimingSupported()) { + return this.skip(); + } + + for (var i = 0; i < tf.beacons.length; i++) { + assert.strictEqual(getBeaconData(i, "var13"), "1"); + } + }); + + it("Should have set var14 = 2 in the beacon data on all beacons", function() { + if (!t.isResourceTimingSupported()) { + return this.skip(); + } + + for (var i = 0; i < tf.beacons.length; i++) { + assert.strictEqual(getBeaconData(i, "var14"), "2"); + } + }); + + it("Should not have set var15 in the beacon data on all beacons", function() { + if (!t.isResourceTimingSupported()) { + return this.skip(); + } + + for (var i = 0; i < tf.beacons.length; i++) { + assert.isUndefined(getBeaconData(i, "var15")); + } + }); + }); +}); diff --git a/tests/page-templates/00-basic/13-onload-minified.html b/tests/page-templates/00-basic/13-onload-minified.html new file mode 100644 index 000000000..39a7b3e33 --- /dev/null +++ b/tests/page-templates/00-basic/13-onload-minified.html @@ -0,0 +1,13 @@ +<%= header %> +<%= boomerangSnippetMin %> + + +<%= footer %> diff --git a/tests/page-templates/00-basic/13-onload-minified.js b/tests/page-templates/00-basic/13-onload-minified.js new file mode 100644 index 000000000..174d6f328 --- /dev/null +++ b/tests/page-templates/00-basic/13-onload-minified.js @@ -0,0 +1,97 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["BOOMR_script_minified"]); + +describe("e2e/00-basic/13-onload-minified", function() { + var tf = BOOMR.plugins.TestFramework; + + it("Should have sent a beacon", function() { + // ensure we fired a beacon ('beacon') + assert.isTrue(tf.fired_onbeacon); + }); + + it("Should have fired 'beacon' with a beacon payload", function() { + // ensure the data was sent to 'beacon' + assert.isObject(tf.lastBeacon()); + }); + + it("Should have set basic beacon properties", function() { + assert.isString(tf.lastBeacon().v); + }); + + it("Should have set dom.* properties", function() { + assert.isNumber(tf.lastBeacon()["dom.img"], "dom.img"); + assert.isNumber(tf.lastBeacon()["dom.ln"], "dom.ln"); + assert.isNumber(tf.lastBeacon()["dom.script"], "dom.script"); + assert.isNumber(tf.lastBeacon()["dom.sz"], "dom.sz"); + + if (BOOMR_test.isResourceTimingSupported()) { + assert.isNumber(tf.lastBeacon()["dom.doms"], "dom.doms"); + assert.isNumber(tf.lastBeacon()["dom.res"], "dom.res"); + } + }); + + it("Should have set mem.* properties", function() { + if ((window.performance && window.performance.memory) || + (window.console && window.console.memory)) { + assert.isNumber(tf.lastBeacon()["mem.total"], "mem.total"); + assert.isNumber(tf.lastBeacon()["mem.used"], "mem.used"); + + // Might not exist except recent builds + if (tf.lastBeacon()["mem.limit"]) { + assert.isNumber(tf.lastBeacon()["mem.limit"], "mem.limit"); + } + } + }); + + it("Should have set RT properties", function() { + assert.isString(tf.lastBeacon().u, "u"); + + assert.isNumber(tf.lastBeacon()["rt.bstart"], "rt.bstart"); + assert.isNumber(tf.lastBeacon()["rt.end"], "rt.end"); + assert.isString(tf.lastBeacon()["rt.si"], "rt.si"); + assert.isNumber(tf.lastBeacon()["rt.sl"], "rt.sl"); + assert.isNumber(tf.lastBeacon()["rt.ss"], "rt.ss"); + assert.isString(tf.lastBeacon()["rt.start"], "rt.start"); + + // optional + if (typeof tf.lastBeacon()["rt.tstart"] !== "undefined") { + assert.isNumber(tf.lastBeacon()["rt.tstart"], "rt.tstart"); + } + }); + + it("Should have set scr.* properties", function() { + var s = window.screen; + + assert.isString(tf.lastBeacon()["scr.bpp"], "scr.bpp"); + assert.isString(tf.lastBeacon()["scr.xy"], "scr.xy"); + + // only if we have orientation + if (s && s.orientation) { + assert.isString(tf.lastBeacon()["scr.orn"], "scr.orn"); + } + + // only if we have pixel ratio + if (window.devicePixelRatio && window.devicePixelRatio > 1) { + assert.isNumber(tf.lastBeacon()["scr.dpx"], "scr.dpx"); + } + }); + + it("Should have set LOGN / SOASTA properties", function() { + assert.isString(tf.lastBeacon()["h.key"], "h.key"); + }); + + it("Should have set vis.* properties", function() { + assert.isString(tf.lastBeacon()["vis.st"], "vis.st"); + }); + + it("Should have set Page ID (pid)", function() { + assert.isString(tf.lastBeacon().pid, "pid"); + }); + + it("Should not have set nocookie", function() { + assert.isUndefined(tf.lastBeacon().nocookie); + }); +}); diff --git a/tests/page-templates/00-basic/14-undefined-var.html b/tests/page-templates/00-basic/14-undefined-var.html new file mode 100644 index 000000000..f1afea25f --- /dev/null +++ b/tests/page-templates/00-basic/14-undefined-var.html @@ -0,0 +1,15 @@ +<%= header %> +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/00-basic/14-undefined-var.js b/tests/page-templates/00-basic/14-undefined-var.js new file mode 100644 index 000000000..512060863 --- /dev/null +++ b/tests/page-templates/00-basic/14-undefined-var.js @@ -0,0 +1,26 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/00-basic/14-undefined-var", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a beacon", function() { + // ensure we fired a beacon ('beacon') + assert.isTrue(tf.fired_onbeacon); + }); + + it("Should have added 'undef' with an empty value", function() { + // we need resourcetiming, as the tf.lastBeacon() will have a direct + // copy of the input value (undefined), not what ends up on the beacon ('') + if (!t.isResourceTimingSupported()) { + return this.skip(); + } + + // ensure the data was sent to 'beacon' + var rt = t.findResourceTimingBeacon(); + + assert.isDefined(rt); + assert.isTrue(/[\?&]undef=&/.test(rt.name)); + }); +}); diff --git a/tests/page-templates/00-basic/15-cookie-setting.html b/tests/page-templates/00-basic/15-cookie-setting.html new file mode 100644 index 000000000..347460b6d --- /dev/null +++ b/tests/page-templates/00-basic/15-cookie-setting.html @@ -0,0 +1,21 @@ +<%= header %> +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/00-basic/15-cookie-setting.js b/tests/page-templates/00-basic/15-cookie-setting.js new file mode 100644 index 000000000..79034fb63 --- /dev/null +++ b/tests/page-templates/00-basic/15-cookie-setting.js @@ -0,0 +1,238 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/00-basic/15-cookie-setting", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a beacon", function() { + // ensure we fired a beacon ('beacon') + assert.isTrue(tf.fired_onbeacon); + }); + + it("Should have only read the cookie twice during init (if there is one init)", function() { + // We use UserTiming to measure marks + if (!t.isUserTimingSupported()) { + return this.skip(); + } + + if (t.findBoomerangMarks("init:start").length !== 1) { + return this.skip(); + } + + assert.equal( + 2, + t.findBoomerangMarksBetween( + "get_raw_cookie", + t.findBoomerangMarks("init:start")[0], + t.findBoomerangMarks("init:end")[0] + ).length); + }); + + it("Should have only set the cookie once during init (if there is one init)", function() { + // We use UserTiming to measure marks + if (!t.isUserTimingSupported()) { + return this.skip(); + } + + if (t.findBoomerangMarks("init:start").length !== 1) { + return this.skip(); + } + + assert.equal( + 1, + t.findBoomerangMarksBetween( + "set_cookie_real", + t.findBoomerangMarks("init:start")[0], + t.findBoomerangMarks("init:end")[0] + ).length); + }); + + it("Should have only read the cookie once during first init (if there are two inits)", function() { + // We use UserTiming to measure marks + if (!t.isUserTimingSupported()) { + return this.skip(); + } + + if (t.findBoomerangMarks("init:start").length !== 2) { + return this.skip(); + } + + assert.equal( + 1, + t.findBoomerangMarksBetween( + "get_raw_cookie", + t.findBoomerangMarks("init:start")[0], + t.findBoomerangMarks("init:end")[0] + ).length); + }); + + it("Should not have set the cookie during first init (if there are two inits)", function() { + // We use UserTiming to measure marks + if (!t.isUserTimingSupported()) { + return this.skip(); + } + + if (t.findBoomerangMarks("init:start").length !== 2) { + return this.skip(); + } + + assert.equal( + 0, + t.findBoomerangMarksBetween( + "set_cookie_real", + t.findBoomerangMarks("init:start")[0], + t.findBoomerangMarks("init:end")[0] + ).length); + }); + + it("Should have only read the cookie once during second init call (if there are two inits)", function() { + // We use UserTiming to measure marks + if (!t.isUserTimingSupported()) { + return this.skip(); + } + + if (t.findBoomerangMarks("init:start").length !== 2) { + return this.skip(); + } + + assert.equal( + 1, + t.findBoomerangMarksBetween( + "get_raw_cookie", + t.findBoomerangMarks("init:start")[1], + t.findBoomerangMarks("init:end")[1] + ).length); + }); + + it("Should have only set the cookie once during second init call (if there are two inits)", function() { + // We use UserTiming to measure marks + if (!t.isUserTimingSupported()) { + return this.skip(); + } + + if (t.findBoomerangMarks("init:start").length !== 2) { + return this.skip(); + } + + assert.equal( + 1, + t.findBoomerangMarksBetween( + "set_cookie_real", + t.findBoomerangMarks("init:start")[1], + t.findBoomerangMarks("init:end")[1] + ).length); + }); + + it("Should not have read the cookie during load", function() { + // We use UserTiming to measure marks + if (!t.isUserTimingSupported()) { + return this.skip(); + } + + assert.equal( + 0, + t.findBoomerangMarksBetween( + "get_raw_cookie", + t.findBoomerangMarks("fire_event:page_ready:start")[0], + t.findBoomerangMarks("fire_event:page_ready:end")[0] + ).length); + }); + + it("Should have only set the cookie once during load", function() { + // We use UserTiming to measure marks + if (!t.isUserTimingSupported()) { + return this.skip(); + } + + assert.equal( + 1, + t.findBoomerangMarksBetween( + "set_cookie_real", + t.findBoomerangMarks("fire_event:page_ready:start")[0], + t.findBoomerangMarks("fire_event:page_ready:end")[0] + ).length); + }); + + it("Should not have read the cookie during sendBeacon", function() { + // We use UserTiming to measure marks + if (!t.isUserTimingSupported()) { + return this.skip(); + } + + assert.equal( + 0, + t.findBoomerangMarksBetween( + "get_raw_cookie", + t.findBoomerangMarks("send_beacon:start")[0], + t.findBoomerangMarks("send_beacon:end")[0] + ).length); + }); + + it("Should not have set cookie during sendBeacon", function() { + // We use UserTiming to measure marks + if (!t.isUserTimingSupported()) { + return this.skip(); + } + + assert.equal( + 0, + t.findBoomerangMarksBetween( + "set_cookie_real", + t.findBoomerangMarks("send_beacon:start")[0], + t.findBoomerangMarks("send_beacon:end")[0] + ).length); + }); + + it("Should not have read the cookie once during unload", function() { + // We use UserTiming to measure marks + if (!t.isUserTimingSupported()) { + return this.skip(); + } + + assert.equal( + 0, + t.findBoomerangMarksBetween( + "get_raw_cookie", + t.findBoomerangMarks("fire_event:page_unload:start")[0], + t.findBoomerangMarks("fire_event:page_unload:end")[0] + ).length); + }); + + it("Should have only set the cookie once during unload", function() { + // We use UserTiming to measure marks + if (!t.isUserTimingSupported()) { + return this.skip(); + } + + assert.equal( + 1, + t.findBoomerangMarksBetween( + "set_cookie_real", + t.findBoomerangMarks("fire_event:page_unload:start")[0], + t.findBoomerangMarks("fire_event:page_unload:end")[0] + ).length); + }); + + it("Should have only read the cookie 2 times total", function() { + // We use UserTiming to measure marks + if (!t.isUserTimingSupported()) { + return this.skip(); + } + + assert.equal( + 2, + t.findBoomerangMarks("get_raw_cookie").length); + }); + + it("Should have only set the cookie 3 times total", function() { + // We use UserTiming to measure marks + if (!t.isUserTimingSupported()) { + return this.skip(); + } + + assert.equal( + 3, + t.findBoomerangMarks("set_cookie_real").length); + }); +}); diff --git a/tests/page-templates/00-basic/16-mo-supported.html b/tests/page-templates/00-basic/16-mo-supported.html new file mode 100644 index 000000000..1bb014130 --- /dev/null +++ b/tests/page-templates/00-basic/16-mo-supported.html @@ -0,0 +1,9 @@ +<%= header %> +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/00-basic/16-mo-supported.js b/tests/page-templates/00-basic/16-mo-supported.js new file mode 100644 index 000000000..1b25b2e7f --- /dev/null +++ b/tests/page-templates/00-basic/16-mo-supported.js @@ -0,0 +1,59 @@ +/* eslint-env mocha */ +/* global describe,it,BOOMR,BOOMR_test,assert */ + +describe("e2e/00-basic/16-mo-supported", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + var backupNavigator = window.navigator; + var backupMO = window.MutationObserver; + var UAIE11 = "Mozilla/5.0 (Windows NT 10.0; Trident/7.0; rv:11.0) like Gecko"; + + it("Should have sent a beacon", function() { + // ensure we fired a beacon ('beacon') + assert.isTrue(tf.fired_onbeacon); + }); + + it("Should return true if navigator does not have userAgentData but `window` has MutationObserver. If neither is available should return false", function() { + if (window.navigator.userAgent.match(/Trident.*rv[ :]*11\./)) { + this.skip(); + } + + if (window.MutationObserver && !window.navigator.userAgentData) { + assert.isTrue(BOOMR.utils.isMutationObserverSupported()); + } + else { + this.skip(); + } + }); + + it("Should return true if MO and UserAgentData is supported (we know it's not IE11 then)", function() { + if (window.navigator.userAgent.match(/Trident.*rv[ :]*11\./)) { + this.skip(); + } + + if (window.MutationObserver && window.navigator.userAgentData) { + assert.isTrue(BOOMR.utils.isMutationObserverSupported()); + } + else { + this.skip(); + } + }); + + it("Should return false if MO and userAgentData is not supported", function() { + if (!window.MutationObserver && !window.navigator.userAgentData) { + assert.isFalse(BOOMR.utils.isMutationObserverSupported()); + } + else { + this.skip(); + } + }); + + it("Should return false if we are in IE11", function() { + if (window.navigator && window.navigator.userAgent.match(/Trident.*rv[ :]*11\./)) { + assert.isFalse(BOOMR.utils.isMutationObserverSupported()); + } + else { + this.skip(); + } + }); +}); diff --git a/tests/page-templates/00-basic/16-onunload-no.html b/tests/page-templates/00-basic/16-onunload-no.html new file mode 100644 index 000000000..5261363ac --- /dev/null +++ b/tests/page-templates/00-basic/16-onunload-no.html @@ -0,0 +1,15 @@ +<%= header %> +<%= boomerangScript %> + + + +<%= footer %> diff --git a/tests/page-templates/00-basic/16-onunload-no.js b/tests/page-templates/00-basic/16-onunload-no.js new file mode 100644 index 000000000..a8debd9cb --- /dev/null +++ b/tests/page-templates/00-basic/16-onunload-no.js @@ -0,0 +1,29 @@ +/* eslint-env mocha */ +/* global assert */ + +describe("e2e/00-basic/16-onunload-no", function() { + var beaconData; + + it("Should not have sent an unload beacon", function(done) { + var successTimeout; + + this.timeout(10000); + + var unloadBeaconHandler = function(data) { + clearTimeout(successTimeout); + + // need to avoid BOOMR event callback wrappers + setTimeout(function() { + assert.fail("Unload beacon sent!"); + }, 0); + }; + + var testFrame = document.getElementById("boomer_test_frame"); + + testFrame.contentWindow.BOOMR.subscribe("beacon", unloadBeaconHandler, null, this); + testFrame.src = "about:blank"; + + // if a beacon doesn't fire in 2 seconds, we're done + successTimeout = setTimeout(done, 2000); + }); +}); diff --git a/tests/page-templates/00-basic/17-ua-plt-baseline.html b/tests/page-templates/00-basic/17-ua-plt-baseline.html new file mode 100644 index 000000000..8175c03f5 --- /dev/null +++ b/tests/page-templates/00-basic/17-ua-plt-baseline.html @@ -0,0 +1,9 @@ +<%= header %> +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/00-basic/17-ua-plt-baseline.js b/tests/page-templates/00-basic/17-ua-plt-baseline.js new file mode 100644 index 000000000..a0a7810cb --- /dev/null +++ b/tests/page-templates/00-basic/17-ua-plt-baseline.js @@ -0,0 +1,16 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/00-basic/17-ua-plt-baseline", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a beacon", function() { + // ensure we fired a beacon ('beacon') + assert.isTrue(tf.fired_onbeacon); + }); + + it("Should have a value for ua.plt", function() { + assert.isString(tf.lastBeacon()["ua.plt"], "ua.plt"); + }); +}); diff --git a/tests/page-templates/00-basic/18-entropy-client-hints-supported.html b/tests/page-templates/00-basic/18-entropy-client-hints-supported.html new file mode 100644 index 000000000..55b7fb5ea --- /dev/null +++ b/tests/page-templates/00-basic/18-entropy-client-hints-supported.html @@ -0,0 +1,10 @@ +<%= header %> +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/00-basic/18-entropy-client-hints-supported.js b/tests/page-templates/00-basic/18-entropy-client-hints-supported.js new file mode 100644 index 000000000..6996ae93f --- /dev/null +++ b/tests/page-templates/00-basic/18-entropy-client-hints-supported.js @@ -0,0 +1,47 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/00-basic/18-entropy-client-hints-supported", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a beacon", function() { + // ensure we fired a beacon ('beacon') + assert.isTrue(tf.fired_onbeacon); + }); + + it("Should have set properties architecture, model, and platformVersion if client hints supported", function(done) { + if (!t.isClientHintsSupported()) { + return this.skip(); + } + + navigator.userAgentData.getHighEntropyValues(["architecture", "model", "platformVersion"]).then(function(ua) { + var arch = tf.lastBeacon()["ua.arch"]; + + assert.isString(arch); + assert.equal(arch, ua.architecture); + + var model = tf.lastBeacon()["ua.model"]; + + assert.isString(model); + assert.equal(model, ua.model); + + var pltv = tf.lastBeacon()["ua.pltv"]; + + assert.isString(pltv); + assert.equal(pltv, ua.platformVersion); + + done(); + }); + }); + + it("Should NOT have set properties architecture, model, and platformVersion if client hints not supported", function() { + if (t.isClientHintsSupported()) { + return this.skip(); + } + + assert.isUndefined(tf.lastBeacon()["ua.arch"]); + assert.isUndefined(tf.lastBeacon()["ua.model"]); + assert.isUndefined(tf.lastBeacon()["ua.pltv"]); + }); +}); diff --git a/tests/page-templates/00-basic/19-entropy-client-hints.html b/tests/page-templates/00-basic/19-entropy-client-hints.html new file mode 100644 index 000000000..dd9376cdd --- /dev/null +++ b/tests/page-templates/00-basic/19-entropy-client-hints.html @@ -0,0 +1,45 @@ +<%= header %> + +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/00-basic/19-entropy-client-hints.js b/tests/page-templates/00-basic/19-entropy-client-hints.js new file mode 100644 index 000000000..65289630c --- /dev/null +++ b/tests/page-templates/00-basic/19-entropy-client-hints.js @@ -0,0 +1,32 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["localUA"]); + +describe("e2e/00-basic/19-entropy-client-hints", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a beacon", function() { + // ensure we fired a beacon ('beacon') + assert.isTrue(tf.fired_onbeacon); + }); + + it("Should have set properties architecture, model, and platformVersion", function() { + var arch = tf.lastBeacon()["ua.arch"]; + + assert.isString(arch); + assert.equal(arch, "test_arch"); + + var model = tf.lastBeacon()["ua.model"]; + + assert.isString(model); + assert.equal(model, "test_model"); + + var pltv = tf.lastBeacon()["ua.pltv"]; + + assert.isString(pltv); + assert.equal(pltv, "test_plat_v"); + }); +}); diff --git a/tests/page-templates/00-basic/20-entropy-client-hints-false.html b/tests/page-templates/00-basic/20-entropy-client-hints-false.html new file mode 100644 index 000000000..8f063e5a2 --- /dev/null +++ b/tests/page-templates/00-basic/20-entropy-client-hints-false.html @@ -0,0 +1,45 @@ +<%= header %> + +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/00-basic/20-entropy-client-hints-false.js b/tests/page-templates/00-basic/20-entropy-client-hints-false.js new file mode 100644 index 000000000..62406afc2 --- /dev/null +++ b/tests/page-templates/00-basic/20-entropy-client-hints-false.js @@ -0,0 +1,21 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["localUA"]); + +describe("e2e/00-basic/20-entropy-client-hints-false", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a beacon", function() { + // ensure we fired a beacon ('beacon') + assert.isTrue(tf.fired_onbeacon); + }); + + it("Should NOT have set properties architecture, model, and platformVersion", function() { + assert.isUndefined(tf.lastBeacon()["ua.arch"]); + assert.isUndefined(tf.lastBeacon()["ua.model"]); + assert.isUndefined(tf.lastBeacon()["ua.pltv"]); + }); +}); diff --git a/tests/page-templates/00-basic/support/generic-autorun-false.html b/tests/page-templates/00-basic/support/generic-autorun-false.html new file mode 100644 index 000000000..c374edcbf --- /dev/null +++ b/tests/page-templates/00-basic/support/generic-autorun-false.html @@ -0,0 +1,28 @@ + + + + Generic Page w/ Boomerang + + + + + + + + + + + +
+ + + diff --git a/tests/page-templates/00-basic/support/generic-no-unload.html b/tests/page-templates/00-basic/support/generic-no-unload.html new file mode 100644 index 000000000..965e6621a --- /dev/null +++ b/tests/page-templates/00-basic/support/generic-no-unload.html @@ -0,0 +1,46 @@ + + + + Generic Page w/ Boomerang + + + + + + + + + + + + + + +
+ + + diff --git a/tests/page-templates/00-basic/support/generic.html b/tests/page-templates/00-basic/support/generic.html new file mode 100644 index 000000000..9d717b3dd --- /dev/null +++ b/tests/page-templates/00-basic/support/generic.html @@ -0,0 +1,45 @@ + + + + Generic Page w/ Boomerang + + + + + + + + + + + + + + +
+ + + diff --git a/tests/page-templates/00-basic/support/generic.html.headers b/tests/page-templates/00-basic/support/generic.html.headers new file mode 100644 index 000000000..cb762eff8 --- /dev/null +++ b/tests/page-templates/00-basic/support/generic.html.headers @@ -0,0 +1 @@ +Access-Control-Allow-Origin: * diff --git a/tests/page-templates/01-beacon-type/00-resourcetiming-disabled.html b/tests/page-templates/01-beacon-type/00-resourcetiming-disabled.html new file mode 100644 index 000000000..6638974cb --- /dev/null +++ b/tests/page-templates/01-beacon-type/00-resourcetiming-disabled.html @@ -0,0 +1,13 @@ +<%= header %> +<%= boomerangSnippet %> +<%= disableSendBeacon %> + + +<%= footer %> diff --git a/tests/page-templates/01-beacon-type/00-resourcetiming-disabled.js b/tests/page-templates/01-beacon-type/00-resourcetiming-disabled.js new file mode 100644 index 000000000..f42116f42 --- /dev/null +++ b/tests/page-templates/01-beacon-type/00-resourcetiming-disabled.js @@ -0,0 +1,8 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +describe("e2e/01-beacon-type/00-resourcetiming-disabled", function() { + it("Should send an Image beacon because ResourceTiming is disabled", function(done) { + BOOMR_test.validateBeaconWasImg(done); + }); +}); diff --git a/tests/page-templates/01-beacon-type/01-resourcetiming-enabled-browser-supports-under-2k.html b/tests/page-templates/01-beacon-type/01-resourcetiming-enabled-browser-supports-under-2k.html new file mode 100644 index 000000000..bf270b864 --- /dev/null +++ b/tests/page-templates/01-beacon-type/01-resourcetiming-enabled-browser-supports-under-2k.html @@ -0,0 +1,19 @@ +<%= header %> +<%= boomerangSnippet %> +<%= disableSendBeacon %> + + +<%= footer %> diff --git a/tests/page-templates/01-beacon-type/01-resourcetiming-enabled-browser-supports-under-2k.js b/tests/page-templates/01-beacon-type/01-resourcetiming-enabled-browser-supports-under-2k.js new file mode 100644 index 000000000..8865f9740 --- /dev/null +++ b/tests/page-templates/01-beacon-type/01-resourcetiming-enabled-browser-supports-under-2k.js @@ -0,0 +1,14 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +describe("e2e/01-beacon-type/01-resourcetiming-enabled-browser-supports-under-2k", function() { + it("Should send a IMG beacon if ResourceTiming is enabled and the browser supports it and the length is under 2k characters", function(done) { + if (BOOMR_test.isResourceTimingSupported()) { + BOOMR_test.validateBeaconWasImg(done); + } + else { + // NOTE: If not, another test handles + done(); + } + }); +}); diff --git a/tests/page-templates/01-beacon-type/02-resourcetiming-enabled-browser-unsupported.html b/tests/page-templates/01-beacon-type/02-resourcetiming-enabled-browser-unsupported.html new file mode 100644 index 000000000..f08b021ce --- /dev/null +++ b/tests/page-templates/01-beacon-type/02-resourcetiming-enabled-browser-unsupported.html @@ -0,0 +1,13 @@ +<%= header %> +<%= boomerangSnippet %> +<%= disableSendBeacon %> + + +<%= footer %> diff --git a/tests/page-templates/01-beacon-type/02-resourcetiming-enabled-browser-unsupported.js b/tests/page-templates/01-beacon-type/02-resourcetiming-enabled-browser-unsupported.js new file mode 100644 index 000000000..9e47aa9c1 --- /dev/null +++ b/tests/page-templates/01-beacon-type/02-resourcetiming-enabled-browser-unsupported.js @@ -0,0 +1,14 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +describe("e2e/01-beacon-type/02-resourcetiming-enabled-browser-unsupported", function() { + it("Should send an Image beacon if ResourceTiming is enabled, but the browser doesn't support it", function(done) { + if (!BOOMR_test.isResourceTimingSupported()) { + BOOMR_test.validateBeaconWasImg(done); + } + else { + // NOTE: If not, another test handles + done(); + } + }); +}); diff --git a/tests/page-templates/01-beacon-type/03-resourcetiming-enabled-browser-supports-over-2k.html b/tests/page-templates/01-beacon-type/03-resourcetiming-enabled-browser-supports-over-2k.html new file mode 100644 index 000000000..a418f1d2f --- /dev/null +++ b/tests/page-templates/01-beacon-type/03-resourcetiming-enabled-browser-supports-over-2k.html @@ -0,0 +1,23 @@ +<%= header %> +<%= boomerangSnippet %> +<%= disableSendBeacon %> + + +<%= footer %> diff --git a/tests/page-templates/01-beacon-type/03-resourcetiming-enabled-browser-supports-over-2k.js b/tests/page-templates/01-beacon-type/03-resourcetiming-enabled-browser-supports-over-2k.js new file mode 100644 index 000000000..7bd16b9e5 --- /dev/null +++ b/tests/page-templates/01-beacon-type/03-resourcetiming-enabled-browser-supports-over-2k.js @@ -0,0 +1,17 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["len", "str", "i"]); + +describe("e2e/01-beacon-type/03-resourcetiming-enabled-browser-supports-over-2k", function() { + it("Should send a XHR beacon if ResourceTiming is enabled and the browser supports it and the length is over 2k characters", function(done) { + if (BOOMR_test.isResourceTimingSupported()) { + BOOMR_test.validateBeaconWasXhr(done); + } + else { + // NOTE: If not, another test handles + done(); + } + }); +}); diff --git a/tests/page-templates/01-beacon-type/04-sendbeacon.html b/tests/page-templates/01-beacon-type/04-sendbeacon.html new file mode 100644 index 000000000..d42cdc625 --- /dev/null +++ b/tests/page-templates/01-beacon-type/04-sendbeacon.html @@ -0,0 +1,23 @@ +<%= header %> + +<%= boomerangSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/01-beacon-type/04-sendbeacon.js b/tests/page-templates/01-beacon-type/04-sendbeacon.js new file mode 100644 index 000000000..ec85b699c --- /dev/null +++ b/tests/page-templates/01-beacon-type/04-sendbeacon.js @@ -0,0 +1,19 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["sendBeaconUrl", "sendBeaconData"]); + +describe("e2e/01-beacon-type/04-send-beacon", function() { + it("Should send an beacon via navigator.sendBeacon if it is available", function() { + if (window && window.navigator && typeof window.navigator.sendBeacon === "function") { + assert.isDefined(window.sendBeaconUrl); + assert.equal(window.sendBeaconUrl, BOOMR_test.BEACON_URL); + + assert.isDefined(window.sendBeaconData); + } + else { + this.skip(); + } + }); +}); diff --git a/tests/page-templates/01-beacon-type/05-beacon-type-get-no-nav-sendbeacon.html b/tests/page-templates/01-beacon-type/05-beacon-type-get-no-nav-sendbeacon.html new file mode 100644 index 000000000..30c9f571a --- /dev/null +++ b/tests/page-templates/01-beacon-type/05-beacon-type-get-no-nav-sendbeacon.html @@ -0,0 +1,24 @@ +<%= header %> + +<%= boomerangSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/01-beacon-type/05-beacon-type-get-no-nav-sendbeacon.js b/tests/page-templates/01-beacon-type/05-beacon-type-get-no-nav-sendbeacon.js new file mode 100644 index 000000000..3fd6fa3af --- /dev/null +++ b/tests/page-templates/01-beacon-type/05-beacon-type-get-no-nav-sendbeacon.js @@ -0,0 +1,14 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["sendBeaconUrl", "sendBeaconData"]); + +describe("e2e/01-beacon-type/05-beacon-type-get-no-nav-sendbeacon", function() { + it("Should not send an beacon via navigator.sendBeacon when beacon type is GET", function() { + if (window && window.navigator && typeof window.navigator.sendBeacon === "function") { + assert.isUndefined(window.sendBeaconUrl, "Expected sendBeaconUrl to be undefined"); + assert.isUndefined(window.sendBeaconData, "Expected sendBeaconData to be undefined"); + } + }); +}); diff --git a/tests/page-templates/01-beacon-type/06-beacon-type-get-no-xhr.html b/tests/page-templates/01-beacon-type/06-beacon-type-get-no-xhr.html new file mode 100644 index 000000000..214d802a7 --- /dev/null +++ b/tests/page-templates/01-beacon-type/06-beacon-type-get-no-xhr.html @@ -0,0 +1,27 @@ +<%= header %> + +<%= boomerangSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/01-beacon-type/06-beacon-type-get-no-xhr.js b/tests/page-templates/01-beacon-type/06-beacon-type-get-no-xhr.js new file mode 100644 index 000000000..d55015516 --- /dev/null +++ b/tests/page-templates/01-beacon-type/06-beacon-type-get-no-xhr.js @@ -0,0 +1,16 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["boomrxhr", "xhrparams"]); + +describe("e2e/01-beacon-type/06-beacon-type-get-no-xhr", function() { + it("Should not send an beacon via XHR when beacon type is GET", function() { + if (BOOMR && typeof BOOMR.sendXhrPostBeacon === "function") { + assert.isDefined(window.boomrxhr); + assert.strictEqual(window.boomrxhr, "TestString", "Expected beacon to be not sent via XHR"); + assert.isDefined(window.xhrparams); + assert.strictEqual(window.xhrparams, "TestString", "Expected beacon to be not sent via XHR"); + } + }); +}); diff --git a/tests/page-templates/01-beacon-type/07-sendbeacon-polyfill.html b/tests/page-templates/01-beacon-type/07-sendbeacon-polyfill.html new file mode 100644 index 000000000..79570413e --- /dev/null +++ b/tests/page-templates/01-beacon-type/07-sendbeacon-polyfill.html @@ -0,0 +1,20 @@ +<%= header %> + +<%= boomerangSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/01-beacon-type/07-sendbeacon-polyfill.js b/tests/page-templates/01-beacon-type/07-sendbeacon-polyfill.js new file mode 100644 index 000000000..4b6b1bc1f --- /dev/null +++ b/tests/page-templates/01-beacon-type/07-sendbeacon-polyfill.js @@ -0,0 +1,15 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["usedPolyfilledSendBeacon", "hasNativeSendBeacon"]); + +describe("e2e/01-beacon-type/07-sendbeacon-polyfill", function() { + it("Should not have used a polyfilled navigator.sendBeacon if it is available", function() { + if (!window.hasNativeSendBeacon) { + return this.skip(); + } + + assert.isFalse(window.usedPolyfilledSendBeacon); + }); +}); diff --git a/tests/page-templates/01-beacon-type/08-sendbeacon-disabled.html b/tests/page-templates/01-beacon-type/08-sendbeacon-disabled.html new file mode 100644 index 000000000..4d6798c47 --- /dev/null +++ b/tests/page-templates/01-beacon-type/08-sendbeacon-disabled.html @@ -0,0 +1,24 @@ +<%= header %> + +<%= boomerangSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/01-beacon-type/08-sendbeacon-disabled.js b/tests/page-templates/01-beacon-type/08-sendbeacon-disabled.js new file mode 100644 index 000000000..27f74a71d --- /dev/null +++ b/tests/page-templates/01-beacon-type/08-sendbeacon-disabled.js @@ -0,0 +1,16 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["sentViaSendBeacon", "supportNativeSendBeacon"]); + +describe("e2e/01-beacon-type/08-sendbeacon-disabled", function() { + it("Should send an beacon via XHR even if navigator.sendBeacon is available", function() { + if (window && window.supportNativeSendBeacon) { + assert.isFalse(window.sentViaSendBeacon); + } + else { + this.skip(); + } + }); +}); diff --git a/tests/page-templates/02-snippet/00-snippet.html b/tests/page-templates/02-snippet/00-snippet.html new file mode 100644 index 000000000..f4de113cb --- /dev/null +++ b/tests/page-templates/02-snippet/00-snippet.html @@ -0,0 +1,9 @@ +<%= header %> +<%= boomerangSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/02-snippet/00-snippet.js b/tests/page-templates/02-snippet/00-snippet.js new file mode 100644 index 000000000..c718bdec8 --- /dev/null +++ b/tests/page-templates/02-snippet/00-snippet.js @@ -0,0 +1,10 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +describe("e2e/02-snippet/00-snippet", function() { + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); +}); diff --git a/tests/page-templates/02-snippet/01-script-removal.html b/tests/page-templates/02-snippet/01-script-removal.html new file mode 100644 index 000000000..469f2a409 --- /dev/null +++ b/tests/page-templates/02-snippet/01-script-removal.html @@ -0,0 +1,21 @@ +<%= header %> +<%= boomerangSnippet %> + + + +<%= footer %> diff --git a/tests/page-templates/02-snippet/01-script-removal.js b/tests/page-templates/02-snippet/01-script-removal.js new file mode 100644 index 000000000..9587f4265 --- /dev/null +++ b/tests/page-templates/02-snippet/01-script-removal.js @@ -0,0 +1,13 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["scripts", "scriptIndex", "script"]); + +describe("e2e/02-snippet/01-script-removal", function() { + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); +}); diff --git a/tests/page-templates/03-load-order/00-before-page-load.html b/tests/page-templates/03-load-order/00-before-page-load.html new file mode 100644 index 000000000..e014db347 --- /dev/null +++ b/tests/page-templates/03-load-order/00-before-page-load.html @@ -0,0 +1,9 @@ +<%= header %> +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/03-load-order/00-before-page-load.js b/tests/page-templates/03-load-order/00-before-page-load.js new file mode 100644 index 000000000..373eda295 --- /dev/null +++ b/tests/page-templates/03-load-order/00-before-page-load.js @@ -0,0 +1,89 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/03-load-order/00-before-page-load", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should contain BOOMR.url set to boomerang's URL", function() { + assert.isString(BOOMR.url, "is not a string"); + assert.match(BOOMR.url, /\/boomerang-latest-debug.js($|\?)/, "does not match: " + BOOMR.url); + }); + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should be a 'navigation' (if NavTiming supported)", function() { + var b = tf.lastBeacon(); + + if (window.performance && window.performance.timing) { + assert.equal(b["rt.start"], "navigation"); + } + else { + return this.skip(); + } + }); + + it("Should be a 'none' (if NavTiming not supported)", function() { + var b = tf.lastBeacon(); + + if (!(window.performance && window.performance.timing)) { + assert.equal(b["rt.start"], "none"); + } + else { + return this.skip(); + } + }); + + it("Should have a start timestamp equal to NavigationTiming's navigationStart timestamp (if NavTiming supported)", function() { + var b = tf.lastBeacon(); + + if (window.performance && window.performance.timing && window.performance.timing.navigationStart) { + assert.equal(b["rt.tstart"], window.performance.timing.navigationStart); + } + else { + return this.skip(); + } + }); + + it("Should have a an empty rt.tstart (if NavTiming is not supported)", function() { + var b = tf.lastBeacon(); + + if (!(window.performance && window.performance.timing && window.performance.timing.navigationStart)) { + assert.isUndefined(b["rt.tstart"]); + } + else { + return this.skip(); + } + }); + + it("Should have a end timestamp sometime after the NavigationTiming's loadEventEnd timestamp and before now (if NavTiming supported)", function() { + var b = tf.lastBeacon(); + var now = +(new Date()); + + if (window.performance && window.performance.timing && window.performance.timing.navigationStart) { + assert.operator(b["rt.end"], ">=", window.performance.timing.loadEventEnd); + assert.operator(b["rt.end"], "<=", now); + } + else { + return this.skip(); + } + }); + + it("Should have a end timestamp sometime in the last hour (if NavTiming is not supported)", function() { + var b = tf.lastBeacon(); + var now = +(new Date()); + + if (!(window.performance && window.performance.timing && window.performance.timing.navigationStart)) { + // ended less than an hour ago + assert.operator(b["rt.end"], ">=", (now - 3600000)); + + // ended less than now + assert.operator(b["rt.end"], "<=", now); + } + else { + return this.skip(); + } + }); +}); diff --git a/tests/page-templates/03-load-order/01-after-page-load.html b/tests/page-templates/03-load-order/01-after-page-load.html new file mode 100644 index 000000000..9520c7d43 --- /dev/null +++ b/tests/page-templates/03-load-order/01-after-page-load.html @@ -0,0 +1,15 @@ +<%= header %> + + +<%= footer %> diff --git a/tests/page-templates/03-load-order/01-after-page-load.js b/tests/page-templates/03-load-order/01-after-page-load.js new file mode 100644 index 000000000..bd0491b6b --- /dev/null +++ b/tests/page-templates/03-load-order/01-after-page-load.js @@ -0,0 +1,84 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/03-load-order/01-after-page-load", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should contain BOOMR.url set to boomerang's URL", function() { + assert.isString(BOOMR.url, "is not a string"); + assert.match(BOOMR.url, /\/boomerang-latest-debug.js($|\?)/, "does not match: " + BOOMR.url); + }); + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have a start timestamp equal to NavigationTiming's navigationStart timestamp (if NavTiming supported)", function() { + var b = tf.lastBeacon(); + + if (window.performance && window.performance.timing) { + assert.equal(b["rt.tstart"], window.performance.timing.navigationStart); + } + else { + return this.skip(); + } + }); + + it("Should have an empty rt.tstart (if NavTiming not supported)", function() { + var b = tf.lastBeacon(); + + if (!(window.performance && window.performance.timing)) { + assert.isUndefined(b["rt.tstart"]); + } + else { + return this.skip(); + } + }); + + it("Should have a end timestamp equal to NavigationTiming's loadEventEnd timestamp (if NavTiming supported)", function() { + var b = tf.lastBeacon(); + + if (window.performance && window.performance.timing) { + assert.equal(b["rt.end"], window.performance.timing.loadEventEnd); + } + else { + return this.skip(); + } + }); + + it("Should have a end timestamp sometime in the last hour (if NavTiming not supported)", function() { + var b = tf.lastBeacon(); + var now = +(new Date()); + + if (!(window.performance && window.performance.timing)) { + assert.operator(b["rt.end"], ">=", now - 3600000); + assert.operator(b["rt.end"], "<=", now); + } + else { + return this.skip(); + } + }); + + it("Should be a 'navigation' (if NavTiming supported)", function() { + var b = tf.lastBeacon(); + + if (window.performance && window.performance.timing) { + assert.equal(b["rt.start"], "navigation"); + } + else { + return this.skip(); + } + }); + + it("Should be a 'none' (if NavTiming not supported)", function() { + var b = tf.lastBeacon(); + + if (!(window.performance && window.performance.timing)) { + assert.equal(b["rt.start"], "none"); + } + else { + return this.skip(); + } + }); +}); diff --git a/tests/page-templates/03-load-order/02-after-page-load-tag-manager.html b/tests/page-templates/03-load-order/02-after-page-load-tag-manager.html new file mode 100644 index 000000000..1c1753356 --- /dev/null +++ b/tests/page-templates/03-load-order/02-after-page-load-tag-manager.html @@ -0,0 +1,9 @@ +<%= header %> + + +<%= boomerangDelayedSnippet %> +<%= footer %> diff --git a/tests/page-templates/03-load-order/02-after-page-load-tag-manager.js b/tests/page-templates/03-load-order/02-after-page-load-tag-manager.js new file mode 100644 index 000000000..5d651ded2 --- /dev/null +++ b/tests/page-templates/03-load-order/02-after-page-load-tag-manager.js @@ -0,0 +1,75 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["BOOMR_script_delay"]); + +describe("e2e/03-load-order/02-after-page-load-tag-manager", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should contain BOOMR.url set to boomerang's URL", function() { + assert.isString(BOOMR.url, "is not a string"); + assert.match(BOOMR.url, /\/boomerang-latest-debug.js($|\?|&)/, "does not match: " + BOOMR.url); + }); + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have a end timestamp equal to NavigationTiming's loadEventEnd timestamp (if NavTiming supported)", function() { + var b = tf.lastBeacon(); + + if (window.performance && window.performance.timing) { + assert.equal(b["rt.end"], window.performance.timing.loadEventEnd); + } + else { + return this.skip(); + } + }); + + it("Should have a end timestamp after the loader start timestamp (if NavTiming not supported)", function() { + var b = tf.lastBeacon(); + + if (!(window.performance && window.performance.timing)) { + assert.operator(b["rt.end"], ">=", BOOMR.t_lstart); + } + else { + return this.skip(); + } + }); + + it("Should have a end timestamp before the Boomerang start timestamp (if NavTiming not supported)", function() { + var b = tf.lastBeacon(); + + if (!(window.performance && window.performance.timing)) { + assert.operator(b["rt.end"], "<=", BOOMR.t_start); + } + else { + return this.skip(); + } + }); + + it("Should have a end timestamp before now (if NavTiming not supported)", function() { + var b = tf.lastBeacon(); + var now = +(new Date()); + + if (!(window.performance && window.performance.timing)) { + assert.operator(b["rt.end"], "<=", now); + } + else { + return this.skip(); + } + }); + + it("Should have a end timestamp equal to BOOMR.t_onload (if NavTiming not supported)", function() { + var b = tf.lastBeacon(); + + if (!(window.performance && window.performance.timing)) { + assert.equal(b["rt.end"], BOOMR.t_onload); + } + else { + return this.skip(); + } + }); +}); diff --git a/tests/page-templates/03-load-order/04-page-ready-after-onload.html b/tests/page-templates/03-load-order/04-page-ready-after-onload.html new file mode 100644 index 000000000..fd541ed5f --- /dev/null +++ b/tests/page-templates/03-load-order/04-page-ready-after-onload.html @@ -0,0 +1,18 @@ +<%= header %> +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/03-load-order/04-page-ready-after-onload.js b/tests/page-templates/03-load-order/04-page-ready-after-onload.js new file mode 100644 index 000000000..6212f6331 --- /dev/null +++ b/tests/page-templates/03-load-order/04-page-ready-after-onload.js @@ -0,0 +1,35 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["pageReadyTs"]); + +describe("e2e/03-load-order/04-page-ready-after-onload", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + var assert = chai.assert; + + it("Should have sent a beacon", function(done) { + t.ensureBeaconCount(done, 1); + }); + + it("Should have a load time over 3 seconds (if NavigationTiming is supported)", function() { + var b = tf.lastBeacon(); + + if (!b.t_done) { + // non-NavTiming browsers won't know how to calculate Page Load time + return this.skip(); + } + + assert.operator(b.t_done, ">=", 3000); + + // should be around window.pageReadyTs + assert.closeTo(b.t_done, window.pageReadyTs - window.performance.timing.navigationStart, 200); + }); + + it("Should have set pr=1", function() { + var b = tf.lastBeacon(); + + assert.equal(b.pr, "1"); + }); +}); diff --git a/tests/page-templates/03-load-order/05-page-ready-timestamp-param.html b/tests/page-templates/03-load-order/05-page-ready-timestamp-param.html new file mode 100644 index 000000000..1a22b3118 --- /dev/null +++ b/tests/page-templates/03-load-order/05-page-ready-timestamp-param.html @@ -0,0 +1,20 @@ +<%= header %> + +<%= boomerangScript %> + + + + +<%= footer %> diff --git a/tests/page-templates/03-load-order/05-page-ready-timestamp-param.js b/tests/page-templates/03-load-order/05-page-ready-timestamp-param.js new file mode 100644 index 000000000..cce093645 --- /dev/null +++ b/tests/page-templates/03-load-order/05-page-ready-timestamp-param.js @@ -0,0 +1,41 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["pageReadyTs"]); + +describe("e2e/03-load-order/05-page-ready-timestamp-param", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + var assert = chai.assert; + + it("Should have sent a beacon", function(done) { + t.ensureBeaconCount(done, 1); + }); + + it("Should have a load time over 1 second (if NavigationTiming is supported)", function() { + var b = tf.lastBeacon(); + + if (!b.t_done) { + // non-NavTiming browsers won't know how to calculate Page Load time + return this.skip(); + } + + assert.operator(b.t_done, ">=", 1000); + + // should be window.pageReadyTs + assert.equal(b.t_done, window.pageReadyTs - window.performance.timing.navigationStart); + }); + + it("Should have a end timestamp equal to our simulated page ready timestamp", function() { + var b = tf.lastBeacon(); + + assert.equal(b["rt.end"], window.pageReadyTs); + }); + + it("Should have set pr=1", function() { + var b = tf.lastBeacon(); + + assert.equal(b.pr, "1"); + }); +}); diff --git a/tests/page-templates/03-load-order/06-page-ready-timestamp-global.html b/tests/page-templates/03-load-order/06-page-ready-timestamp-global.html new file mode 100644 index 000000000..a45479701 --- /dev/null +++ b/tests/page-templates/03-load-order/06-page-ready-timestamp-global.html @@ -0,0 +1,20 @@ +<%= header %> + +<%= boomerangScript %> + + + + +<%= footer %> diff --git a/tests/page-templates/03-load-order/06-page-ready-timestamp-global.js b/tests/page-templates/03-load-order/06-page-ready-timestamp-global.js new file mode 100644 index 000000000..4bcb5d9dc --- /dev/null +++ b/tests/page-templates/03-load-order/06-page-ready-timestamp-global.js @@ -0,0 +1,41 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["pageReadyTs", "BOOMR_page_ready"]); + +describe("e2e/03-load-order/06-page-ready-timestamp-global", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + var assert = chai.assert; + + it("Should have sent a beacon", function(done) { + t.ensureBeaconCount(done, 1); + }); + + it("Should have a load time over 1 second (if NavigationTiming is supported)", function() { + var b = tf.lastBeacon(); + + if (!b.t_done) { + // non-NavTiming browsers won't know how to calculate Page Load time + return this.skip(); + } + + assert.operator(b.t_done, ">=", 1000); + + // should be window.pageReadyTs + assert.equal(b.t_done, window.pageReadyTs - window.performance.timing.navigationStart); + }); + + it("Should have a end timestamp equal to our simulated page ready timestamp", function() { + var b = tf.lastBeacon(); + + assert.equal(b["rt.end"], window.pageReadyTs); + }); + + it("Should have set pr=1", function() { + var b = tf.lastBeacon(); + + assert.equal(b.pr, "1"); + }); +}); diff --git a/tests/page-templates/03-load-order/07-after-page-load-boomr-page-ready.html b/tests/page-templates/03-load-order/07-after-page-load-boomr-page-ready.html new file mode 100644 index 000000000..81a258ddd --- /dev/null +++ b/tests/page-templates/03-load-order/07-after-page-load-boomr-page-ready.html @@ -0,0 +1,21 @@ +<%= header %> + + + + + +<%= footer %> diff --git a/tests/page-templates/03-load-order/07-after-page-load-boomr-page-ready.js b/tests/page-templates/03-load-order/07-after-page-load-boomr-page-ready.js new file mode 100644 index 000000000..86904e26e --- /dev/null +++ b/tests/page-templates/03-load-order/07-after-page-load-boomr-page-ready.js @@ -0,0 +1,83 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["pageReadyTs", "BOOMR_page_ready"]); + +describe("e2e/03-load-order/07-after-page-load-boomr-page-ready", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have a start timestamp equal to NavigationTiming's navigationStart timestamp (if NavTiming supported)", function() { + var b = tf.lastBeacon(); + + if (window.performance && window.performance.timing) { + assert.equal(b["rt.tstart"], window.performance.timing.navigationStart); + } + else { + return this.skip(); + } + }); + + it("Should have an empty rt.tstart (if NavTiming not supported)", function() { + var b = tf.lastBeacon(); + + if (!(window.performance && window.performance.timing)) { + assert.isUndefined(b["rt.tstart"]); + } + else { + return this.skip(); + } + }); + + it("Should have a end timestamp equal to our simulated page ready timestamp", function() { + var b = tf.lastBeacon(); + + assert.equal(b["rt.end"], window.pageReadyTs); + }); + + it("Should have a end timestamp sometime in the last hour (if NavTiming not supported)", function() { + var b = tf.lastBeacon(); + var now = +(new Date()); + + if (!(window.performance && window.performance.timing)) { + assert.operator(b["rt.end"], ">=", now - 3600000); + assert.operator(b["rt.end"], "<=", now); + } + else { + return this.skip(); + } + }); + + it("Should be a 'navigation' (if NavTiming supported)", function() { + var b = tf.lastBeacon(); + + if (window.performance && window.performance.timing) { + assert.equal(b["rt.start"], "navigation"); + } + else { + return this.skip(); + } + }); + + it("Should be a 'none' (if NavTiming not supported)", function() { + var b = tf.lastBeacon(); + + if (!(window.performance && window.performance.timing)) { + assert.equal(b["rt.start"], "none"); + } + else { + return this.skip(); + } + }); + + it("Should have set pr=1", function() { + var b = tf.lastBeacon(); + + assert.equal(b.pr, "1"); + }); +}); diff --git a/tests/page-templates/05-angular/00-simple.html b/tests/page-templates/05-angular/00-simple.html new file mode 100644 index 000000000..9ff4ac060 --- /dev/null +++ b/tests/page-templates/05-angular/00-simple.html @@ -0,0 +1,29 @@ +<%= header %> +<%= boomerangScript %> + + + + + +
+ + + +<%= footer %> diff --git a/tests/page-templates/05-angular/00-simple.js b/tests/page-templates/05-angular/00-simple.js new file mode 100644 index 000000000..20c0402a3 --- /dev/null +++ b/tests/page-templates/05-angular/00-simple.js @@ -0,0 +1,11 @@ +/* eslint-env mocha */ +/* +* This app uses a delayed angular.bootstrap (and no ng-app) +* directive. +*/ +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["angular", "ng339", "modules", "app", "angular_imgs", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2"]); + +describe("e2e/05-angular/00-simple", function() { + BOOMR_test.templates.SPA["00-simple"](); +}); diff --git a/tests/page-templates/05-angular/03-ng-app.html b/tests/page-templates/05-angular/03-ng-app.html new file mode 100644 index 000000000..b64e605fe --- /dev/null +++ b/tests/page-templates/05-angular/03-ng-app.html @@ -0,0 +1,25 @@ +<%= header %> +<%= boomerangScript %> + + + + +
+
+
+
+ + +<%= footer %> diff --git a/tests/page-templates/05-angular/03-ng-app.js b/tests/page-templates/05-angular/03-ng-app.js new file mode 100644 index 000000000..c9657eaf4 --- /dev/null +++ b/tests/page-templates/05-angular/03-ng-app.js @@ -0,0 +1,57 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +/* +* This app uses a ng-app directive instead of angular.bootstrap() +*/ +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["angular", "ng339", "modules", "app", "angular_imgs", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2"]); + +describe("e2e/05-angular/03-ng-app", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have only sent one beacon", function() { + // only one beacon should've been sent + assert.equal(tf.beacons.length, 1); + }); + + it("Should take as long as the longest img load (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + t.validateBeaconWasSentAfter(0, "img.jpg", 200, 3000, 30000, true); + } + }); + + it("Should not have a load time (if MutationObserver is supported but NavigationTiming is not)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() === "undefined") { + var b = tf.lastBeacon(); + + assert.equal(b.t_done, undefined); + } + }); + + it("Should take as long as the XHRs (if MutationObserver is not supported but NavigationTiming is)", function() { + if (!t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + t.validateBeaconWasSentAfter(0, "widgets.json", 200, 0, 30000, true); + } + }); + + it("Shouldn't have a load time (if MutationObserver and NavigationTiming are not supported)", function() { + if (!t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() === "undefined") { + var b = tf.lastBeacon(); + + assert.equal(b.t_done, undefined); + assert.equal(b["rt.start"], "none"); + } + }); + + it("Should have sent the http.initiator as 'spa_hard'", function() { + var b = tf.lastBeacon(); + + assert.equal(b["http.initiator"], "spa_hard"); + }); +}); diff --git a/tests/page-templates/05-angular/04-route-change.html b/tests/page-templates/05-angular/04-route-change.html new file mode 100644 index 000000000..fe32830d0 --- /dev/null +++ b/tests/page-templates/05-angular/04-route-change.html @@ -0,0 +1,32 @@ +<%= header %> +<%= boomerangScript %> + + + + + + +
+
+
+
+ + + +<%= footer %> diff --git a/tests/page-templates/05-angular/04-route-change.js b/tests/page-templates/05-angular/04-route-change.js new file mode 100644 index 000000000..c2614d379 --- /dev/null +++ b/tests/page-templates/05-angular/04-route-change.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["angular", "ng339", "angular_imgs", "angular_html5_mode", "angular_nav_routes", "modules", "app", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2", "i"]); + +describe("e2e/05-angular/04-route-change", function() { + BOOMR_test.templates.SPA["04-route-change"](); +}); diff --git a/tests/page-templates/05-angular/05-route-change-hashtags.html b/tests/page-templates/05-angular/05-route-change-hashtags.html new file mode 100644 index 000000000..f47ac07fd --- /dev/null +++ b/tests/page-templates/05-angular/05-route-change-hashtags.html @@ -0,0 +1,32 @@ +<%= header %> +<%= boomerangScript %> + + + + + + +
+
+
+
+ + + +<%= footer %> diff --git a/tests/page-templates/05-angular/05-route-change-hashtags.js b/tests/page-templates/05-angular/05-route-change-hashtags.js new file mode 100644 index 000000000..91f5b2649 --- /dev/null +++ b/tests/page-templates/05-angular/05-route-change-hashtags.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["angular", "ng339", "angular_imgs", "angular_html5_mode", "angular_nav_routes", "modules", "app", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2", "i"]); + +describe("e2e/05-angular/05-route-change-hashtags", function() { + BOOMR_test.templates.SPA["05-route-change-hashtags"](); +}); diff --git a/tests/page-templates/05-angular/06-hard-nav-resources.html b/tests/page-templates/05-angular/06-hard-nav-resources.html new file mode 100644 index 000000000..68fbbb7f8 --- /dev/null +++ b/tests/page-templates/05-angular/06-hard-nav-resources.html @@ -0,0 +1,28 @@ +<%= header %> +<%= boomerangScript %> + + + + + +
+ + + +<%= footer %> diff --git a/tests/page-templates/05-angular/06-hard-nav-resources.js b/tests/page-templates/05-angular/06-hard-nav-resources.js new file mode 100644 index 000000000..366ca33bf --- /dev/null +++ b/tests/page-templates/05-angular/06-hard-nav-resources.js @@ -0,0 +1,13 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +/* +* This app uses a delayed angular.bootstrap (and no ng-app) +* directive. +*/ +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["angular", "ng339", "modules", "app", "angular_imgs", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2"]); + +describe("e2e/05-angular/06-hard-nav-resources", function() { + BOOMR_test.templates.SPA["06-hard-nav-resources"](); +}); diff --git a/tests/page-templates/05-angular/07-soft-nav-resources.html b/tests/page-templates/05-angular/07-soft-nav-resources.html new file mode 100644 index 000000000..f10fe3126 --- /dev/null +++ b/tests/page-templates/05-angular/07-soft-nav-resources.html @@ -0,0 +1,37 @@ +<%= header %> +<%= boomerangScript %> + + + + + + +
+
+
+
+ + + +<%= footer %> diff --git a/tests/page-templates/05-angular/07-soft-nav-resources.js b/tests/page-templates/05-angular/07-soft-nav-resources.js new file mode 100644 index 000000000..377ae4228 --- /dev/null +++ b/tests/page-templates/05-angular/07-soft-nav-resources.js @@ -0,0 +1,13 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +/* +* This app uses a delayed angular.bootstrap (and no ng-app) +* directive. +*/ +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["angular", "ng339", "angular_html5_mode", "angular_nav_routes", "modules", "app", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2", "i"]); + +describe("e2e/05-angular/07-soft-nav-resources", function() { + BOOMR_test.templates.SPA["07-soft-nav-resources"](); +}); diff --git a/tests/page-templates/05-angular/08-no-images.html b/tests/page-templates/05-angular/08-no-images.html new file mode 100644 index 000000000..4392c7342 --- /dev/null +++ b/tests/page-templates/05-angular/08-no-images.html @@ -0,0 +1,25 @@ +<%= header %> +<%= boomerangScript %> + + + + +
+
+
+
+ + + +<%= footer %> diff --git a/tests/page-templates/05-angular/08-no-images.js b/tests/page-templates/05-angular/08-no-images.js new file mode 100644 index 000000000..db1ea6d3a --- /dev/null +++ b/tests/page-templates/05-angular/08-no-images.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["angular", "ng339", "modules", "app", "angular_imgs", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2"]); + +describe("e2e/05-angular/08-no-images", function() { + BOOMR_test.templates.SPA["08-no-images"](); +}); diff --git a/tests/page-templates/05-angular/09-autoxhr-after-load.html b/tests/page-templates/05-angular/09-autoxhr-after-load.html new file mode 100644 index 000000000..ea37ce3ef --- /dev/null +++ b/tests/page-templates/05-angular/09-autoxhr-after-load.html @@ -0,0 +1,65 @@ +<%= header %> +<%= boomerangScript %> + + + + +
+
+
+
+ + + +<%= footer %> diff --git a/tests/page-templates/05-angular/09-autoxhr-after-load.js b/tests/page-templates/05-angular/09-autoxhr-after-load.js new file mode 100644 index 000000000..358ae2900 --- /dev/null +++ b/tests/page-templates/05-angular/09-autoxhr-after-load.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["angular", "ng339", "modules", "app", "xhr", "beaconNum", "angular_imgs", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2"]); + +describe("e2e/05-angular/09-autoxhr-after-load", function() { + BOOMR_test.templates.SPA["09-autoxhr-after-load"](); +}); diff --git a/tests/page-templates/05-angular/10-autoxhr-overlapping.html b/tests/page-templates/05-angular/10-autoxhr-overlapping.html new file mode 100644 index 000000000..6fd5c9cb7 --- /dev/null +++ b/tests/page-templates/05-angular/10-autoxhr-overlapping.html @@ -0,0 +1,61 @@ +<%= header %> +<%= boomerangScript %> + + + + +
+
+
+
+ + + +<%= footer %> diff --git a/tests/page-templates/05-angular/10-autoxhr-overlapping.js b/tests/page-templates/05-angular/10-autoxhr-overlapping.js new file mode 100644 index 000000000..9d6eea4e3 --- /dev/null +++ b/tests/page-templates/05-angular/10-autoxhr-overlapping.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["angular", "ng339", "modules", "app", "angular_imgs", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2"]); + +describe("e2e/05-angular/10-autoxhr-overlapping", function() { + BOOMR_test.templates.SPA["10-autoxhr-overlapping"](); +}); diff --git a/tests/page-templates/05-angular/100-angular-14-route-change.html b/tests/page-templates/05-angular/100-angular-14-route-change.html new file mode 100644 index 000000000..39be28143 --- /dev/null +++ b/tests/page-templates/05-angular/100-angular-14-route-change.html @@ -0,0 +1,32 @@ +<%= header %> +<%= boomerangScript %> + + + + + + +
+
+
+
+ + + +<%= footer %> diff --git a/tests/page-templates/05-angular/100-angular-14-route-change.js b/tests/page-templates/05-angular/100-angular-14-route-change.js new file mode 100644 index 000000000..a46388107 --- /dev/null +++ b/tests/page-templates/05-angular/100-angular-14-route-change.js @@ -0,0 +1,15 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert,angular */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["angular", "ng339", "angular_imgs", "angular_html5_mode", "angular_nav_routes", "modules", "app", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2", "i"]); + +describe("e2e/05-angular/100-angular-14-route-change", function() { + // use tests from #4 + BOOMR_test.templates.SPA["04-route-change"](); + + it("Should be on version 1.4.x", function() { + assert.equal(1, angular.version.major); + assert.equal(4, angular.version.minor); + }); +}); diff --git a/tests/page-templates/05-angular/101-angular-15-route-change.html b/tests/page-templates/05-angular/101-angular-15-route-change.html new file mode 100644 index 000000000..08fbc80a1 --- /dev/null +++ b/tests/page-templates/05-angular/101-angular-15-route-change.html @@ -0,0 +1,32 @@ +<%= header %> +<%= boomerangScript %> + + + + + + +
+
+
+
+ + + +<%= footer %> diff --git a/tests/page-templates/05-angular/101-angular-15-route-change.js b/tests/page-templates/05-angular/101-angular-15-route-change.js new file mode 100644 index 000000000..b3ac8db81 --- /dev/null +++ b/tests/page-templates/05-angular/101-angular-15-route-change.js @@ -0,0 +1,15 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert,angular */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["angular", "ng339", "angular_imgs", "angular_html5_mode", "angular_nav_routes", "modules", "app", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2", "i"]); + +describe("e2e/05-angular/101-angular-15-route-change", function() { + // use tests from #4 + BOOMR_test.templates.SPA["04-route-change"](); + + it("Should be on version 1.5.x", function() { + assert.equal(1, angular.version.major); + assert.equal(5, angular.version.minor); + }); +}); diff --git a/tests/page-templates/05-angular/102-ui-router.html b/tests/page-templates/05-angular/102-ui-router.html new file mode 100644 index 000000000..e56d5dfce --- /dev/null +++ b/tests/page-templates/05-angular/102-ui-router.html @@ -0,0 +1,32 @@ +<%= header %> +<%= boomerangScript %> + + + + + + +
+ + +<%= footer %> diff --git a/tests/page-templates/05-angular/102-ui-router.js b/tests/page-templates/05-angular/102-ui-router.js new file mode 100644 index 000000000..1c6912057 --- /dev/null +++ b/tests/page-templates/05-angular/102-ui-router.js @@ -0,0 +1,119 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["angular", "ng339", "angular_imgs", "angular_html5_mode", "angular_nav_routes", "handler", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2", "i"]); + +describe("e2e/05-angular/102-ui-router", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + var pathName = window.location.pathname; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent three beacons", function() { + assert.equal(tf.beacons.length, 3); + }); + + it("Should have sent the first beacon as http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + + it("Should have sent all subsequent beacons as http.initiator = spa", function() { + for (var i = 1; i < 2; i++) { + assert.equal(tf.beacons[i]["http.initiator"], "spa"); + } + }); + + // + // Beacon 1 + // + it("Should have sent the first beacon for " + pathName, function() { + var b = tf.beacons[0]; + + assert.isTrue(b.u.indexOf(pathName) !== -1); + }); + + it("Should take as long as the longest img load (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + t.validateBeaconWasSentAfter(0, "img.jpg&id=home", 500, 3000, 30000, 0); + } + }); + + it("Should not have a load time (if MutationObserver is supported but NavigationTiming is not)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() === "undefined") { + var b = tf.beacons[0]; + + assert.equal(b.t_done, undefined); + } + }); + + it("Should take as long as the XHRs (if MutationObserver is not supported but NavigationTiming is)", function() { + if (!t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + t.validateBeaconWasSentAfter(0, "widgets.json", 500, 0, 30000, false); + } + }); + + it("Shouldn't have a load time (if MutationObserver and NavigationTiming are not supported)", function() { + if (!t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() === "undefined") { + var b = tf.beacons[0]; + + assert.equal(b.t_done, undefined); + assert.equal(b["rt.start"], "none"); + } + }); + + // + // Beacon 2 + // + it("Should have sent the second beacon for /widgets/1", function() { + var b = tf.beacons[1]; + + assert.isTrue(b.u.indexOf("/widgets/1") !== -1); + }); + + it("Should have sent the second beacon with a timestamp of at least 1 second (if MutationObserver is supported)", function() { + if (t.isMutationObserverSupported()) { + // because of the widget IMG delaying 1 second + var b = tf.beacons[1]; + + assert.operator(b.t_done, ">=", 1000); + } + }); + + it("Should have sent the second beacon with a timestamp of at least 1 millisecond (if MutationObserver is not supported)", function() { + if (!t.isMutationObserverSupported()) { + // because of the widget IMG delaying 1 second but we couldn't track it because no MO support + var b = tf.beacons[1]; + + assert.operator(b.t_done, ">=", 0); + } + }); + + // + // Beacon 3 + // + it("Should have sent the third beacon for " + pathName, function() { + var b = tf.beacons[2]; + + assert.isTrue(b.u.indexOf(pathName) !== -1); + }); + + it("Should have sent the third with a timestamp of at least 3 seconds (if MutationObserver is supported)", function() { + if (t.isMutationObserverSupported()) { + var b = tf.beacons[2]; + + assert.operator(b.t_done, ">=", 3000); + } + }); + + it("Should have sent the third with a timestamp of under 1 second (if MutationObserver is not supported)", function() { + if (!t.isMutationObserverSupported()) { + var b = tf.beacons[2]; + + assert.operator(b.t_done, "<=", 1000); + } + }); +}); diff --git a/tests/page-templates/05-angular/105-hard-redirect.html b/tests/page-templates/05-angular/105-hard-redirect.html new file mode 100644 index 000000000..0444377a3 --- /dev/null +++ b/tests/page-templates/05-angular/105-hard-redirect.html @@ -0,0 +1,20 @@ +<%= header %> +<%= boomerangScript %> + + + +<%= footer %> diff --git a/tests/page-templates/05-angular/105-hard-redirect.js b/tests/page-templates/05-angular/105-hard-redirect.js new file mode 100644 index 000000000..44f18d8ba --- /dev/null +++ b/tests/page-templates/05-angular/105-hard-redirect.js @@ -0,0 +1,34 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["onBeaconHandler", "testFrame"]); + +describe("e2e/05-angular/105-hard-redirect", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + var assert = window.chai.assert; + + it("Should have sent one beacon", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 1); + }); + + it("Should have included the redirection time in t_resp (if NavigationTiming is supported)", function() { + if (t.isNavigationTimingSupported()) { + assert.operator(tf.beacons[0].t_resp, ">=", 3000); + } + }); + + it("Should not have a t_resp (if NavigationTiming is not supported)", function() { + if (!t.isNavigationTimingSupported()) { + assert.isUndefined(tf.beacons[0].t_resp); + } + }); + + it("Should have t_resp + t_page = t_done (if NavigationTiming is supported)", function() { + if (t.isNavigationTimingSupported()) { + assert.equal(tf.beacons[0].t_done, tf.beacons[0].t_resp + tf.beacons[0].t_page); + } + }); +}); diff --git a/tests/page-templates/05-angular/106-backendtime-script.html b/tests/page-templates/05-angular/106-backendtime-script.html new file mode 100644 index 000000000..e1db62fb2 --- /dev/null +++ b/tests/page-templates/05-angular/106-backendtime-script.html @@ -0,0 +1,52 @@ +<%= header %> +<%= boomerangScript %> + + + + +
+
+
+
+ + +<%= footer %> diff --git a/tests/page-templates/05-angular/106-backendtime-script.js b/tests/page-templates/05-angular/106-backendtime-script.js new file mode 100644 index 000000000..63ef122a4 --- /dev/null +++ b/tests/page-templates/05-angular/106-backendtime-script.js @@ -0,0 +1,31 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["angular", "ng339", "modules", "app", "angular_imgs", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2", "i"]); + +describe("e2e/05-angular/106-backendtime-script", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent two beacons", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 2); + }); + + it("Should have a t_resp <= t_done on second beacon (if NavigationTiming is supported)", function() { + if (t.isNavigationTimingSupported()) { + var b = tf.beacons[1]; + + assert.operator(parseInt(b.t_resp, 10), "<=", parseInt(b.t_done, 10)); + } + }); + + it("Should have a t_resp > 0 on second beacon (if NavigationTiming is supported)", function() { + if (t.isNavigationTimingSupported()) { + var b = tf.beacons[1]; + + assert.operator(parseInt(b.t_resp, 10), ">", 0); + } + }); +}); diff --git a/tests/page-templates/05-angular/107-hard-browser-bugs.html b/tests/page-templates/05-angular/107-hard-browser-bugs.html new file mode 100644 index 000000000..0c8137845 --- /dev/null +++ b/tests/page-templates/05-angular/107-hard-browser-bugs.html @@ -0,0 +1,46 @@ +<%= header %> +<%= boomerangScript %> + + + + + + +
+ + +<%= footer %> diff --git a/tests/page-templates/05-angular/107-hard-browser-bugs.js b/tests/page-templates/05-angular/107-hard-browser-bugs.js new file mode 100644 index 000000000..ad5707ecd --- /dev/null +++ b/tests/page-templates/05-angular/107-hard-browser-bugs.js @@ -0,0 +1,33 @@ +/* eslint-env mocha */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["angular", "ng339", "modules", "app", "angular_imgs", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2"]); + +describe("e2e/05-angular/107-hard-browser-bugs", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should not have t_resp (if NavigationTiming is supported)", function() { + if (t.isNavigationTimingSupported()) { + var b = tf.lastBeacon(); + + assert.isUndefined(b.t_resp); + } + }); + + it("Should not have t_page (if NavigationTiming is supported)", function() { + if (t.isNavigationTimingSupported()) { + var b = tf.lastBeacon(); + + assert.isUndefined(b.t_page); + } + }); + + it("Should have nt_bad (if NavigationTiming is supported)", function() { + if (t.isNavigationTimingSupported()) { + var b = tf.lastBeacon(); + + assert.isDefined(b.nt_bad); + } + }); +}); diff --git a/tests/page-templates/05-angular/108-iframe-wait-for-spa.html b/tests/page-templates/05-angular/108-iframe-wait-for-spa.html new file mode 100644 index 000000000..21aea0da7 --- /dev/null +++ b/tests/page-templates/05-angular/108-iframe-wait-for-spa.html @@ -0,0 +1,17 @@ +<%= header %> +<%= boomerangScript %> + + + + + +<%= footer %> diff --git a/tests/page-templates/05-angular/108-iframe-wait-for-spa.js b/tests/page-templates/05-angular/108-iframe-wait-for-spa.js new file mode 100644 index 000000000..71b2cff47 --- /dev/null +++ b/tests/page-templates/05-angular/108-iframe-wait-for-spa.js @@ -0,0 +1,59 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,assert,describe,it */ + +describe("e2e/05-angular/108-iframe-wait-for-spa", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + function getIFrameBeacon(id) { + return document.getElementById(id).contentWindow.BOOMR.plugins.TestFramework.lastBeacon(); + } + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have only sent one beacon", function(done) { + // only one beacon should've been sent + t.ensureBeaconCount(done, 1); + }); + + it("Should have page_ready (pr) flag", function() { + var b = tf.lastBeacon(); + + assert.equal(b.pr, "1"); + }); + + it("Should have ifdl.done param and be greater than the rt.end of the slowest iframe", function() { + var b = tf.lastBeacon(), + bf1 = getIFrameBeacon("frame1"); + + assert.isDefined(b["ifdl.done"]); + assert.operator(b["ifdl.done"], ">", bf1["rt.end"]); // will be at least 1s more due to spa timeout delay + }); + + it("Should have ifdl.ct param", function() { + var b = tf.lastBeacon(); + + assert.equal(b["ifdl.ct"], "1"); + }); + + it("Should have ifdl.r param", function() { + var b = tf.lastBeacon(); + + assert.equal(b["ifdl.r"], "0"); + }); + + it("Should have ifdl.mon param", function() { + var b = tf.lastBeacon(); + + assert.equal(b["ifdl.mon"], "1"); + }); + + it("Should have rt.end of the slowest iframe", function() { + var b = tf.lastBeacon(), + bf1 = getIFrameBeacon("frame1"); + + assert.equal(b["rt.end"], bf1["rt.end"]); + }); +}); diff --git a/tests/page-templates/05-angular/108-location-change-only.html b/tests/page-templates/05-angular/108-location-change-only.html new file mode 100644 index 000000000..a17a05239 --- /dev/null +++ b/tests/page-templates/05-angular/108-location-change-only.html @@ -0,0 +1,33 @@ +<%= header %> +<%= boomerangScript %> + + + + + + +
+
+
+
+ + +<%= footer %> diff --git a/tests/page-templates/05-angular/108-location-change-only.js b/tests/page-templates/05-angular/108-location-change-only.js new file mode 100644 index 000000000..6813f1bba --- /dev/null +++ b/tests/page-templates/05-angular/108-location-change-only.js @@ -0,0 +1,153 @@ +/* eslint-env mocha */ +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["angular", "ng339", "angular_imgs", "angular_html5_mode", "angular_nav_routes", "angular_no_route", "modules", "app", "i"]); + +describe("e2e/05-angular/108-location-change-only", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + var pathName = window.location.pathname; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent three beacons", function() { + assert.equal(tf.beacons.length, 3); + }); + + it("Should have sent the first beacon as http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + + it("Should have sent all subsequent beacons as http.initiator = spa", function() { + for (var i = 1; i < 2; i++) { + assert.equal(tf.beacons[i]["http.initiator"], "spa"); + } + }); + + it("Should have sent all subsequent beacons have rt.nstart = navigationTiming (if NavigationTiming is supported)", function() { + if (typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + for (var i = 1; i < 2; i++) { + assert.equal(tf.beacons[i]["rt.nstart"], BOOMR.plugins.RT.navigationStart()); + } + } + }); + + it("Should not have Boomerang timings on SPA Soft beacons", function() { + for (var i = 1; i < 2; i++) { + if (tf.beacons[i].t_other) { + assert.equal(tf.beacons[i].t_other.indexOf("boomr_fb"), -1, "should not have boomr_fb"); + assert.equal(tf.beacons[i].t_other.indexOf("boomr_ld"), -1, "should not have boomr_ld"); + assert.equal(tf.beacons[i].t_other.indexOf("boomr_lat"), -1, "should not have boomr_lat"); + assert.equal(tf.beacons[i].t_other.indexOf("boomerang"), -1, "should not have boomerang"); + } + + // Boomerang and config timing parameters + assert.isUndefined(tf.beacons[i]["rt.bmr"]); + assert.isUndefined(tf.beacons[i]["rt.cnf"]); + } + }); + + // + // Beacon 1 + // + it("Should have sent the first beacon for " + pathName, function() { + var b = tf.beacons[0]; + + assert.isTrue(b.u.indexOf(pathName) !== -1); + }); + + it("Should have a load time (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + var b = tf.beacons[0]; + + assert.isDefined(b.t_done); + } + }); + + it("Should not have a load time (if MutationObserver is supported but NavigationTiming is not)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() === "undefined") { + var b = tf.beacons[0]; + + assert.equal(b.t_done, undefined); + } + }); + + it("Should take as long as the XHRs (if MutationObserver is not supported but NavigationTiming is)", function() { + if (!t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + t.validateBeaconWasSentAfter(0, "widgets.json", 500, 0, 30000, false); + } + }); + + it("Shouldn't have a load time (if MutationObserver and NavigationTiming are not supported)", function() { + if (!t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() === "undefined") { + var b = tf.beacons[0]; + + assert.equal(b.t_done, undefined); + assert.equal(b["rt.start"], "none"); + } + }); + + it("Should have a t_resp of the root page (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + var pt = window.performance.timing; + var b = tf.beacons[0]; + + assert.equal(b.t_resp, pt.responseStart - pt.navigationStart); + } + }); + + it("Should have a t_page of total - t_resp (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + var pt = window.performance.timing; + var b = tf.beacons[0]; + + assert.equal(b.t_page, b.t_done - b.t_resp); + } + }); + + // + // Beacon 2 + // + it("Should have sent the second beacon for /nothing", function() { + var b = tf.beacons[1]; + + assert.isTrue(b.u.indexOf("/nothing") !== -1); + }); + + it("Should have sent the second beacon with a timestamp of ~0 seconds (if MutationObserver is supported)", function() { + if (t.isMutationObserverSupported()) { + var b = tf.beacons[1]; + + assert.closeTo(b.t_done, 0, 50); + } + }); + + // + // Beacon 3 + // + it("Should have sent the third beacon for " + pathName, function() { + var b = tf.beacons[2]; + + assert.isTrue(b.u.indexOf(pathName) !== -1); + }); + + it("Should have sent the third with a timestamp of at around 0 seconds (if MutationObserver is supported)", function() { + if (t.isMutationObserverSupported()) { + var b = tf.beacons[2]; + + assert.closeTo(b.t_done, 0, 50); + } + }); + + it("Should have set the same Page ID (pid) on all beacons", function() { + var pid = tf.beacons[0].pid; + + for (var i = 0; i < tf.beacons.length; i++) { + var b = tf.beacons[i]; + + assert.equal(b.pid, pid); + } + }); +}); diff --git a/tests/page-templates/05-angular/109-multiple-iframes.html b/tests/page-templates/05-angular/109-multiple-iframes.html new file mode 100644 index 000000000..05a81aa55 --- /dev/null +++ b/tests/page-templates/05-angular/109-multiple-iframes.html @@ -0,0 +1,20 @@ +<%= header %> +<%= boomerangScript %> + + + + + + + + +<%= footer %> diff --git a/tests/page-templates/05-angular/109-multiple-iframes.js b/tests/page-templates/05-angular/109-multiple-iframes.js new file mode 100644 index 000000000..03d663411 --- /dev/null +++ b/tests/page-templates/05-angular/109-multiple-iframes.js @@ -0,0 +1,65 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,describe,it */ + +describe("e2e/05-angular/109-multiple-iframes", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + function getIFrameBeacon(id) { + return document.getElementById(id).contentWindow.BOOMR.plugins.TestFramework.lastBeacon(); + } + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have only sent one beacon", function(done) { + // only one beacon should've been sent + t.ensureBeaconCount(done, 1); + }); + + it("Should have page_ready (pr) flag", function() { + var b = tf.lastBeacon(); + + assert.equal(b.pr, "1"); + }); + + it("Should have ifdl.done param and be greater than the rt.end of the slowest iframe", function() { + var b = tf.lastBeacon(), + bf1 = getIFrameBeacon("frame1"), + bf2 = getIFrameBeacon("frame2"); + + assert.isDefined(b["ifdl.done"]); + var loadEnd = Math.max(bf1["rt.end"], bf2["rt.end"]); + + assert.operator(b["ifdl.done"], ">", loadEnd); // will be at least 1s more due to spa timeout delay + }); + + it("Should have ifdl.ct param", function() { + var b = tf.lastBeacon(); + + assert.equal(b["ifdl.ct"], "2"); + }); + + it("Should have ifdl.r param", function() { + var b = tf.lastBeacon(); + + assert.equal(b["ifdl.r"], "0"); + }); + + it("Should have ifdl.mon param", function() { + var b = tf.lastBeacon(); + + assert.equal(b["ifdl.mon"], "2"); + }); + + it("Should have rt.end of the slowest iframe", function() { + var b = tf.lastBeacon(), + bf1 = getIFrameBeacon("frame1"), + bf2 = getIFrameBeacon("frame2"); + var loadEnd = Math.max(bf1["rt.end"], bf2["rt.end"]); + + assert.equal(b["rt.end"], loadEnd); + }); +}); + diff --git a/tests/page-templates/05-angular/11-autoxhr-trigger-additional.html b/tests/page-templates/05-angular/11-autoxhr-trigger-additional.html new file mode 100644 index 000000000..38a60298b --- /dev/null +++ b/tests/page-templates/05-angular/11-autoxhr-trigger-additional.html @@ -0,0 +1,50 @@ +<%= header %> +<%= boomerangScript %> + + + + +
+
+
+
+ + + +<%= footer %> diff --git a/tests/page-templates/05-angular/11-autoxhr-trigger-additional.js b/tests/page-templates/05-angular/11-autoxhr-trigger-additional.js new file mode 100644 index 000000000..e7714e884 --- /dev/null +++ b/tests/page-templates/05-angular/11-autoxhr-trigger-additional.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["angular", "ng339", "modules", "app", "angular_imgs", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2"]); + +describe("e2e/05-angular/11-autoxhr-trigger-additional", function() { + BOOMR_test.templates.SPA["11-autoxhr-trigger-additional"](); +}); diff --git a/tests/page-templates/05-angular/111-iframe-delay-with-child-spa-routes.html b/tests/page-templates/05-angular/111-iframe-delay-with-child-spa-routes.html new file mode 100644 index 000000000..e912a3516 --- /dev/null +++ b/tests/page-templates/05-angular/111-iframe-delay-with-child-spa-routes.html @@ -0,0 +1,15 @@ +<%= header %> +<%= boomerangScript %> + + + +<%= footer %> diff --git a/tests/page-templates/05-angular/111-iframe-delay-with-child-spa-routes.js b/tests/page-templates/05-angular/111-iframe-delay-with-child-spa-routes.js new file mode 100644 index 000000000..f27dd5993 --- /dev/null +++ b/tests/page-templates/05-angular/111-iframe-delay-with-child-spa-routes.js @@ -0,0 +1,60 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,describe,it */ + +describe("e2e/05-angular/111-iframe-delay-with-child-spa-routes", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + function getIFrameBeacon(id) { + return document.getElementById(id).contentWindow.BOOMR.plugins.TestFramework.lastBeacon(); + } + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have only sent one beacon", function() { + // only one beacon should've been sent + assert.equal(tf.beacons.length, 1); + }); + + it("Should have page_ready (pr) flag", function() { + var b = tf.lastBeacon(); + + assert.equal(b.pr, "1"); + }); + + it("Should have ifdl.done param and be greater than the rt.end of the slowest iframe", function() { + var b = tf.lastBeacon(), + bf1 = getIFrameBeacon("frame1"); + + assert.isDefined(b["ifdl.done"]); + assert.operator(b["ifdl.done"], ">", bf1["rt.end"]); // will be at least 1s more due to spa timeout delay + }); + + it("Should have ifdl.ct param", function() { + var b = tf.lastBeacon(); + + assert.equal(b["ifdl.ct"], "1"); + }); + + it("Should have ifdl.r param", function() { + var b = tf.lastBeacon(); + + assert.equal(b["ifdl.r"], "0"); + }); + + it("Should have ifdl.mon param", function() { + var b = tf.lastBeacon(); + + assert.equal(b["ifdl.mon"], "1"); + }); + + it("Should have rt.end of the slowest iframe", function() { + var b = tf.lastBeacon(), + bf1 = getIFrameBeacon("frame1"); + + assert.equal(b["rt.end"], bf1["rt.end"]); + }); +}); + diff --git a/tests/page-templates/05-angular/112-autoxhr-open-without-send.html b/tests/page-templates/05-angular/112-autoxhr-open-without-send.html new file mode 100644 index 000000000..451a4fea6 --- /dev/null +++ b/tests/page-templates/05-angular/112-autoxhr-open-without-send.html @@ -0,0 +1,59 @@ +<%= header %> +<%= boomerangScript %> + + + + + + +
+
+
+
+ + +<%= footer %> diff --git a/tests/page-templates/05-angular/112-autoxhr-open-without-send.js b/tests/page-templates/05-angular/112-autoxhr-open-without-send.js new file mode 100644 index 000000000..d6a4fb8ee --- /dev/null +++ b/tests/page-templates/05-angular/112-autoxhr-open-without-send.js @@ -0,0 +1,48 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,describe,it */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["angular", "ng339", "angular_imgs", "angular_html5_mode", "angular_nav_routes", "modules", "app", "img", "xhr", "beaconCount", "timerid", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2", "xhr2", "i"]); + +describe("e2e/05-angular/112-autoxhr-open-without-send", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + clearTimeout(window.timerid); + }); + + it("Should have sent four beacons", function() { + assert.equal(tf.beacons.length, 4); + }); + + it("Should have sent the first beacon as http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + + it("Should have beacon 1 as a spa_hard", function() { + var b = tf.beacons[0]; + + assert.equal(b["http.initiator"], "spa_hard"); + }); + + it("Should have beacon 2 as a spa", function() { + var b = tf.beacons[1]; + + assert.equal(b["http.initiator"], "spa"); + }); + + it("Should have beacon 3 as a spa", function() { + var b = tf.beacons[2]; + + assert.equal(b["http.initiator"], "spa"); + }); + + it("Should have beacon 4 as an xhr", function() { + var b = tf.beacons[3]; + + assert.equal(b["http.initiator"], "xhr"); + }); +}); + diff --git a/tests/page-templates/05-angular/113-late-locationchangestart.html b/tests/page-templates/05-angular/113-late-locationchangestart.html new file mode 100644 index 000000000..41227dacb --- /dev/null +++ b/tests/page-templates/05-angular/113-late-locationchangestart.html @@ -0,0 +1,34 @@ +<%= header %> +<%= boomerangScript %> + + + + + + +
+ + +<%= footer %> diff --git a/tests/page-templates/05-angular/113-late-locationchangestart.js b/tests/page-templates/05-angular/113-late-locationchangestart.js new file mode 100644 index 000000000..2829f8c64 --- /dev/null +++ b/tests/page-templates/05-angular/113-late-locationchangestart.js @@ -0,0 +1,119 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["angular", "ng339", "angular_imgs", "angular_html5_mode", "angular_nav_routes", "angularBroadcastLocationAfterStateChange", "handler", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2", "i"]); + +describe("e2e/05-angular/113-late-locationchangestart", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + var pathName = window.location.pathname; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent three beacons", function() { + assert.equal(tf.beacons.length, 3); + }); + + it("Should have sent the first beacon as http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + + it("Should have sent all subsequent beacons as http.initiator = spa", function() { + for (var i = 1; i < 2; i++) { + assert.equal(tf.beacons[i]["http.initiator"], "spa"); + } + }); + + // + // Beacon 1 + // + it("Should have sent the first beacon for " + pathName, function() { + var b = tf.beacons[0]; + + assert.isTrue(b.u.indexOf(pathName) !== -1); + }); + + it("Should take as long as the longest img load (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + t.validateBeaconWasSentAfter(0, "img.jpg&id=home", 500, 3000, 30000, 0); + } + }); + + it("Should not have a load time (if MutationObserver is supported but NavigationTiming is not)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() === "undefined") { + var b = tf.beacons[0]; + + assert.equal(b.t_done, undefined); + } + }); + + it("Should take as long as the XHRs (if MutationObserver is not supported but NavigationTiming is)", function() { + if (!t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + t.validateBeaconWasSentAfter(0, "widgets.json", 500, 0, 30000, false); + } + }); + + it("Shouldn't have a load time (if MutationObserver and NavigationTiming are not supported)", function() { + if (!t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() === "undefined") { + var b = tf.beacons[0]; + + assert.equal(b.t_done, undefined); + assert.equal(b["rt.start"], "none"); + } + }); + + // + // Beacon 2 + // + it("Should have sent the second beacon for /widgets/1", function() { + var b = tf.beacons[1]; + + assert.isTrue(b.u.indexOf("/widgets/1") !== -1); + }); + + it("Should have sent the second beacon with a timestamp of at least 1 second (if MutationObserver is supported)", function() { + if (t.isMutationObserverSupported()) { + // because of the widget IMG delaying 1 second + var b = tf.beacons[1]; + + assert.operator(b.t_done, ">=", 1000); + } + }); + + it("Should have sent the second beacon with a timestamp of at least 1 millisecond (if MutationObserver is not supported)", function() { + if (!t.isMutationObserverSupported()) { + // because of the widget IMG delaying 1 second but we couldn't track it because no MO support + var b = tf.beacons[1]; + + assert.operator(b.t_done, ">=", 0); + } + }); + + // + // Beacon 3 + // + it("Should have sent the third beacon for " + pathName, function() { + var b = tf.beacons[2]; + + assert.isTrue(b.u.indexOf(pathName) !== -1); + }); + + it("Should have sent the third with a timestamp of at least 3 seconds (if MutationObserver is supported)", function() { + if (t.isMutationObserverSupported()) { + var b = tf.beacons[2]; + + assert.operator(b.t_done, ">=", 3000); + } + }); + + it("Should have sent the third with a timestamp of under 1 second (if MutationObserver is not supported)", function() { + if (!t.isMutationObserverSupported()) { + var b = tf.beacons[2]; + + assert.operator(b.t_done, "<=", 1000); + } + }); +}); diff --git a/tests/page-templates/05-angular/114-route-change-abld.html b/tests/page-templates/05-angular/114-route-change-abld.html new file mode 100644 index 000000000..49a3783c9 --- /dev/null +++ b/tests/page-templates/05-angular/114-route-change-abld.html @@ -0,0 +1,49 @@ +<%= header %> +<%= boomerangScript %> + + + + + + + +
+
+
+
+ + +<%= footer %> diff --git a/tests/page-templates/05-angular/114-route-change-abld.js b/tests/page-templates/05-angular/114-route-change-abld.js new file mode 100644 index 000000000..5f5c572ce --- /dev/null +++ b/tests/page-templates/05-angular/114-route-change-abld.js @@ -0,0 +1,299 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["angular", "ng339", "ResourceTimingDecompression", "angular_imgs", "angular_html5_mode", "modules", "app", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2", "nav1time", "nav2time", "i"]); + +describe("e2e/05-angular/114-route-change-abld", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + var pathName = window.location.pathname; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent three beacons", function() { + assert.equal(tf.beacons.length, 3); + }); + + it("Should have set the same Page ID (pid) on all beacons", function() { + var pid = tf.beacons[0].pid; + + for (var i = 0; i < tf.beacons.length; i++) { + var b = tf.beacons[i]; + + assert.equal(b.pid, pid); + } + }); + + it("Should have sent the first beacon as http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + + it("Should have sent all subsequent beacons as http.initiator = spa", function() { + for (var i = 1; i < 2; i++) { + assert.equal(tf.beacons[i]["http.initiator"], "spa"); + } + }); + + it("Should have sent all subsequent beacons have rt.nstart = navigationTiming (if NavigationTiming is supported)", function() { + if (typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + for (var i = 1; i < 2; i++) { + assert.equal(tf.beacons[i]["rt.nstart"], BOOMR.plugins.RT.navigationStart()); + } + } + }); + + it("Should not have Boomerang timings on SPA Soft beacons", function() { + for (var i = 1; i < 2; i++) { + if (tf.beacons[i].t_other) { + assert.equal(tf.beacons[i].t_other.indexOf("boomr_fb"), -1, "should not have boomr_fb"); + assert.equal(tf.beacons[i].t_other.indexOf("boomr_ld"), -1, "should not have boomr_ld"); + assert.equal(tf.beacons[i].t_other.indexOf("boomr_lat"), -1, "should not have boomr_lat"); + assert.equal(tf.beacons[i].t_other.indexOf("boomerang"), -1, "should not have boomerang"); + } + + // Boomerang and config timing parameters + assert.isUndefined(tf.beacons[i]["rt.bmr"]); + assert.isUndefined(tf.beacons[i]["rt.cnf"]); + } + }); + + // + // Beacon 1 + // + it("Should have sent the first beacon for " + pathName, function() { + var b = tf.beacons[0]; + + assert.isTrue(b.u.indexOf(pathName) !== -1); + }); + + it("Should have sent the first beacon with a load time of when the abort happened (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + assert.closeTo(tf.beacons[0].t_done, window.nav1time - BOOMR.plugins.RT.navigationStart(), 100); + } + }); + + it("Should have sent the first beacon without a load time (if MutationObserver is supported but NavigationTiming is not)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() === "undefined") { + var b = tf.beacons[0]; + + assert.equal(b.t_done, undefined); + assert.equal(b["rt.start"], "none"); + } + }); + + it("Should have sent the first beacon with a t_resp of the root page (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + var pt = window.performance.timing; + var b = tf.beacons[0]; + + assert.equal(b.t_resp, pt.responseStart - pt.navigationStart); + } + }); + + it("Should have sent the first beacon with a t_page of total - t_resp (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + var pt = window.performance.timing; + var b = tf.beacons[0]; + + assert.equal(b.t_page, b.t_done - b.t_resp); + } + }); + + it("Should have sent the first beacon with rt.quit and rt.abld (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + var b = tf.beacons[0]; + + assert.equal(b["rt.quit"], ""); + assert.equal(b["rt.abld"], ""); + } + }); + + it("Should have sent the first beacon with resources only from its nav (if ResourceTiming is supported)", function() { + if (t.isResourceTimingSupported()) { + var b = tf.beacons[0]; + + var resources = ResourceTimingDecompression.decompressResources(JSON.parse(b.restiming)); + + // should have widgets.json, home.html, app.js + assert.equal(resources.filter(function(r) { + return r.name.indexOf("widgets.json") !== -1; + }).length, 1, "Should have widgets.json"); + + assert.equal(resources.filter(function(r) { + return r.name.indexOf("home.html") !== -1; + }).length, 1, "Should have home.html"); + + assert.equal(resources.filter(function(r) { + return r.name.indexOf("app.js") !== -1; + }).length, 1, "Should have app.js"); + + // shouldn't have widgets.html + assert.equal(resources.filter(function(r) { + return r.name.indexOf("widget.html") !== -1; + }).length, 0, "Should not have widgets.html"); + } + }); + + // + // Beacon 2 + // + it("Should have sent the second beacon for /widgets/1", function() { + var b = tf.beacons[1]; + + assert.isTrue(b.u.indexOf("/widgets/1") !== -1); + }); + + it("Should have sent the second beacon with a timestamp of when the abort happened (if MutationObserver is supported)", function() { + if (t.isMutationObserverSupported()) { + var b = tf.beacons[1]; + + assert.closeTo(b.t_done, window.nav2time - b["rt.tstart"], 100); + } + }); + + it("Should have sent the second beacon with a timestamp of at least 1 millisecond (if MutationObserver is not supported)", function() { + if (!t.isMutationObserverSupported()) { + // because of the widget IMG delaying 1 second but we couldn't track it because no MO support + var b = tf.beacons[1]; + + assert.operator(b.t_done, ">=", 0); + } + }); + + it("Should have sent the second beacon with a t_resp value (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + var pt = window.performance.timing; + var b = tf.beacons[1]; + + assert.operator(b.t_resp, ">=", 0); + } + }); + + it("Should have sent the second beacon with a t_page of total - t_resp (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + var pt = window.performance.timing; + var b = tf.beacons[1]; + + assert.equal(b.t_page, b.t_done - b.t_resp); + } + }); + + it("Should have sent the second beacon with rt.quit and rt.abld (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + var b = tf.beacons[1]; + + assert.equal(b["rt.quit"], ""); + assert.equal(b["rt.abld"], ""); + } + }); + + it("Should have sent the second beacon with resources only from its nav (if ResourceTiming is supported)", function() { + if (t.isResourceTimingSupported()) { + var b = tf.beacons[1]; + + var resources = ResourceTimingDecompression.decompressResources(JSON.parse(b.restiming)); + + // should have widgets.html, widgets.json + assert.equal(resources.filter(function(r) { + return r.name.indexOf("widgets.json") !== -1; + }).length, 1, "Should have widgets.json"); + + assert.equal(resources.filter(function(r) { + return r.name.indexOf("widget.html") !== -1; + }).length, 1, "Shoud have widgets.html"); + + // should not have home.html, app.js + assert.equal(resources.filter(function(r) { + return r.name.indexOf("home.html") !== -1; + }).length, 0, "Should not have home.html"); + + assert.equal(resources.filter(function(r) { + return r.name.indexOf("app.js") !== -1; + }).length, 0, "Should not have app.js"); + } + }); + + // + // Beacon 3 + // + it("Should have sent the third beacon for " + pathName, function() { + var b = tf.beacons[2]; + + assert.isTrue(b.u.indexOf(pathName) !== -1); + }); + + it("Should have sent the third with a timestamp of at least 3 seconds (if MutationObserver is supported)", function() { + if (t.isMutationObserverSupported()) { + var b = tf.beacons[2]; + + assert.operator(b.t_done, ">=", 3000); + } + }); + + it("Should have sent the third with a timestamp of under 1 second (if MutationObserver is not supported)", function() { + if (!t.isMutationObserverSupported()) { + var b = tf.beacons[2]; + + assert.operator(b.t_done, "<=", 1000); + } + }); + + it("Should have sent the third beacon with a t_resp value (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + var pt = window.performance.timing; + var b = tf.beacons[2]; + + assert.operator(b.t_resp, ">=", 0); + } + }); + + it("Should have sent the third beacon with a t_page of total - t_resp (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + var pt = window.performance.timing; + var b = tf.beacons[2]; + + assert.equal(b.t_page, b.t_done - b.t_resp); + } + }); + + it("Should have sent the third beacon without rt.quit and rt.abld", function() { + var b = tf.beacons[2]; + + assert.equal(b["rt.quit"], undefined); + assert.equal(b["rt.abld"], undefined); + }); + + it("Should have sent the second beacon with resources only from its nav (if ResourceTiming is supported)", function() { + if (t.isResourceTimingSupported()) { + var b = tf.beacons[2]; + + var resources = ResourceTimingDecompression.decompressResources(JSON.parse(b.restiming)); + + // should have widgets.json, img.jpg + assert.equal(resources.filter(function(r) { + return r.name.indexOf("widgets.json") !== -1; + }).length, 1, "Should have widgets.json"); + + assert.equal(resources.filter(function(r) { + return r.name.indexOf("img.jpg") !== -1; + }).length, 1, "Should have img.jpg"); + + // should not have home.html, app.js + assert.equal(resources.filter(function(r) { + return r.name.indexOf("home.html") !== -1; + }).length, 0, "Should not have home.html"); + + assert.equal(resources.filter(function(r) { + return r.name.indexOf("app.js") !== -1; + }).length, 0, "Should not have app.js"); + + assert.equal(resources.filter(function(r) { + return r.name.indexOf("widget.html") !== -1; + }).length, 0, "Shoud not have widgets.html"); + } + }); +}); diff --git a/tests/page-templates/05-angular/115-no-url-change.html b/tests/page-templates/05-angular/115-no-url-change.html new file mode 100644 index 000000000..850c1bfd9 --- /dev/null +++ b/tests/page-templates/05-angular/115-no-url-change.html @@ -0,0 +1,63 @@ +<%= header %> +<%= boomerangScript %> + + + + + + +
+ + +<%= footer %> diff --git a/tests/page-templates/05-angular/115-no-url-change.js b/tests/page-templates/05-angular/115-no-url-change.js new file mode 100644 index 000000000..76f9b10bf --- /dev/null +++ b/tests/page-templates/05-angular/115-no-url-change.js @@ -0,0 +1,149 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, [ + "angular", + "ng339", + "angular_imgs", + "angular_html5_mode", + "angular_nav_routes", + "angular_nav_route_timeout", + "total_spa_navigations", + "total_spa_inits", + "total_spa_cancels", + "handler", + "custom_metric_1", + "custom_metric_2", + "custom_timer_1", + "custom_timer_2", + "angular_timerid", + "i" +]); + +describe("e2e/05-angular/115-no-url-change", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + var pathName = window.location.pathname; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent three beacons", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 3); + }); + + it("Should have sent the first beacon as http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + + it("Should have sent all subsequent beacons as http.initiator = spa", function() { + for (var i = 1; i < 2; i++) { + assert.equal(tf.beacons[i]["http.initiator"], "spa"); + } + }); + + it("Should have handled three spa_navigation events", function() { + assert.equal(window.total_spa_navigations, 3); + }); + + it("Should have handled four spa_init events", function() { + assert.equal(window.total_spa_inits, 4); + }); + + it("Should have handled one spa_cancel events", function() { + assert.equal(window.total_spa_cancels, 1); + }); + + // + // Beacon 1 + // + it("Should have sent the first beacon for " + pathName, function() { + var b = tf.beacons[0]; + + assert.isTrue(b.u.indexOf(pathName) !== -1); + }); + + it("Should take as long as the longest img load (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + t.validateBeaconWasSentAfter(0, "img.jpg&id=home", 500, 3000, 30000, 0); + } + }); + + it("Should not have a load time (if MutationObserver is supported but NavigationTiming is not)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() === "undefined") { + var b = tf.beacons[0]; + + assert.equal(b.t_done, undefined); + } + }); + + it("Should take as long as the XHRs (if MutationObserver is not supported but NavigationTiming is)", function() { + if (!t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + t.validateBeaconWasSentAfter(0, "widgets.json", 500, 0, 30000, false); + } + }); + + it("Shouldn't have a load time (if MutationObserver and NavigationTiming are not supported)", function() { + if (!t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() === "undefined") { + var b = tf.beacons[0]; + + assert.equal(b.t_done, undefined); + assert.equal(b["rt.start"], "none"); + } + }); + + // + // Beacon 2 + // + it("Should have sent the second beacon for /widgets/1", function() { + var b = tf.beacons[1]; + + assert.isTrue(b.u.indexOf("/widgets/1") !== -1); + }); + + it("Should have sent the second beacon with a timestamp of at least 1 second (if MutationObserver is supported)", function() { + if (t.isMutationObserverSupported()) { + // because of the widget IMG delaying 1 second + var b = tf.beacons[1]; + + assert.operator(b.t_done, ">=", 1000); + } + }); + + it("Should have sent the second beacon with a timestamp of at least 1 millisecond (if MutationObserver is not supported)", function() { + if (!t.isMutationObserverSupported()) { + // because of the widget IMG delaying 1 second but we couldn't track it because no MO support + var b = tf.beacons[1]; + + assert.operator(b.t_done, ">=", 0); + } + }); + + // + // Beacon 3 + // + it("Should have sent the third beacon for " + pathName, function() { + var b = tf.beacons[2]; + + assert.isTrue(b.u.indexOf(pathName) !== -1); + }); + + it("Should have sent the third with a timestamp of at least 3 seconds (if MutationObserver is supported)", function() { + if (t.isMutationObserverSupported()) { + var b = tf.beacons[2]; + + assert.operator(b.t_done, ">=", 3000); + } + }); + + it("Should have sent the third with a timestamp of under 1 second (if MutationObserver is not supported)", function() { + if (!t.isMutationObserverSupported()) { + var b = tf.beacons[2]; + + assert.operator(b.t_done, "<=", 1000); + } + }); +}); diff --git a/tests/page-templates/05-angular/116-autoxhr-xhrexcludes.html b/tests/page-templates/05-angular/116-autoxhr-xhrexcludes.html new file mode 100644 index 000000000..4c9f250c4 --- /dev/null +++ b/tests/page-templates/05-angular/116-autoxhr-xhrexcludes.html @@ -0,0 +1,56 @@ + +<%= header %> +<%= boomerangScript %> + + + + + + +
+
+
+
+ + +<%= footer %> diff --git a/tests/page-templates/05-angular/116-autoxhr-xhrexcludes.js b/tests/page-templates/05-angular/116-autoxhr-xhrexcludes.js new file mode 100644 index 000000000..41980441e --- /dev/null +++ b/tests/page-templates/05-angular/116-autoxhr-xhrexcludes.js @@ -0,0 +1,42 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,describe,it */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["angular", "ng339", "angular_imgs", "angular_html5_mode", "angular_nav_routes", "modules", "app", "timerid", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2", "i"]); + +describe("e2e/05-angular/116-autoxhr-xhrexcludes.js", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + clearTimeout(window.timerid); + }); + + it("Should have sent three beacons", function() { + assert.equal(tf.beacons.length, 3); + }); + + it("Should have sent the first beacon as http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + + it("Should have beacon 1 as a spa_hard", function() { + var b = tf.beacons[0]; + + assert.equal(b["http.initiator"], "spa_hard"); + }); + + it("Should have beacon 2 as a spa", function() { + var b = tf.beacons[1]; + + assert.equal(b["http.initiator"], "spa"); + }); + + it("Should have beacon 3 as a spa", function() { + var b = tf.beacons[2]; + + assert.equal(b["http.initiator"], "spa"); + }); +}); + diff --git a/tests/page-templates/05-angular/117-route-change-markcomplete.html b/tests/page-templates/05-angular/117-route-change-markcomplete.html new file mode 100644 index 000000000..cacb1ef66 --- /dev/null +++ b/tests/page-templates/05-angular/117-route-change-markcomplete.html @@ -0,0 +1,53 @@ +<%= header %> +<%= boomerangScript %> + + + + + + + +
+
+
+
+ + +<%= footer %> diff --git a/tests/page-templates/05-angular/117-route-change-markcomplete.js b/tests/page-templates/05-angular/117-route-change-markcomplete.js new file mode 100644 index 000000000..5bb9c159c --- /dev/null +++ b/tests/page-templates/05-angular/117-route-change-markcomplete.js @@ -0,0 +1,421 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["angular", "ng339", "ResourceTimingDecompression", "angular_imgs", "angular_html5_mode", "modules", "app", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2", "nav1time", "nav2time", "i"]); + +describe("e2e/05-angular/117-route-change-markcomplete", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + var pathName = window.location.pathname; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent three beacons", function() { + assert.equal(tf.beacons.length, 3); + }); + + it("Should have set the same Page ID (pid) on all beacons", function() { + var pid = tf.beacons[0].pid; + + for (var i = 0; i < tf.beacons.length; i++) { + var b = tf.beacons[i]; + + assert.equal(b.pid, pid); + } + }); + + it("Should have sent the first beacon as http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + + it("Should have sent all subsequent beacons as http.initiator = spa", function() { + for (var i = 1; i < 2; i++) { + assert.equal(tf.beacons[i]["http.initiator"], "spa"); + } + }); + + it("Should have sent all subsequent beacons have rt.nstart = navigationTiming (if NavigationTiming is supported)", function() { + if (typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + for (var i = 1; i < 2; i++) { + assert.equal(tf.beacons[i]["rt.nstart"], BOOMR.plugins.RT.navigationStart()); + } + } + else { + this.skip(); + } + }); + + it("Should not have Boomerang timings on SPA Soft beacons", function() { + for (var i = 1; i < 2; i++) { + if (tf.beacons[i].t_other) { + assert.equal(tf.beacons[i].t_other.indexOf("boomr_fb"), -1, "should not have boomr_fb"); + assert.equal(tf.beacons[i].t_other.indexOf("boomr_ld"), -1, "should not have boomr_ld"); + assert.equal(tf.beacons[i].t_other.indexOf("boomr_lat"), -1, "should not have boomr_lat"); + assert.equal(tf.beacons[i].t_other.indexOf("boomerang"), -1, "should not have boomerang"); + } + + // Boomerang and config timing parameters + assert.isUndefined(tf.beacons[i]["rt.bmr"]); + assert.isUndefined(tf.beacons[i]["rt.cnf"]); + } + }); + + // + // Beacon 1 + // + describe("Beacon 1 (spa_hard)", function() { + var i = 0; + + it("Should have sent the first beacon for " + pathName, function() { + var b = tf.beacons[i]; + + assert.isTrue(b.u.indexOf(pathName) !== -1); + }); + + it("Should have sent the first beacon with a load time of when the completion happened (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + assert.closeTo(tf.beacons[i].t_done, window.nav1time - BOOMR.plugins.RT.navigationStart(), 100); + } + else { + this.skip(); + } + }); + + it("Should have sent the first beacon without a load time (if MutationObserver is supported but NavigationTiming is not)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() === "undefined") { + var b = tf.beacons[i]; + + assert.equal(b.t_done, undefined); + assert.equal(b["rt.start"], "none"); + } + else { + this.skip(); + } + }); + + it("Should have sent the first beacon with a t_resp of the root page (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + var pt = window.performance.timing; + var b = tf.beacons[i]; + + assert.equal(b.t_resp, pt.responseStart - pt.navigationStart); + } + else { + this.skip(); + } + }); + + it("Should have sent the first beacon with a t_page of total - t_resp (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + var pt = window.performance.timing; + var b = tf.beacons[i]; + + assert.equal(b.t_page, b.t_done - b.t_resp); + } + else { + this.skip(); + } + }); + + it("Should have sent the first beacon without rt.quit or rt.abld (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + var b = tf.beacons[0]; + + assert.isUndefined(b["rt.quit"]); + assert.isUndefined(b["rt.abld"]); + } + else { + this.skip(); + } + }); + + it("Should have sent the first beacon with spa.forced (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + var b = tf.beacons[0]; + + assert.equal(b["spa.forced"], "1"); + } + else { + this.skip(); + } + }); + + it("Should have sent the first beacon with spa.waiting (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + var b = tf.beacons[0]; + + assert.operator(parseInt(b["spa.forced"], 10), ">=", 1); + } + else { + this.skip(); + } + }); + + it("Should have sent the first beacon with resources only from its nav (if ResourceTiming is supported)", function() { + if (t.isResourceTimingSupported()) { + var b = tf.beacons[0]; + + var resources = ResourceTimingDecompression.decompressResources(JSON.parse(b.restiming)); + + // should have widgets.json, home.html, app.js + assert.equal(resources.filter(function(r) { + return r.name.indexOf("widgets.json") !== -1; + }).length, 1, "Should have widgets.json"); + + assert.equal(resources.filter(function(r) { + return r.name.indexOf("home.html") !== -1; + }).length, 1, "Should have home.html"); + + assert.equal(resources.filter(function(r) { + return r.name.indexOf("app.js") !== -1; + }).length, 1, "Should have app.js"); + + // shouldn't have widgets.html + assert.equal(resources.filter(function(r) { + return r.name.indexOf("widget.html") !== -1; + }).length, 0, "Should not have widgets.html"); + } + else { + this.skip(); + } + }); + }); + + // + // Beacon 2 + // + describe("Beacon 2 (spa)", function() { + var i = 1; + + it("Should have sent the second beacon for /widgets/1", function() { + var b = tf.beacons[i]; + + assert.isTrue(b.u.indexOf("/widgets/1") !== -1); + }); + + it("Should have sent the second beacon with a timestamp of when the completion happened (if MutationObserver is supported)", function() { + if (t.isMutationObserverSupported()) { + var b = tf.beacons[i]; + + assert.closeTo(b.t_done, window.nav2time - b["rt.tstart"], 100); + } + else { + this.skip(); + } + }); + + it("Should have sent the second beacon with a timestamp of around 1 second (if MutationObserver is not supported)", function() { + if (!t.isMutationObserverSupported()) { + // because of the widget IMG delaying 1 second but we couldn't track it because no MO support + var b = tf.beacons[i]; + + assert.closeTo(parseInt(b.t_done, 10), 1000, 50); + } + else { + this.skip(); + } + }); + + it("Should have sent the second beacon with a t_resp value (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + var pt = window.performance.timing; + var b = tf.beacons[i]; + + assert.operator(parseInt(b.t_resp, 10), ">=", 0); + } + else { + this.skip(); + } + }); + + it("Should have sent the second beacon with a t_page of total - t_resp (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + var pt = window.performance.timing; + var b = tf.beacons[i]; + + assert.equal(b.t_page, b.t_done - b.t_resp); + } + else { + this.skip(); + } + }); + + it("Should have sent the second beacon without rt.quit and rt.abld (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + var b = tf.beacons[i]; + + assert.isUndefined(b["rt.quit"]); + assert.isUndefined(b["rt.abld"]); + } + else { + this.skip(); + } + }); + + it("Should have sent the second beacon with spa.forced (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + var b = tf.beacons[i]; + + assert.equal(b["spa.forced"], "1"); + } + else { + this.skip(); + } + }); + + it("Should have sent the second beacon with spa.waiting (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + var b = tf.beacons[i]; + + assert.operator(parseInt(b["spa.forced"], 10), ">=", 1); + } + else { + this.skip(); + } + }); + + it("Should have sent the second beacon with resources only from its nav (if ResourceTiming is supported)", function() { + if (t.isResourceTimingSupported()) { + var b = tf.beacons[i]; + + var resources = ResourceTimingDecompression.decompressResources(JSON.parse(b.restiming)); + + // should have widgets.html, widgets.json + assert.equal(resources.filter(function(r) { + return r.name.indexOf("widgets.json") !== -1; + }).length, 1, "Should have widgets.json"); + + assert.equal(resources.filter(function(r) { + return r.name.indexOf("widget.html") !== -1; + }).length, 1, "Shoud have widgets.html"); + + // should not have home.html, app.js + assert.equal(resources.filter(function(r) { + return r.name.indexOf("home.html") !== -1; + }).length, 0, "Should not have home.html"); + + assert.equal(resources.filter(function(r) { + return r.name.indexOf("app.js") !== -1; + }).length, 0, "Should not have app.js"); + } + else { + this.skip(); + } + }); + }); + + // + // Beacon 3 + // + describe("Beacon 3 (spa)", function() { + var i = 2; + + it("Should have sent the third beacon for " + pathName, function() { + var b = tf.beacons[i]; + + assert.isTrue(b.u.indexOf(pathName) !== -1); + }); + + it("Should have sent the third with a timestamp of at least 3 seconds (if MutationObserver is supported)", function() { + if (t.isMutationObserverSupported()) { + var b = tf.beacons[i]; + + assert.operator(parseInt(b.t_done, 10), ">=", 3000); + } + else { + this.skip(); + } + }); + + it("Should have sent the third with a timestamp of under 1 second (if MutationObserver is not supported)", function() { + if (!t.isMutationObserverSupported()) { + var b = tf.beacons[i]; + + assert.operator(parseInt(b.t_done, 10), "<=", 1000); + } + else { + this.skip(); + } + }); + + it("Should have sent the third beacon with a t_resp value (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + var pt = window.performance.timing; + var b = tf.beacons[i]; + + assert.operator(parseInt(b.t_resp, 10), ">=", 0); + } + else { + this.skip(); + } + }); + + it("Should have sent the third beacon with a t_page of total - t_resp (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + var pt = window.performance.timing; + var b = tf.beacons[i]; + + assert.equal(b.t_page, b.t_done - b.t_resp); + } + else { + this.skip(); + } + }); + + it("Should have sent the third beacon without rt.quit or rt.abld", function() { + var b = tf.beacons[i]; + + assert.isUndefined(b["rt.quit"]); + assert.isUndefined(b["rt.abld"]); + }); + + it("Should have sent the third beacon without spa.forced", function() { + var b = tf.beacons[i]; + + assert.isUndefined(b["spa.forced"]); + }); + + it("Should have sent the third beacon without spa.waiting", function() { + var b = tf.beacons[i]; + + assert.isUndefined(b["spa.waiting"]); + }); + + it("Should have sent the second beacon with resources only from its nav (if ResourceTiming is supported)", function() { + if (t.isResourceTimingSupported()) { + var b = tf.beacons[i]; + + var resources = ResourceTimingDecompression.decompressResources(JSON.parse(b.restiming)); + + // should have widgets.json, img.jpg + assert.equal(resources.filter(function(r) { + return r.name.indexOf("widgets.json") !== -1; + }).length, 1, "Should have widgets.json"); + + assert.equal(resources.filter(function(r) { + return r.name.indexOf("img.jpg") !== -1; + }).length, 1, "Should have img.jpg"); + + // should not have home.html, app.js + assert.equal(resources.filter(function(r) { + return r.name.indexOf("home.html") !== -1; + }).length, 0, "Should not have home.html"); + + assert.equal(resources.filter(function(r) { + return r.name.indexOf("app.js") !== -1; + }).length, 0, "Should not have app.js"); + + assert.equal(resources.filter(function(r) { + return r.name.indexOf("widget.html") !== -1; + }).length, 0, "Shoud not have widgets.html"); + } + else { + this.skip(); + } + }); + }); +}); diff --git a/tests/page-templates/05-angular/118-spa-hard-xhr-spa-soft.html b/tests/page-templates/05-angular/118-spa-hard-xhr-spa-soft.html new file mode 100644 index 000000000..dd89c0b20 --- /dev/null +++ b/tests/page-templates/05-angular/118-spa-hard-xhr-spa-soft.html @@ -0,0 +1,71 @@ +<%= header %> +<%= boomerangScript %> + + + + + + +
+
+
+
+ + +<%= footer %> diff --git a/tests/page-templates/05-angular/118-spa-hard-xhr-spa-soft.js b/tests/page-templates/05-angular/118-spa-hard-xhr-spa-soft.js new file mode 100644 index 000000000..36bcf65c4 --- /dev/null +++ b/tests/page-templates/05-angular/118-spa-hard-xhr-spa-soft.js @@ -0,0 +1,63 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,describe,it */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["angular", "ng339", "angular_imgs", "angular_html5_mode", "modules", "app", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2", "i"]); + +describe("e2e/05-angular/118-spa-hard-xhr-spa-soft.js", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + clearTimeout(window.timerid); + }); + + it("Should have sent four beacons", function() { + assert.equal(tf.beacons.length, 4); + }); + + it("Should have sent the first beacon as http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + + it("Should have sent the first beacon with a URL of the page", function() { + assert.isTrue(tf.beacons[0].u.indexOf("118-spa-hard-xhr-spa-soft") !== -1); + }); + + it("Should have sent the second beacon as http.initiator = xhr", function() { + assert.equal(tf.beacons[1]["http.initiator"], "xhr"); + }); + + it("Should have sent the second beacon with a URL of the XHR", function() { + assert.isTrue(tf.beacons[1].u.indexOf("pages/05-angular/support/img.jpg?rnd=") !== -1); + }); + + it("Should have sent the second beacon with a Page URL of the app", function() { + assert.isTrue(tf.beacons[1].pgu.indexOf("118-spa-hard-xhr-spa-soft") !== -1); + }); + + it("Should have sent the third beacon as http.initiator = spa", function() { + assert.equal(tf.beacons[2]["http.initiator"], "spa"); + }); + + it("Should have sent the third beacon with a URL of the widget", function() { + assert.isTrue(tf.beacons[2].u.indexOf("/widgets/10") !== -1); + }); + + it("Should have sent the third beacon with no pgu", function() { + assert.isUndefined(tf.beacons[2].pgu); + }); + + it("Should have sent the fourth beacon as http.initiator = spa", function() { + assert.equal(tf.beacons[3]["http.initiator"], "spa"); + }); + + it("Should have sent the fourth beacon with a URL of the page", function() { + assert.isTrue(tf.beacons[3].u.indexOf("118-spa-hard-xhr-spa-soft") !== -1); + }); + + it("Should have sent the fourth beacon with no pgu", function() { + assert.isUndefined(tf.beacons[3].pgu); + }); +}); diff --git a/tests/page-templates/05-angular/119-alwayssendxhr-function.html b/tests/page-templates/05-angular/119-alwayssendxhr-function.html new file mode 100644 index 000000000..211b149d9 --- /dev/null +++ b/tests/page-templates/05-angular/119-alwayssendxhr-function.html @@ -0,0 +1,36 @@ +<%= header %> +<%= boomerangScript %> + + + + + +
+ + +<%= footer %> diff --git a/tests/page-templates/05-angular/119-alwayssendxhr-function.js b/tests/page-templates/05-angular/119-alwayssendxhr-function.js new file mode 100644 index 000000000..819098112 --- /dev/null +++ b/tests/page-templates/05-angular/119-alwayssendxhr-function.js @@ -0,0 +1,73 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,describe,it */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["angular", "ng339", "modules", "app", "angular_imgs", "angular_nav_routes", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2", "i"]); + +describe("e2e/05-angular/119-alwayssendxhr-function.js", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + clearTimeout(window.timerid); + }); + + it("Should have sent four beacons", function() { + assert.equal(tf.beacons.length, 4); + }); + + it("Should have sent the first beacon as http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + + it("Should have sent the first beacon with a URL of the page", function() { + assert.isTrue(tf.beacons[0].u.indexOf("119-alwayssendxhr-function") !== -1); + }); + + it("Should have sent the second beacon as http.initiator = xhr", function() { + assert.equal(tf.beacons[1]["http.initiator"], "xhr"); + }); + + it("Should have sent the second beacon with a URL of the widget.html XHR that is alwaysSendXhr", function() { + assert.include(tf.beacons[1].u, "widget.html"); + }); + + it("Should have sent the second beacon with a Page URL of the app", function() { + assert.isTrue(tf.beacons[1].pgu.indexOf("119-alwayssendxhr-function") !== -1); + }); + + it("Should have sent the second beacon with a duration of between 2 and 3 seconds", function() { + assert.operator(tf.beacons[1].t_done, ">=", 2000); + assert.operator(tf.beacons[1].t_done, "<=", 3000); + }); + + it("Should have sent the third beacon as http.initiator = spa", function() { + assert.equal(tf.beacons[2]["http.initiator"], "spa"); + }); + + it("Should have sent the third beacon with a URL of the widget", function() { + assert.isTrue(tf.beacons[2].u.indexOf("/widgets/delay/1") !== -1); + }); + + it("Should have sent the third beacon with no pgu", function() { + assert.isUndefined(tf.beacons[2].pgu); + }); + + it("Should have sent the third beacon with a duration of between 3 and 4 seconds", function() { + assert.operator(tf.beacons[2].t_done, ">=", 3000); + assert.operator(tf.beacons[2].t_done, "<=", 4000); + }); + + it("Should have sent the fourth beacon as http.initiator = spa", function() { + assert.equal(tf.beacons[3]["http.initiator"], "spa"); + }); + + it("Should have sent the fourth beacon with a URL of the page", function() { + assert.isTrue(tf.beacons[3].u.indexOf("119-alwayssendxhr-function") !== -1); + }); + + it("Should have sent the fourth beacon with no pgu", function() { + assert.isUndefined(tf.beacons[3].pgu); + }); +}); diff --git a/tests/page-templates/05-angular/12-autoxhr-trigger-additional-after-delay.html b/tests/page-templates/05-angular/12-autoxhr-trigger-additional-after-delay.html new file mode 100644 index 000000000..be26733a1 --- /dev/null +++ b/tests/page-templates/05-angular/12-autoxhr-trigger-additional-after-delay.html @@ -0,0 +1,52 @@ +<%= header %> +<%= boomerangScript %> + + + + +
+
+
+
+ + + +<%= footer %> diff --git a/tests/page-templates/05-angular/12-autoxhr-trigger-additional-after-delay.js b/tests/page-templates/05-angular/12-autoxhr-trigger-additional-after-delay.js new file mode 100644 index 000000000..9bce2e052 --- /dev/null +++ b/tests/page-templates/05-angular/12-autoxhr-trigger-additional-after-delay.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["angular", "ng339", "modules", "app", "angular_imgs", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2"]); + +describe("e2e/05-angular/12-autoxhr-trigger-additional-after-delay", function() { + BOOMR_test.templates.SPA["12-autoxhr-trigger-additional-after-delay"](); +}); diff --git a/tests/page-templates/05-angular/120-alwayssendxhr-list.html b/tests/page-templates/05-angular/120-alwayssendxhr-list.html new file mode 100644 index 000000000..9a90f7ae5 --- /dev/null +++ b/tests/page-templates/05-angular/120-alwayssendxhr-list.html @@ -0,0 +1,36 @@ +<%= header %> +<%= boomerangScript %> + + + + + +
+ + +<%= footer %> diff --git a/tests/page-templates/05-angular/120-alwayssendxhr-list.js b/tests/page-templates/05-angular/120-alwayssendxhr-list.js new file mode 100644 index 000000000..680bff990 --- /dev/null +++ b/tests/page-templates/05-angular/120-alwayssendxhr-list.js @@ -0,0 +1,73 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,describe,it */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["angular", "ng339", "modules", "app", "angular_imgs", "angular_nav_routes", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2", "i"]); + +describe("e2e/05-angular/120-alwayssendxhr-list.js", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + clearTimeout(window.timerid); + }); + + it("Should have sent four beacons", function() { + assert.equal(tf.beacons.length, 4); + }); + + it("Should have sent the first beacon as http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + + it("Should have sent the first beacon with a URL of the page", function() { + assert.isTrue(tf.beacons[0].u.indexOf("120-alwayssendxhr-list") !== -1); + }); + + it("Should have sent the second beacon as http.initiator = xhr", function() { + assert.equal(tf.beacons[1]["http.initiator"], "xhr"); + }); + + it("Should have sent the second beacon with a URL of the widget.html XHR that is alwaysSendXhr", function() { + assert.include(tf.beacons[1].u, "widget.html"); + }); + + it("Should have sent the second beacon with a Page URL of the app", function() { + assert.isTrue(tf.beacons[1].pgu.indexOf("120-alwayssendxhr-list") !== -1); + }); + + it("Should have sent the second beacon with a duration of between 2 and 3 seconds", function() { + assert.operator(tf.beacons[1].t_done, ">=", 2000); + assert.operator(tf.beacons[1].t_done, "<=", 3000); + }); + + it("Should have sent the third beacon as http.initiator = spa", function() { + assert.equal(tf.beacons[2]["http.initiator"], "spa"); + }); + + it("Should have sent the third beacon with a URL of the widget", function() { + assert.isTrue(tf.beacons[2].u.indexOf("/widgets/delay/1") !== -1); + }); + + it("Should have sent the third beacon with no pgu", function() { + assert.isUndefined(tf.beacons[2].pgu); + }); + + it("Should have sent the third beacon with a duration of between 3 and 4 seconds", function() { + assert.operator(tf.beacons[2].t_done, ">=", 3000); + assert.operator(tf.beacons[2].t_done, "<=", 4000); + }); + + it("Should have sent the fourth beacon as http.initiator = spa", function() { + assert.equal(tf.beacons[3]["http.initiator"], "spa"); + }); + + it("Should have sent the fourth beacon with a URL of the page", function() { + assert.isTrue(tf.beacons[3].u.indexOf("120-alwayssendxhr-list") !== -1); + }); + + it("Should have sent the fourth beacon with no pgu", function() { + assert.isUndefined(tf.beacons[3].pgu); + }); +}); diff --git a/tests/page-templates/05-angular/121-painttiming.html b/tests/page-templates/05-angular/121-painttiming.html new file mode 100644 index 000000000..bb12d6f69 --- /dev/null +++ b/tests/page-templates/05-angular/121-painttiming.html @@ -0,0 +1,34 @@ +<%= header %> +<%= boomerangScript %> + + + + + +
+ + +<%= footer %> diff --git a/tests/page-templates/05-angular/121-painttiming.js b/tests/page-templates/05-angular/121-painttiming.js new file mode 100644 index 000000000..a477ec0bb --- /dev/null +++ b/tests/page-templates/05-angular/121-painttiming.js @@ -0,0 +1,101 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,describe,it */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["angular", "ng339", "modules", "app", "angular_imgs", "angular_nav_routes", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2", "i"]); + +describe("e2e/05-angular/121-painttiming.js", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + clearTimeout(window.timerid); + }); + + it("Should have sent three beacons", function() { + assert.equal(tf.beacons.length, 3); + }); + + describe("Beacon 1", function() { + it("Should have http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + + it("Should have set pt.fp (if PaintTiming is supported and happened by load)", function() { + if (!t.isPaintTimingSupported()) { + return this.skip(); + } + + var pt = BOOMR.utils.arrayFind(performance.getEntriesByType("paint"), function(entry) { + return entry.name === "first-paint"; + }); + + if (!pt || pt.startTime > parseInt(tf.beacons[0].t_done, 10)) { + // might happen if there haven't been any paints by beacon, like if it + // loaded in the background + return this.skip(); + } + + // validation of First Paint + assert.isNumber(tf.beacons[0]["pt.fp"]); + assert.operator(parseInt(tf.beacons[0]["pt.fp"], 10), ">=", 0); + assert.equal(tf.beacons[0]["pt.fp"], Math.floor(pt.startTime)); + }); + + it("Should have set pt.fcp (if PaintTiming is supported and happened by load)", function() { + if (!t.isPaintTimingSupported()) { + return this.skip(); + } + + var pt = BOOMR.utils.arrayFind(performance.getEntriesByType("paint"), function(entry) { + return entry.name === "first-contentful-paint"; + }); + + if (!pt || pt.startTime > parseInt(tf.beacons[0].t_done, 10)) { + // might happen if there haven't been any paints by beacon, like if it + // loaded in the background + return this.skip(); + } + + // validation of First Contentful Paint + assert.isNumber(tf.beacons[0]["pt.fcp"]); + assert.operator(parseInt(tf.beacons[0]["pt.fcp"], 10), ">=", 0); + + if (tf.beacons[0]["pt.fp"]) { + // FF has fcp but no fp + assert.operator(parseInt(tf.beacons[0]["pt.fcp"], 10), ">=", parseInt(tf.beacons[0]["pt.fp"], 10)); + } + + assert.equal(tf.beacons[0]["pt.fcp"], Math.floor(pt.startTime)); + }); + }); + + describe("Beacon 2", function() { + it("Should have http.initiator = spa", function() { + assert.equal(tf.beacons[1]["http.initiator"], "spa"); + }); + + it("Should not have set pt.fp", function() { + assert.isUndefined(tf.beacons[1]["pt.fp"]); + }); + + it("Should not have set pt.fcp", function() { + assert.isUndefined(tf.beacons[1]["pt.fcp"]); + }); + }); + + describe("Beacon 3", function() { + it("Should have http.initiator = spa", function() { + assert.equal(tf.beacons[2]["http.initiator"], "spa"); + }); + + it("Should not have set pt.fp", function() { + assert.isUndefined(tf.beacons[2]["pt.fp"]); + }); + + it("Should not have set pt.fcp", function() { + assert.isUndefined(tf.beacons[2]["pt.fcp"]); + }); + }); +}); diff --git a/tests/page-templates/05-angular/122-alwayssendxhr-markcomplete.html b/tests/page-templates/05-angular/122-alwayssendxhr-markcomplete.html new file mode 100644 index 000000000..31d4c0b1c --- /dev/null +++ b/tests/page-templates/05-angular/122-alwayssendxhr-markcomplete.html @@ -0,0 +1,97 @@ +<%= header %> +<%= boomerangScript %> + + + + + + + +
+
+
+
+ + +<%= footer %> diff --git a/tests/page-templates/05-angular/122-alwayssendxhr-markcomplete.js b/tests/page-templates/05-angular/122-alwayssendxhr-markcomplete.js new file mode 100644 index 000000000..7855a2f1d --- /dev/null +++ b/tests/page-templates/05-angular/122-alwayssendxhr-markcomplete.js @@ -0,0 +1,476 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["angular", "ng339", "ResourceTimingDecompression", "angular_imgs", "angular_html5_mode", "modules", "app", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2", "nav1time", "nav2time", "i"]); + +describe("e2e/05-angular/122-alwayssendxhr-markcomplete", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + var pathName = window.location.pathname; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent nine beacons", function() { + assert.equal(tf.beacons.length, 9); + }); + + it("Should have set the same Page ID (pid) on all beacons", function() { + var pid = tf.beacons[0].pid; + + for (var i = 0; i < tf.beacons.length; i++) { + var b = tf.beacons[i]; + + assert.equal(b.pid, pid); + } + }); + + it("Should have sent all beacons > 1 with rt.nstart = navigationTiming (if NavigationTiming is supported)", function() { + if (typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + for (var i = 1; i < tf.beacons.length; i++) { + assert.equal(tf.beacons[i]["rt.nstart"], BOOMR.plugins.RT.navigationStart()); + } + } + else { + this.skip(); + } + }); + + it("Should not have Boomerang timings on SPA Soft and XHR beacons", function() { + for (var i = 1; i < tf.beacons.length; i++) { + if (tf.beacons[i].t_other) { + assert.equal(tf.beacons[i].t_other.indexOf("boomr_fb"), -1, "should not have boomr_fb"); + assert.equal(tf.beacons[i].t_other.indexOf("boomr_ld"), -1, "should not have boomr_ld"); + assert.equal(tf.beacons[i].t_other.indexOf("boomr_lat"), -1, "should not have boomr_lat"); + assert.equal(tf.beacons[i].t_other.indexOf("boomerang"), -1, "should not have boomerang"); + } + + // Boomerang and config timing parameters + assert.isUndefined(tf.beacons[i]["rt.bmr"]); + assert.isUndefined(tf.beacons[i]["rt.cnf"]); + } + }); + + // + // Beacon 1 + // + describe("Beacon 1 (spa_hard)", function() { + var i = 0; + + it("Should have sent the first beacon as http.initiator = spa_hard", function() { + assert.equal(tf.beacons[i]["http.initiator"], "spa_hard"); + }); + + it("Should have sent the first beacon for " + pathName, function() { + var b = tf.beacons[i]; + + assert.isTrue(b.u.indexOf(pathName) !== -1); + }); + + it("Should have sent the first beacon with a load time of when the completion happened (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + assert.closeTo(tf.beacons[i].t_done, window.nav1time - BOOMR.plugins.RT.navigationStart(), 100); + } + else { + this.skip(); + } + }); + + it("Should have sent the first beacon without a load time (if MutationObserver is supported but NavigationTiming is not)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() === "undefined") { + var b = tf.beacons[i]; + + assert.equal(b.t_done, undefined); + assert.equal(b["rt.start"], "none"); + } + else { + this.skip(); + } + }); + + it("Should have sent the first beacon with a t_resp of the root page (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + var pt = window.performance.timing; + var b = tf.beacons[i]; + + assert.equal(b.t_resp, pt.responseStart - pt.navigationStart); + } + else { + this.skip(); + } + }); + + it("Should have sent the first beacon with a t_page of total - t_resp (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + var pt = window.performance.timing; + var b = tf.beacons[i]; + + assert.equal(b.t_page, b.t_done - b.t_resp); + } + else { + this.skip(); + } + }); + + it("Should have sent the first beacon without rt.quit or rt.abld (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + var b = tf.beacons[0]; + + assert.isUndefined(b["rt.quit"]); + assert.isUndefined(b["rt.abld"]); + } + else { + this.skip(); + } + }); + + it("Should have sent the first beacon with spa.forced (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + var b = tf.beacons[0]; + + assert.equal(b["spa.forced"], "1"); + } + else { + this.skip(); + } + }); + + it("Should have sent the first beacon with spa.waiting (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + var b = tf.beacons[0]; + + assert.operator(parseInt(b["spa.forced"], 10), ">=", 1); + } + else { + this.skip(); + } + }); + + it("Should have sent the first beacon with resources only from its nav (if ResourceTiming is supported)", function() { + if (t.isResourceTimingSupported()) { + var b = tf.beacons[0]; + + var resources = ResourceTimingDecompression.decompressResources(JSON.parse(b.restiming)); + + // should have widgets.json, home.html, app.js + assert.equal(resources.filter(function(r) { + return r.name.indexOf("widgets.json") !== -1; + }).length, 1, "Should have widgets.json"); + + assert.equal(resources.filter(function(r) { + return r.name.indexOf("home.html") !== -1; + }).length, 1, "Should have home.html"); + + assert.equal(resources.filter(function(r) { + return r.name.indexOf("app.js") !== -1; + }).length, 1, "Should have app.js"); + + // shouldn't have widgets.html + assert.equal(resources.filter(function(r) { + return r.name.indexOf("widget.html") !== -1; + }).length, 0, "Should not have widgets.html"); + } + else { + this.skip(); + } + }); + }); + + // + // Beacon 2 + // + describe("Beacon 2 (xhr)", function() { + var i = 1; + + it("Should have sent the beacon as http.initiator = xhr", function() { + assert.equal(tf.beacons[i]["http.initiator"], "xhr"); + }); + }); + + // + // Beacon 3 + // + describe("Beacon 3 (xhr)", function() { + var i = 2; + + it("Should have sent the beacon as http.initiator = xhr", function() { + assert.equal(tf.beacons[i]["http.initiator"], "xhr"); + }); + }); + + // + // Beacon 4 + // + describe("Beacon 4 (xhr)", function() { + var i = 3; + + it("Should have sent the beacon as http.initiator = xhr", function() { + assert.equal(tf.beacons[i]["http.initiator"], "xhr"); + }); + }); + + // + // Beacon 5 + // + describe("Beacon 5 (xhr)", function() { + var i = 4; + + it("Should have sent the beacon as http.initiator = xhr", function() { + assert.equal(tf.beacons[i]["http.initiator"], "xhr"); + }); + }); + + // + // Beacon 6 + // + describe("Beacon 6 (spa)", function() { + var i = 5; + + it("Should have sent the beacon for /widgets/1", function() { + var b = tf.beacons[i]; + + assert.isTrue(b.u.indexOf("/widgets/1") !== -1); + }); + + it("Should have sent the beacon with a timestamp of when the completion happened (if MutationObserver is supported)", function() { + if (t.isMutationObserverSupported()) { + var b = tf.beacons[i]; + + assert.closeTo(b.t_done, window.nav2time - b["rt.tstart"], 100); + } + else { + this.skip(); + } + }); + + it("Should have sent the beacon with a timestamp of around 1 second (if MutationObserver is not supported)", function() { + if (!t.isMutationObserverSupported()) { + // because of the widget IMG delaying 1 second but we couldn't track it because no MO support + var b = tf.beacons[i]; + + assert.closeTo(parseInt(b.t_done, 10), 1000, 50); + } + else { + this.skip(); + } + }); + + it("Should have sent the beacon with a t_resp value (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + var pt = window.performance.timing; + var b = tf.beacons[i]; + + assert.operator(parseInt(b.t_resp, 10), ">=", 0); + } + else { + this.skip(); + } + }); + + it("Should have sent the beacon with a t_page of total - t_resp (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + var pt = window.performance.timing; + var b = tf.beacons[i]; + + assert.equal(b.t_page, b.t_done - b.t_resp); + } + else { + this.skip(); + } + }); + + it("Should have sent the beacon without rt.quit and rt.abld (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + var b = tf.beacons[i]; + + assert.isUndefined(b["rt.quit"]); + assert.isUndefined(b["rt.abld"]); + } + else { + this.skip(); + } + }); + + it("Should have sent the beacon with spa.forced (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + var b = tf.beacons[i]; + + assert.equal(b["spa.forced"], "1"); + } + else { + this.skip(); + } + }); + + it("Should have sent the beacon with spa.waiting (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + var b = tf.beacons[i]; + + assert.operator(parseInt(b["spa.forced"], 10), ">=", 1); + } + else { + this.skip(); + } + }); + + it("Should have sent the beacon with resources only from its nav (if ResourceTiming is supported)", function() { + if (t.isResourceTimingSupported()) { + var b = tf.beacons[i]; + + var resources = ResourceTimingDecompression.decompressResources(JSON.parse(b.restiming)); + + // should have widgets.html, widgets.json + assert.equal(resources.filter(function(r) { + return r.name.indexOf("widgets.json") !== -1; + }).length, 1, "Should have widgets.json"); + + assert.equal(resources.filter(function(r) { + return r.name.indexOf("widget.html") !== -1; + }).length, 1, "Shoud have widgets.html"); + + // should not have home.html, app.js + assert.equal(resources.filter(function(r) { + return r.name.indexOf("home.html") !== -1; + }).length, 0, "Should not have home.html"); + + assert.equal(resources.filter(function(r) { + return r.name.indexOf("app.js") !== -1; + }).length, 0, "Should not have app.js"); + } + else { + this.skip(); + } + }); + }); + + // + // Beacon 7 + // + describe("Beacon 7 (xhr)", function() { + var i = 6; + + it("Should have sent the beacon as http.initiator = xhr", function() { + assert.equal(tf.beacons[i]["http.initiator"], "xhr"); + }); + + it("Should have sent the beacon for support/widgets.json", function() { + var b = tf.beacons[i]; + + assert.isTrue(b.u.indexOf("support/widgets.json") !== -1); + }); + }); + + // + // Beacon 8 + // + describe("Beacon 8 (spa)", function() { + var i = 7; + + it("Should have sent the beacon for " + pathName, function() { + var b = tf.beacons[i]; + + assert.isTrue(b.u.indexOf(pathName) !== -1); + }); + + it("Should have sent the with a timestamp of at least 3 seconds (if MutationObserver is supported)", function() { + if (t.isMutationObserverSupported()) { + var b = tf.beacons[i]; + + assert.operator(parseInt(b.t_done, 10), ">=", 3000); + } + else { + this.skip(); + } + }); + + it("Should have sent the with a timestamp of under 1 second (if MutationObserver is not supported)", function() { + if (!t.isMutationObserverSupported()) { + var b = tf.beacons[i]; + + assert.operator(parseInt(b.t_done, 10), "<=", 1000); + } + else { + this.skip(); + } + }); + + it("Should have sent the beacon with a t_resp value (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + var pt = window.performance.timing; + var b = tf.beacons[i]; + + assert.operator(parseInt(b.t_resp, 10), ">=", 0); + } + else { + this.skip(); + } + }); + + it("Should have sent the beacon with a t_page of total - t_resp (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + var pt = window.performance.timing; + var b = tf.beacons[i]; + + assert.equal(b.t_page, b.t_done - b.t_resp); + } + else { + this.skip(); + } + }); + + it("Should have sent the beacon without rt.quit or rt.abld", function() { + var b = tf.beacons[i]; + + assert.isUndefined(b["rt.quit"]); + assert.isUndefined(b["rt.abld"]); + }); + + it("Should have sent the beacon without spa.forced", function() { + var b = tf.beacons[i]; + + assert.isUndefined(b["spa.forced"]); + }); + + it("Should have sent the beacon without spa.waiting", function() { + var b = tf.beacons[i]; + + assert.isUndefined(b["spa.waiting"]); + }); + + it("Should have sent the beacon with resources only from its nav (if ResourceTiming is supported)", function() { + if (t.isResourceTimingSupported()) { + var b = tf.beacons[i]; + + var resources = ResourceTimingDecompression.decompressResources(JSON.parse(b.restiming)); + + // should have widgets.json, img.jpg + assert.equal(resources.filter(function(r) { + return r.name.indexOf("widgets.json") !== -1; + }).length, 1, "Should have widgets.json"); + + assert.equal(resources.filter(function(r) { + return r.name.indexOf("img.jpg") !== -1; + }).length, 1, "Should have img.jpg"); + + // should not have home.html, app.js + assert.equal(resources.filter(function(r) { + return r.name.indexOf("home.html") !== -1; + }).length, 0, "Should not have home.html"); + + assert.equal(resources.filter(function(r) { + return r.name.indexOf("app.js") !== -1; + }).length, 0, "Should not have app.js"); + + assert.equal(resources.filter(function(r) { + return r.name.indexOf("widget.html") !== -1; + }).length, 0, "Shoud not have widgets.html"); + } + else { + this.skip(); + } + }); + }); +}); diff --git a/tests/page-templates/05-angular/123-alwayssendxhr-merging.html b/tests/page-templates/05-angular/123-alwayssendxhr-merging.html new file mode 100644 index 000000000..05f086a04 --- /dev/null +++ b/tests/page-templates/05-angular/123-alwayssendxhr-merging.html @@ -0,0 +1,49 @@ +<%= header %> + +<%= boomerangScript %> + + + + + +
+
+
+
+ + +<%= footer %> diff --git a/tests/page-templates/05-angular/123-alwayssendxhr-merging.js b/tests/page-templates/05-angular/123-alwayssendxhr-merging.js new file mode 100644 index 000000000..f275f8094 --- /dev/null +++ b/tests/page-templates/05-angular/123-alwayssendxhr-merging.js @@ -0,0 +1,107 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["angular", "ng339", "modules", "app", "angular_html5_mode", "angular_nav_routes", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2", "i"]); + +describe("e2e/05-angular/123-alwayssendxhr-merging", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + var pathName = window.location.pathname; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent five beacons", function() { + assert.equal(tf.beacons.length, 5); + }); + + // + // Beacon 1 + // + describe("Beacon 1 (spa_hard)", function() { + var i = 0; + + it("Should have sent the first beacon as http.initiator = spa_hard", function() { + assert.equal(tf.beacons[i]["http.initiator"], "spa_hard"); + }); + + it("Should have a URL (u) of " + pathName, function() { + var b = tf.beacons[i]; + + assert.isTrue(b.u.indexOf(pathName) !== -1); + }); + }); + + if (BOOMR.plugins.AutoXHR) { + // + // Beacon 2 + // + describe("Beacon 2 (spa)", function() { + var i = 1; + + it("Should have sent the beacon as http.initiator = spa", function() { + assert.equal(tf.beacons[i]["http.initiator"], "spa"); + }); + + it("Should have a URL (u) of /widgets/1", function() { + var b = tf.beacons[i]; + + assert.include(b.u, "/widgets/1"); + }); + }); + + // + // Beacon 3 + // + describe("Beacon 3 (xhr)", function() { + var i = 2; + + it("Should have sent the beacon as http.initiator = xhr", function() { + assert.equal(tf.beacons[i]["http.initiator"], "xhr"); + }); + + it("Should have a URL (u) of bad.com", function() { + var b = tf.beacons[i]; + + assert.include(b.u, "bad.com"); + }); + }); + + // + // Beacon 4 + // + describe("Beacon 4 (spa)", function() { + var i = 3; + + it("Should have sent the beacon as http.initiator = spa", function() { + assert.equal(tf.beacons[i]["http.initiator"], "spa"); + }); + + it("Should have a URL (u) of " + pathname, function() { + var b = tf.beacons[i]; + + assert.include(b.u, pathname); + }); + }); + + // + // Beacon 5 + // + describe("Beacon 5 (xhr)", function() { + var i = 4; + + it("Should have sent the beacon as http.initiator = xhr", function() { + assert.equal(tf.beacons[i]["http.initiator"], "xhr"); + }); + + it("Should have a URL (u) of bad.com", function() { + var b = tf.beacons[i]; + + assert.include(b.u, "bad.com"); + }); + }); + } +}); diff --git a/tests/page-templates/05-angular/124-continuity.html b/tests/page-templates/05-angular/124-continuity.html new file mode 100644 index 000000000..942f0ba9d --- /dev/null +++ b/tests/page-templates/05-angular/124-continuity.html @@ -0,0 +1,34 @@ +<%= header %> +<%= boomerangScript %> + + + + + +
+ + +<%= footer %> diff --git a/tests/page-templates/05-angular/124-continuity.js b/tests/page-templates/05-angular/124-continuity.js new file mode 100644 index 000000000..27c14799c --- /dev/null +++ b/tests/page-templates/05-angular/124-continuity.js @@ -0,0 +1,61 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,describe,it */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["angular", "ng339", "modules", "app", "angular_imgs", "angular_nav_routes", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2", "i"]); + +describe("e2e/05-angular/124-continuity.js", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + clearTimeout(window.timerid); + }); + + it("Should have sent three beacons", function() { + assert.equal(tf.beacons.length, 3); + }); + + describe("Beacon 1", function() { + it("Should have http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + + it("Should have set Time to Visually Ready (c.tti.vr)", function() { + assert.isDefined(tf.beacons[0]["c.tti.vr"]); + }); + + it("Should have set Time to Interactive (c.tti)", function() { + assert.isDefined(tf.beacons[0]["c.tti"]); + }); + }); + + describe("Beacon 2", function() { + it("Should have http.initiator = spa", function() { + assert.equal(tf.beacons[1]["http.initiator"], "spa"); + }); + + it("Should not have set Time to Visually Ready (c.tti.vr)", function() { + assert.isUndefined(tf.beacons[1]["c.tti.vr"]); + }); + + it("Should not have set Time to Interactive (c.tti)", function() { + assert.isUndefined(tf.beacons[1]["c.tti"]); + }); + }); + + describe("Beacon 3", function() { + it("Should have http.initiator = spa", function() { + assert.equal(tf.beacons[2]["http.initiator"], "spa"); + }); + + it("Should not have set Time to Visually Ready (c.tti.vr)", function() { + assert.isUndefined(tf.beacons[2]["c.tti.vr"]); + }); + + it("Should not have set Time to Interactive (c.tti)", function() { + assert.isUndefined(tf.beacons[2]["c.tti"]); + }); + }); +}); diff --git a/tests/page-templates/05-angular/125-continuity-no-tti-first-beacon.html b/tests/page-templates/05-angular/125-continuity-no-tti-first-beacon.html new file mode 100644 index 000000000..813792a11 --- /dev/null +++ b/tests/page-templates/05-angular/125-continuity-no-tti-first-beacon.html @@ -0,0 +1,53 @@ +<%= header %> +<%= boomerangScript %> + + + + + +
+ + +<%= footer %> diff --git a/tests/page-templates/05-angular/125-continuity-no-tti-first-beacon.js b/tests/page-templates/05-angular/125-continuity-no-tti-first-beacon.js new file mode 100644 index 000000000..2ba76b7cd --- /dev/null +++ b/tests/page-templates/05-angular/125-continuity-no-tti-first-beacon.js @@ -0,0 +1,66 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,describe,it */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["angular", "ng339", "modules", "app", "called", "angular_imgs", "angular_nav_routes", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2", "workDone", "i"]); + +describe("e2e/05-angular/125-continuity-no-tti-first-beacon.js", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + clearTimeout(window.timerid); + }); + + it("Should have sent three beacons", function() { + assert.equal(tf.beacons.length, 3); + }); + + describe("Beacon 1", function() { + it("Should have http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + + it("Should have set Time to Visually Ready (c.tti.vr)", function() { + assert.isDefined(tf.beacons[0]["c.tti.vr"]); + }); + + it("Should not have set Time to Interactive (c.tti)", function() { + if (t.isFirefox()) { + // TTI isn't as reliable on Firefox because it can't use LongTasks or Page Busy monitoring + return this.skip(); + } + + assert.isUndefined(tf.beacons[0]["c.tti"]); + }); + }); + + describe("Beacon 2", function() { + it("Should have http.initiator = spa", function() { + assert.equal(tf.beacons[1]["http.initiator"], "spa"); + }); + + it("Should not have set Time to Visually Ready (c.tti.vr)", function() { + assert.isUndefined(tf.beacons[1]["c.tti.vr"]); + }); + + it("Should not have set Time to Interactive (c.tti)", function() { + assert.isUndefined(tf.beacons[1]["c.tti"]); + }); + }); + + describe("Beacon 3", function() { + it("Should have http.initiator = spa", function() { + assert.equal(tf.beacons[2]["http.initiator"], "spa"); + }); + + it("Should not have set Time to Visually Ready (c.tti.vr)", function() { + assert.isUndefined(tf.beacons[2]["c.tti.vr"]); + }); + + it("Should not have set Time to Interactive (c.tti)", function() { + assert.isUndefined(tf.beacons[2]["c.tti"]); + }); + }); +}); diff --git a/tests/page-templates/05-angular/126-unload-params.html b/tests/page-templates/05-angular/126-unload-params.html new file mode 100644 index 000000000..372f8e45c --- /dev/null +++ b/tests/page-templates/05-angular/126-unload-params.html @@ -0,0 +1,40 @@ +<%= header %> +<%= boomerangScript %> + + + + +
+ + + + +<%= footer %> diff --git a/tests/page-templates/05-angular/126-unload-params.js b/tests/page-templates/05-angular/126-unload-params.js new file mode 100644 index 000000000..e2589c031 --- /dev/null +++ b/tests/page-templates/05-angular/126-unload-params.js @@ -0,0 +1,77 @@ +/* eslint-env mocha */ +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["angular", "ng339", "modules", "app", "angular_imgs", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2"]); + +describe("e2e/05-angular/126-unload-params", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + var UNLOAD_MEMORY_PARAMS = [ + "cpu.cnc", + "dom.ck", + "dom.doms", + "dom.iframe", + "dom.img", + "dom.link", + "dom.ln", + "dom.res", + "dom.script.ext", + "dom.script", + "dom.sz", + "mem.limit", + "mem.lsln", + "mem.lssz", + "mem.ssln", + "mem.sssz", + "mem.total", + "mem.used", + "scr.bpp", + "scr.dpx", + "scr.orn", + "scr.xy " + ]; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + clearTimeout(window.timerid); + }); + + it("Should have sent 2 beacons", function() { + assert.equal(tf.beacons.length, 2); + }); + + describe("Beacon 1", function() { + it("Should have http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + + it("Should have ResourceTiming data (restiming) (if supported)", function() { + if (!t.isResourceTimingSupported()) { + this.skip(); + } + + assert.isDefined(tf.beacons[0].restiming); + }); + }); + + describe("Beacon 2", function() { + it("Should be an Unload beacon (rt.quit)", function() { + assert.isDefined(tf.beacons[1]["rt.quit"]); + }); + + it("Should not have ResourceTiming data (restiming)", function() { + if (!t.isResourceTimingSupported()) { + this.skip(); + } + + assert.isUndefined(tf.beacons[1].restiming); + }); + + it("Should not have Memory data", function() { + for (var i = 0; i < UNLOAD_MEMORY_PARAMS.length; i++) { + assert.isUndefined(tf.beacons[1][UNLOAD_MEMORY_PARAMS[i]], + UNLOAD_MEMORY_PARAMS[i] + " should not be on the beacon"); + } + }); + }); +}); diff --git a/tests/page-templates/05-angular/127-abld-metriconunload.html b/tests/page-templates/05-angular/127-abld-metriconunload.html new file mode 100644 index 000000000..60beb2446 --- /dev/null +++ b/tests/page-templates/05-angular/127-abld-metriconunload.html @@ -0,0 +1,53 @@ +<%= header %> +<%= boomerangScript %> + + + + + + + +
+
+
+
+ + +<%= footer %> diff --git a/tests/page-templates/05-angular/127-abld-metriconunload.js b/tests/page-templates/05-angular/127-abld-metriconunload.js new file mode 100644 index 000000000..859051566 --- /dev/null +++ b/tests/page-templates/05-angular/127-abld-metriconunload.js @@ -0,0 +1,47 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["angular", "ng339", "ResourceTimingDecompression", "angular_imgs", "angular_html5_mode", "modules", "app", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2", "nav1time", "nav2time", "i"]); + +describe("e2e/05-angular/127-abld-metriconunload", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent three beacons", function() { + assert.equal(tf.beacons.length, 3); + }); + + it("Should have sent the first beacon with rt.quit and rt.abld (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + var b = tf.beacons[0]; + + assert.equal(b["rt.quit"], ""); + assert.equal(b["rt.abld"], ""); + } + }); + + it("Should have DOM count data on unload, as the onNonPageLoad is set", function() { + var b = tf.beacons[0]; + + assert.isDefined(b["dom.ln"]); + assert.isDefined(b["dom.img"]); + assert.isDefined(b["dom.script"]); + assert.isDefined(b["dom.iframe"]); + assert.isDefined(b["dom.link"]); + }); + + it("Should have ResourceTiming data (restiming) (if supported)", function() { + if (!t.isResourceTimingSupported()) { + this.skip(); + } + + var b = tf.beacons[0]; + + assert.isDefined(b.restiming); + }); +}); diff --git a/tests/page-templates/05-angular/128-abld-no-metriconunload.html b/tests/page-templates/05-angular/128-abld-no-metriconunload.html new file mode 100644 index 000000000..3ad4dfc4e --- /dev/null +++ b/tests/page-templates/05-angular/128-abld-no-metriconunload.html @@ -0,0 +1,55 @@ +<%= header %> +<%= boomerangScript %> + + + + + + + +
+
+
+
+ + +<%= footer %> diff --git a/tests/page-templates/05-angular/128-abld-no-metriconunload.js b/tests/page-templates/05-angular/128-abld-no-metriconunload.js new file mode 100644 index 000000000..32035de24 --- /dev/null +++ b/tests/page-templates/05-angular/128-abld-no-metriconunload.js @@ -0,0 +1,47 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["angular", "ng339", "ResourceTimingDecompression", "angular_imgs", "angular_html5_mode", "modules", "app", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2", "nav1time", "nav2time", "i"]); + +describe("e2e/05-angular/128-abld-no-metriconunload", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent three beacons", function() { + assert.equal(tf.beacons.length, 3); + }); + + it("Should have sent the first beacon with rt.quit and rt.abld (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + var b = tf.beacons[0]; + + assert.equal(b["rt.quit"], ""); + assert.equal(b["rt.abld"], ""); + } + }); + + it("Should have DOM count data on unload since abort occurred before load finished", function() { + var b = tf.beacons[0]; + + assert.isDefined(b["dom.ln"]); + assert.isDefined(b["dom.img"]); + assert.isDefined(b["dom.script"]); + assert.isDefined(b["dom.iframe"]); + assert.isDefined(b["dom.link"]); + }); + + it("Should have ResourceTiming data (restiming) (if supported)", function() { + if (!t.isResourceTimingSupported()) { + this.skip(); + } + + var b = tf.beacons[0]; + + assert.isDefined(b.restiming); + }); +}); diff --git a/tests/page-templates/05-angular/13-autoxhr-disabled.html b/tests/page-templates/05-angular/13-autoxhr-disabled.html new file mode 100644 index 000000000..853eb7256 --- /dev/null +++ b/tests/page-templates/05-angular/13-autoxhr-disabled.html @@ -0,0 +1,50 @@ +<%= header %> +<%= boomerangScript %> + + + + +
+
+
+
+ + + +<%= footer %> diff --git a/tests/page-templates/05-angular/13-autoxhr-disabled.js b/tests/page-templates/05-angular/13-autoxhr-disabled.js new file mode 100644 index 000000000..eb6d82128 --- /dev/null +++ b/tests/page-templates/05-angular/13-autoxhr-disabled.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["angular", "ng339", "modules", "app", "angular_imgs", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2"]); + +describe("e2e/05-angular/13-autoxhr-disabled", function() { + BOOMR_test.templates.SPA["13-autoxhr-disabled"](); +}); diff --git a/tests/page-templates/05-angular/14-autoxhr-before-page-load.html b/tests/page-templates/05-angular/14-autoxhr-before-page-load.html new file mode 100644 index 000000000..f9c723654 --- /dev/null +++ b/tests/page-templates/05-angular/14-autoxhr-before-page-load.html @@ -0,0 +1,36 @@ +<%= header %> +<%= boomerangScript %> + + + + + +
+ + + +<%= footer %> diff --git a/tests/page-templates/05-angular/14-autoxhr-before-page-load.js b/tests/page-templates/05-angular/14-autoxhr-before-page-load.js new file mode 100644 index 000000000..3bc87e73b --- /dev/null +++ b/tests/page-templates/05-angular/14-autoxhr-before-page-load.js @@ -0,0 +1,13 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +/* +* This app uses a delayed angular.bootstrap (and no ng-app) +* directive. +*/ +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["angular", "ng339", "modules", "app", "xhr", "angular_imgs", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2"]); + +describe("e2e/05-angular/14-autoxhr-before-page-load", function() { + BOOMR_test.templates.SPA["14-autoxhr-before-page-load"](); +}); diff --git a/tests/page-templates/05-angular/15-delayed-boomerang.html b/tests/page-templates/05-angular/15-delayed-boomerang.html new file mode 100644 index 000000000..ace842009 --- /dev/null +++ b/tests/page-templates/05-angular/15-delayed-boomerang.html @@ -0,0 +1,27 @@ +<%= header %> + + +<%= boomerangDelayedSnippet %> + + + + + + +
+ + + +<%= footer %> diff --git a/tests/page-templates/05-angular/15-delayed-boomerang.js b/tests/page-templates/05-angular/15-delayed-boomerang.js new file mode 100644 index 000000000..eefa321fd --- /dev/null +++ b/tests/page-templates/05-angular/15-delayed-boomerang.js @@ -0,0 +1,13 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +/* + * This app uses a delayed angular.bootstrap (and no ng-app) + * directive. + */ +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["BOOMR_script_delay", "angular", "ng339", "modules", "app", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2"]); + +describe("e2e/05-angular/15-delayed-boomerang", function() { + BOOMR_test.templates.SPA["15-delayed-boomerang"](); +}); diff --git a/tests/page-templates/05-angular/16-autoxhr-after-load-with-mutation.html b/tests/page-templates/05-angular/16-autoxhr-after-load-with-mutation.html new file mode 100644 index 000000000..a780920bf --- /dev/null +++ b/tests/page-templates/05-angular/16-autoxhr-after-load-with-mutation.html @@ -0,0 +1,68 @@ +<%= header %> +<%= boomerangScript %> + + + + +
+
+
+
+
+ + + +<%= footer %> diff --git a/tests/page-templates/05-angular/16-autoxhr-after-load-with-mutation.js b/tests/page-templates/05-angular/16-autoxhr-after-load-with-mutation.js new file mode 100644 index 000000000..4517f6247 --- /dev/null +++ b/tests/page-templates/05-angular/16-autoxhr-after-load-with-mutation.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["angular", "ng339", "modules", "app", "xhr", "beaconNum", "angular_imgs", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2"]); + +describe("e2e/05-angular/16-autoxhr-after-load-with-mutation", function() { + BOOMR_test.templates.SPA["16-autoxhr-after-load-with-mutation"](); +}); diff --git a/tests/page-templates/05-angular/17-wait.html b/tests/page-templates/05-angular/17-wait.html new file mode 100644 index 000000000..5ed428ab5 --- /dev/null +++ b/tests/page-templates/05-angular/17-wait.html @@ -0,0 +1,72 @@ +<%= header %> +<%= boomerangScript %> + + + + + +
+
+
+
+
+ + + +<%= footer %> diff --git a/tests/page-templates/05-angular/17-wait.js b/tests/page-templates/05-angular/17-wait.js new file mode 100644 index 000000000..5e1626b80 --- /dev/null +++ b/tests/page-templates/05-angular/17-wait.js @@ -0,0 +1,26 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, [ + "angular", + "ng339", + "modules", + "app", + "routeWaits", + "routeNumber", + "angular_imgs", + "angular_html5_mode", + "angular_nav_routes", + "spaWaitCompleteTimes", + "angular_route_wait", + "custom_metric_1", + "custom_metric_2", + "custom_timer_1", + "custom_timer_2", + "i" +]); + +describe("e2e/05-angular/17-wait", function() { + BOOMR_test.templates.SPA["17-wait"](); +}); diff --git a/tests/page-templates/05-angular/18-autoxhr-before-page-load-alwayssendxhr.html b/tests/page-templates/05-angular/18-autoxhr-before-page-load-alwayssendxhr.html new file mode 100644 index 000000000..c041cac74 --- /dev/null +++ b/tests/page-templates/05-angular/18-autoxhr-before-page-load-alwayssendxhr.html @@ -0,0 +1,33 @@ +<%= header %> +<%= boomerangScript %> + + + + + +
+ + + +<%= footer %> diff --git a/tests/page-templates/05-angular/18-autoxhr-before-page-load-alwayssendxhr.js b/tests/page-templates/05-angular/18-autoxhr-before-page-load-alwayssendxhr.js new file mode 100644 index 000000000..14740e8fb --- /dev/null +++ b/tests/page-templates/05-angular/18-autoxhr-before-page-load-alwayssendxhr.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["angular", "ng339", "modules", "app", "angular_imgs", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2"]); + +describe("e2e/05-angular/18-autoxhr-before-page-load-alwayssendxhr", function() { + BOOMR_test.templates.SPA["18-autoxhr-before-page-load-alwayssendxhr"](); +}); diff --git a/tests/page-templates/05-angular/19-autoxhr-during-nav-alwayssendxhr.html b/tests/page-templates/05-angular/19-autoxhr-during-nav-alwayssendxhr.html new file mode 100644 index 000000000..fd5abca90 --- /dev/null +++ b/tests/page-templates/05-angular/19-autoxhr-during-nav-alwayssendxhr.html @@ -0,0 +1,35 @@ +<%= header %> +<%= boomerangScript %> + + + + + +
+ + + +<%= footer %> diff --git a/tests/page-templates/05-angular/19-autoxhr-during-nav-alwayssendxhr.js b/tests/page-templates/05-angular/19-autoxhr-during-nav-alwayssendxhr.js new file mode 100644 index 000000000..3ddd7f59a --- /dev/null +++ b/tests/page-templates/05-angular/19-autoxhr-during-nav-alwayssendxhr.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["angular", "ng339", "modules", "app", "angular_imgs", "angular_nav_routes", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2", "i"]); + +describe("e2e/05-angular/19-autoxhr-during-nav-alwayssendxhr", function() { + BOOMR_test.templates.SPA["19-autoxhr-during-nav-alwayssendxhr"](); +}); diff --git a/tests/page-templates/05-angular/20-no-resources.html b/tests/page-templates/05-angular/20-no-resources.html new file mode 100644 index 000000000..2f9959742 --- /dev/null +++ b/tests/page-templates/05-angular/20-no-resources.html @@ -0,0 +1,34 @@ +<%= header %> +<%= boomerangScript %> + + + + + +
+
+
+
+ + + + +<%= footer %> diff --git a/tests/page-templates/05-angular/20-no-resources.js b/tests/page-templates/05-angular/20-no-resources.js new file mode 100644 index 000000000..4e17123fd --- /dev/null +++ b/tests/page-templates/05-angular/20-no-resources.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["angular", "ng339", "modules", "app", "angular_html5_mode", "angular_nav_routes", "angular_imgs", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2", "i"]); + +describe("e2e/05-angular/20-no-resources", function() { + BOOMR_test.templates.SPA["20-no-resources"](); +}); diff --git a/tests/page-templates/05-angular/21-constant-mutations.html b/tests/page-templates/05-angular/21-constant-mutations.html new file mode 100644 index 000000000..2d9d94ab2 --- /dev/null +++ b/tests/page-templates/05-angular/21-constant-mutations.html @@ -0,0 +1,30 @@ +<%= header %> +<%= boomerangScript %> + + + + + +
+
+
+
+ + + +<%= footer %> diff --git a/tests/page-templates/05-angular/21-constant-mutations.js b/tests/page-templates/05-angular/21-constant-mutations.js new file mode 100644 index 000000000..d37781dbf --- /dev/null +++ b/tests/page-templates/05-angular/21-constant-mutations.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["angular", "ng339", "modules", "app", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2"]); + +describe("e2e/05-angular/21-constant-mutations", function() { + BOOMR_test.templates.SPA["21-constant-mutations"](); +}); diff --git a/tests/page-templates/05-angular/23-hard-wait-for-onload.html b/tests/page-templates/05-angular/23-hard-wait-for-onload.html new file mode 100644 index 000000000..6c9a4d0b6 --- /dev/null +++ b/tests/page-templates/05-angular/23-hard-wait-for-onload.html @@ -0,0 +1,26 @@ +<%= header %> +<%= boomerangScript %> + + + + +
+ + + + + +<%= footer %> diff --git a/tests/page-templates/05-angular/23-hard-wait-for-onload.js b/tests/page-templates/05-angular/23-hard-wait-for-onload.js new file mode 100644 index 000000000..776c667be --- /dev/null +++ b/tests/page-templates/05-angular/23-hard-wait-for-onload.js @@ -0,0 +1,8 @@ +/* eslint-env mocha */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["angular", "ng339", "modules", "app", "angular_imgs", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2"]); + +describe("e2e/05-angular/23-hard-wait-for-onload", function() { + BOOMR_test.templates.SPA["23-hard-wait-for-onload"](); +}); diff --git a/tests/page-templates/05-angular/24-route-filter.html b/tests/page-templates/05-angular/24-route-filter.html new file mode 100644 index 000000000..eddbb6432 --- /dev/null +++ b/tests/page-templates/05-angular/24-route-filter.html @@ -0,0 +1,46 @@ +<%= header %> + +<%= boomerangScript %> + + + + + + +
+
+
+
+ + + +<%= footer %> diff --git a/tests/page-templates/05-angular/24-route-filter.js b/tests/page-templates/05-angular/24-route-filter.js new file mode 100644 index 000000000..b1abedddf --- /dev/null +++ b/tests/page-templates/05-angular/24-route-filter.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["angular", "ng339", "navs", "angular_imgs", "angular_html5_mode", "angular_nav_routes", "angular_route_filter", "modules", "app", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2", "i"]); + +describe("e2e/05-angular/24-route-filter", function() { + BOOMR_test.templates.SPA["24-route-filter"](); +}); diff --git a/tests/page-templates/05-angular/25-delayed-boomerang-pre-config-snippet.html b/tests/page-templates/05-angular/25-delayed-boomerang-pre-config-snippet.html new file mode 100644 index 000000000..d6e68ca14 --- /dev/null +++ b/tests/page-templates/05-angular/25-delayed-boomerang-pre-config-snippet.html @@ -0,0 +1,48 @@ +<%= header %> +<%= boomerangDelayedSnippet %> + + + + + +
+ + + + + +<%= footer %> diff --git a/tests/page-templates/05-angular/25-delayed-boomerang-pre-config-snippet.js b/tests/page-templates/05-angular/25-delayed-boomerang-pre-config-snippet.js new file mode 100644 index 000000000..8eb83ffe3 --- /dev/null +++ b/tests/page-templates/05-angular/25-delayed-boomerang-pre-config-snippet.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["BOOMR_script_delay", "angular", "ng339", "timestamp", "modules", "app", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2"]); + +describe("e2e/05-angular/25-delayed-boomerang-pre-config-snippet", function() { + BOOMR_test.templates.SPA["25-delayed-boomerang-pre-config-snippet"](); +}); diff --git a/tests/page-templates/05-angular/27-route-change-back.html b/tests/page-templates/05-angular/27-route-change-back.html new file mode 100644 index 000000000..546a39a0f --- /dev/null +++ b/tests/page-templates/05-angular/27-route-change-back.html @@ -0,0 +1,48 @@ +<%= header %> +<%= boomerangScript %> + + + + + + +
+
+
+
+ + + +<%= footer %> diff --git a/tests/page-templates/05-angular/27-route-change-back.js b/tests/page-templates/05-angular/27-route-change-back.js new file mode 100644 index 000000000..06573628b --- /dev/null +++ b/tests/page-templates/05-angular/27-route-change-back.js @@ -0,0 +1,10 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert,angular */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["angular", "ng339", "angular_imgs", "angular_html5_mode", "angular_nav_routes", "modules", "app", "beaconNum", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2", "i"]); + +describe("e2e/05-angular/27-route-change-back", function() { + // use tests from #4 + BOOMR_test.templates.SPA["04-route-change"](); +}); diff --git a/tests/page-templates/05-angular/28-route-change-history-auto.html b/tests/page-templates/05-angular/28-route-change-history-auto.html new file mode 100644 index 000000000..4b9624e75 --- /dev/null +++ b/tests/page-templates/05-angular/28-route-change-history-auto.html @@ -0,0 +1,36 @@ +<%= header %> +<%= boomerangScript %> + + + + + + +
+
+
+
+ + + +<%= footer %> diff --git a/tests/page-templates/05-angular/28-route-change-history-auto.js b/tests/page-templates/05-angular/28-route-change-history-auto.js new file mode 100644 index 000000000..33bb8c10a --- /dev/null +++ b/tests/page-templates/05-angular/28-route-change-history-auto.js @@ -0,0 +1,10 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert,angular */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["angular", "ng339", "angular_imgs", "angular_html5_mode", "angular_nav_routes", "disableBoomerangHook", "modules", "app", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2", "i"]); + +describe("e2e/05-angular/28-route-change-history-auto", function() { + // use tests from #4 + BOOMR_test.templates.SPA["04-route-change"](); +}); diff --git a/tests/page-templates/05-angular/29-route-change-early-beacon.html b/tests/page-templates/05-angular/29-route-change-early-beacon.html new file mode 100644 index 000000000..db75735dd --- /dev/null +++ b/tests/page-templates/05-angular/29-route-change-early-beacon.html @@ -0,0 +1,142 @@ +<%= header %> +<%= boomerangScript %> + + + + + + +
+
+
+
+ + + +<%= footer %> diff --git a/tests/page-templates/05-angular/29-route-change-early-beacon.js b/tests/page-templates/05-angular/29-route-change-early-beacon.js new file mode 100644 index 000000000..ad50ef3d2 --- /dev/null +++ b/tests/page-templates/05-angular/29-route-change-early-beacon.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["angular", "ng339", "angular_imgs", "angular_html5_mode", "angular_nav_routes", "modules", "app", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2", "i"]); + +describe("e2e/05-angular/29-route-change-early-beacon", function() { + BOOMR_test.templates.SPA["29-route-change-early-beacon"](); +}); diff --git a/tests/page-templates/05-angular/support/app-ui-router.js b/tests/page-templates/05-angular/support/app-ui-router.js new file mode 100644 index 000000000..681d71e21 --- /dev/null +++ b/tests/page-templates/05-angular/support/app-ui-router.js @@ -0,0 +1,160 @@ +/* global angular */ +angular.module("app", ["ngResource", "ui.router"]) + .factory("Widgets", ["$resource", function($resource) { + // NOTE: Using absolute urls instead of relative URLs otherwise IE11 has problems + // resolving them in html5Mode + return { + query: function() { + var rnd = Math.random(); + + return $resource("/pages/05-angular/support/widgets.json", {}, { + query: { method: "GET", params: {rnd: rnd}, isArray: true } + }).query(); + } + }; + }]) + + .controller("mainCtrl", ["$scope", "Widgets", function($scope, Widgets) { + // these overwrite what was in the HTML + window.custom_metric_1 = 11; + window.custom_metric_2 = function() { + return 22; + }; + + $scope.rnd = Math.random(); + + window.custom_timer_1 = 11; + window.custom_timer_2 = function() { + return 22; + }; + + if (typeof window.performance !== "undefined" && + typeof window.performance.mark === "function") { + window.performance.mark("mark_usertiming"); + } + + $scope.imgs = typeof window.angular_imgs !== "undefined" ? window.angular_imgs : [0]; + $scope.hide_imgs = $scope.imgs[0] === -1; + + if (typeof $scope.widgets === "undefined") { + $scope.widgets = Widgets.query(function() { + window.lastWidgetJsonTimestamp = +(new Date()); + }); + } + }]) + + .controller("widgetDetailCtrl", ["$scope", "Widgets", "$stateParams", function($scope, Widgets, $stateParams) { + Widgets.query().$promise.then(function(widgets) { + $scope.rnd = Math.random(); + + var wid = parseInt($stateParams.widgetId); + + for (var i = 0; i < widgets.length; i++) { + if (widgets[i].id === wid) { + $scope.widget = widgets[i]; + break; + } + } + }); + }]) + + .config(["$stateProvider", "$urlRouterProvider", "$locationProvider", function($stateProvider, $urlRouterProvider, $locationProvider) { + if (typeof window.angular_html5_mode !== "undefined" && window.angular_html5_mode) { + $locationProvider.html5Mode(true); + } + + // grab the last portion of the path so that we can use this from multiple tests + var pathName = window.location.pathname.split("/"); + + pathName = pathName[pathName.length - 1]; + $urlRouterProvider.otherwise(pathName); + + // NOTE: Using absolute urls instead of relative URLs otherwise IE11 has problems + // resolving them in html5Mode + $stateProvider. + state("home", { + url: "/" + pathName, + templateUrl: "/pages/05-angular/support/home.html", + controller: "mainCtrl" + }). + state("widget", { + url: "/widgets/:widgetId", + templateUrl: "/pages/05-angular/support/widget.html", + controller: "widgetDetailCtrl" + }); + }]) + + .run(["$rootScope", "$location", "$timeout", function($rootScope, $location, $timeout) { + var hadRouteChange = false; + + $rootScope.$on("$stateChangeStart", function() { + hadRouteChange = true; + }); + + var hookOptions = {}; + + if (window.angular_route_wait) { + hookOptions.routeChangeWaitFilter = window.angular_route_wait; + } + + function hookAngularBoomerang() { + if (window.BOOMR && BOOMR.version) { + if (BOOMR.plugins && BOOMR.plugins.Angular) { + BOOMR.plugins.Angular.hook($rootScope, hadRouteChange, hookOptions); + } + + return true; + } + } + + if (!hookAngularBoomerang()) { + if (document.addEventListener) { + document.addEventListener("onBoomerangLoaded", hookAngularBoomerang); + } + else if (document.attachEvent) { + document.attachEvent("onpropertychange", function(e) { + e = e || window.event; + + if (e && e.propertyName === "onBoomerangLoaded") { + hookAngularBoomerang(); + } + }); + } + } + + if (typeof window.angular_nav_routes !== "undefined" && + Object.prototype.toString.call(window.angular_nav_routes) === "[object Array]") { + handler = function(beacon) { + // only continue for SPA beacons + if (beacon && !BOOMR.utils.inArray(beacon["http.initiator"], BOOMR.constants.BEACON_TYPE_SPAS)) { + return; + } + + if (window.angular_nav_routes.length > 0) { + var nextRoute = window.angular_nav_routes.shift(); + + $timeout(function() { + $location.url(nextRoute); + + if (window.angularBroadcastLocationAfterStateChange) { + $timeout(function() { + // for + $rootScope.$broadcast("$locationChangeStart", null, null, null, null); + }, 500); + } + }, 100); + } + + // set a timeout that calls the handler if beacon hasn't fired + if (window.angular_nav_route_timeout) { + if (window.angular_timerid) { + clearTimeout(window.angular_timerid); + } + + window.angular_timerid = setTimeout(handler, window.angular_nav_route_timeout); + } + }; + + BOOMR.subscribe("beacon", handler); + } + }]); diff --git a/tests/page-templates/05-angular/support/app.js b/tests/page-templates/05-angular/support/app.js new file mode 100644 index 000000000..ec74d7168 --- /dev/null +++ b/tests/page-templates/05-angular/support/app.js @@ -0,0 +1,183 @@ +/* global angular */ +var modules = ["ngResource"]; + +if (!window.angular_no_route) { + modules.push("ngRoute"); +} + +var app = angular.module("app", modules) + .factory("Widgets", ["$resource", function($resource) { + // NOTE: Using absolute urls instead of relative URLs otherwise IE11 has problems + // resolving them in html5Mode + return { + query: function() { + var rnd = Math.random(); + + return $resource("/delay?delay=250&file=/pages/05-angular/support/widgets.json", {}, { + query: { method: "GET", params: {rnd: rnd}, isArray: true } + }).query(); + } + }; + }]) + + .controller("mainCtrl", ["$scope", "Widgets", function($scope, Widgets) { + // these overwrite what was in the HTML + window.custom_metric_1 = 11; + window.custom_metric_2 = function() { + return 22; + }; + + $scope.rnd = Math.random(); + + $scope.carttotal = 444.44; + + window.custom_timer_1 = 11; + window.custom_timer_2 = function() { + return 22; + }; + + if (typeof window.performance !== "undefined" && + typeof window.performance.mark === "function") { + window.performance.mark("mark_usertiming"); + } + + $scope.imgs = typeof window.angular_imgs !== "undefined" ? window.angular_imgs : [0]; + $scope.hide_imgs = $scope.imgs[0] === -1; + + if (typeof $scope.widgets === "undefined") { + $scope.widgets = Widgets.query(function() { + window.lastWidgetJsonTimestamp = +(new Date()); + }); + } + }]) + + .controller("widgetDetailCtrl", ["$scope", "Widgets", "$routeParams", function($scope, Widgets, $routeParams) { + Widgets.query().$promise.then(function(widgets) { + $scope.rnd = Math.random(); + + var wid = parseInt($routeParams.widgetId); + + $scope.carttotal = 11.11 * wid; + + // these overwrite what was in the HTML + window.custom_metric_1 = wid; + window.custom_metric_2 = function() { + return 10 * wid; + }; + + window.custom_timer_1 = wid; + window.custom_timer_2 = function() { + return 10 * wid; + }; + + for (var i = 0; i < widgets.length; i++) { + if (widgets[i].id === wid) { + $scope.widget = widgets[i]; + break; + } + } + }); + }]) + .config(["$locationProvider", function($locationProvider) { + if (typeof window.angular_html5_mode !== "undefined" && window.angular_html5_mode) { + $locationProvider.html5Mode(true); + } + }]); + +if (!window.angular_no_route) { + app.config(["$routeProvider", function($routeProvider) { + // NOTE: Using absolute urls instead of relative URLs otherwise IE11 has problems + // resolving them in html5Mode + $routeProvider. + when("/widgets/:widgetId", { + templateUrl: "/pages/05-angular/support/widget.html", + controller: "widgetDetailCtrl" + }). + when("/widgets/delay/:widgetId", { + templateUrl: "/delay?delay=2000&file=/pages/05-angular/support/widget.html", + controller: "widgetDetailCtrl" + }). + when("/04-route-change.html", { + templateUrl: "/pages/05-angular/support/home.html", + controller: "mainCtrl" + }). + when("/24-route-filter.html", { + templateUrl: "/pages/05-angular/support/home.html", + controller: "mainCtrl" + }). + when("/08-no-resources.html", { + template: "

Empty

" + }). + when("/empty", { + template: "

Empty

" + }). + otherwise({ + templateUrl: "/pages/05-angular/support/home.html", + controller: "mainCtrl" + }); + }]); +} + +app.run(["$rootScope", "$location", "$timeout", function($rootScope, $location, $timeout) { + var hadRouteChange = false; + + $rootScope.$on("$routeChangeStart", function() { + hadRouteChange = true; + }); + + var hookOptions = {}; + + if (window.angular_route_wait) { + hookOptions.routeChangeWaitFilter = window.angular_route_wait; + } + + if (window.angular_route_filter) { + hookOptions.routeFilter = window.angular_route_filter; + } + + function hookAngularBoomerang() { + if (window.BOOMR && BOOMR.version) { + if (BOOMR.plugins && BOOMR.plugins.Angular) { + BOOMR.plugins.Angular.hook($rootScope, hadRouteChange, hookOptions); + } + + return true; + } + } + + if (!window.disableBoomerangHook) { + if (!hookAngularBoomerang()) { + if (document.addEventListener) { + document.addEventListener("onBoomerangLoaded", hookAngularBoomerang); + } + else if (document.attachEvent) { + document.attachEvent("onpropertychange", function(e) { + e = e || window.event; + + if (e && e.propertyName === "onBoomerangLoaded") { + hookAngularBoomerang(); + } + }); + } + } + } + + if (typeof window.angular_nav_routes !== "undefined" && BOOMR.utils.isArray(window.angular_nav_routes)) { + BOOMR.subscribe("onbeacon", function(beacon) { + // only continue for non-early SPA beacons + if (!BOOMR.utils.inArray(beacon["http.initiator"], BOOMR.constants.BEACON_TYPE_SPAS) || + typeof beacon.early !== "undefined") { + return; + } + + if (window.angular_nav_routes.length > 0) { + var nextRoute = window.angular_nav_routes.shift(); + + $timeout(function() { + $location.url(nextRoute); + // delay for 1s to allow for any late-loading images to appear + }, 1000); + } + }); + } +}]); diff --git a/tests/page-templates/05-angular/support/hello.js b/tests/page-templates/05-angular/support/hello.js new file mode 100644 index 000000000..702f4280c --- /dev/null +++ b/tests/page-templates/05-angular/support/hello.js @@ -0,0 +1 @@ +console.log("hello"); diff --git a/tests/page-templates/05-angular/support/home.html b/tests/page-templates/05-angular/support/home.html new file mode 100644 index 000000000..8c4b8dbcc --- /dev/null +++ b/tests/page-templates/05-angular/support/home.html @@ -0,0 +1,10 @@ +
+

Home

+ + + +
diff --git a/tests/page-templates/05-angular/support/img.jpg b/tests/page-templates/05-angular/support/img.jpg new file mode 100644 index 000000000..de3a1f7e9 Binary files /dev/null and b/tests/page-templates/05-angular/support/img.jpg differ diff --git a/tests/page-templates/05-angular/support/simple-spa-page-with-nav.html b/tests/page-templates/05-angular/support/simple-spa-page-with-nav.html new file mode 100644 index 000000000..c4c6b6c68 --- /dev/null +++ b/tests/page-templates/05-angular/support/simple-spa-page-with-nav.html @@ -0,0 +1,63 @@ + + + + + + Boomerang Test - Simple Testing SPA App + + + + + + + + + + + + + + + + + +
+ + + + + diff --git a/tests/page-templates/05-angular/support/simple-spa-page.html b/tests/page-templates/05-angular/support/simple-spa-page.html new file mode 100644 index 000000000..643321f4a --- /dev/null +++ b/tests/page-templates/05-angular/support/simple-spa-page.html @@ -0,0 +1,56 @@ + + + + + + Boomerang Test - Simple Testing SPA App + + + + + + + + + + + + + + + +
+ + + + diff --git a/tests/page-templates/05-angular/support/widget.html b/tests/page-templates/05-angular/support/widget.html new file mode 100644 index 000000000..0d63e951a --- /dev/null +++ b/tests/page-templates/05-angular/support/widget.html @@ -0,0 +1,5 @@ +

{{widget.name}}

+ + diff --git a/tests/page-templates/05-angular/support/widgets.json b/tests/page-templates/05-angular/support/widgets.json new file mode 100644 index 000000000..d3e310551 --- /dev/null +++ b/tests/page-templates/05-angular/support/widgets.json @@ -0,0 +1,18 @@ +[ + { + "id": 1, + "name": "Widget 1" + }, + { + "id": 2, + "name": "Widget 2" + }, + { + "id": 3, + "name": "Widget 3" + }, + { + "id": 10, + "name": "Widget 10" + } +] diff --git a/tests/page-templates/05-angular/tests.json5 b/tests/page-templates/05-angular/tests.json5 new file mode 100644 index 000000000..f7fda2522 --- /dev/null +++ b/tests/page-templates/05-angular/tests.json5 @@ -0,0 +1,32 @@ +{ + all: { + requires: ["spa", "history", "auto-xhr"] + }, + "108-iframe-wait-for-spa": { + requires: ["iframe-delay"] + }, + "109-multiple-iframes": { + requires: ["iframe-delay"] + }, + "111-iframe-delay-with-child-spa-routes": { + requires: ["iframe-delay"] + }, + "29-route-change-early-beacon": { + requires: ["early"] + }, + "124-continuity": { + requires: ["continuity"] + }, + "125-continuity-no-tti-first-beacon": { + requires: ["continuity"] + }, + "126-unload-params": { + requires: ["restiming", "memory"] + }, + "127-abld-metriconunload": { + requires: ["restiming", "memory"] + }, + "128-abld-no-metriconunload": { + requires: ["restiming", "memory"] + } +} diff --git a/tests/page-templates/06-bugs/01-resstart-before-navstart.html b/tests/page-templates/06-bugs/01-resstart-before-navstart.html new file mode 100644 index 000000000..c0f0de06d --- /dev/null +++ b/tests/page-templates/06-bugs/01-resstart-before-navstart.html @@ -0,0 +1,31 @@ +<%= header %> +<%= boomerangScript %> + + + +<%= footer %> diff --git a/tests/page-templates/06-bugs/01-resstart-before-navstart.js b/tests/page-templates/06-bugs/01-resstart-before-navstart.js new file mode 100644 index 000000000..148b593a5 --- /dev/null +++ b/tests/page-templates/06-bugs/01-resstart-before-navstart.js @@ -0,0 +1,31 @@ +/* eslint-env mocha */ +/* global assert */ + +describe("e2e/06-bugs/01-resstart-before-navstart", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should not have t_resp (if NavigationTiming is supported)", function() { + if (t.isNavigationTimingSupported()) { + var b = tf.lastBeacon(); + + assert.isUndefined(b.t_resp); + } + }); + + it("Should not have t_page (if NavigationTiming is supported)", function() { + if (t.isNavigationTimingSupported()) { + var b = tf.lastBeacon(); + + assert.isUndefined(b.t_page); + } + }); + + it("Should have nt_bad (if NavigationTiming is supported)", function() { + if (t.isNavigationTimingSupported()) { + var b = tf.lastBeacon(); + + assert.isDefined(b.nt_bad); + } + }); +}); diff --git a/tests/page-templates/06-bugs/02-resstart-before-fetchstart.html b/tests/page-templates/06-bugs/02-resstart-before-fetchstart.html new file mode 100644 index 000000000..470975911 --- /dev/null +++ b/tests/page-templates/06-bugs/02-resstart-before-fetchstart.html @@ -0,0 +1,33 @@ +<%= header %> +<%= boomerangScript %> + + + +<%= footer %> diff --git a/tests/page-templates/06-bugs/02-resstart-before-fetchstart.js b/tests/page-templates/06-bugs/02-resstart-before-fetchstart.js new file mode 100644 index 000000000..66cc17486 --- /dev/null +++ b/tests/page-templates/06-bugs/02-resstart-before-fetchstart.js @@ -0,0 +1,31 @@ +/* eslint-env mocha */ +/* global assert */ + +describe("e2e/06-bugs/02-resstart-before-fetchstart", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should not have t_resp (if NavigationTiming is supported)", function() { + if (t.isNavigationTimingSupported()) { + var b = tf.lastBeacon(); + + assert.isUndefined(b.t_resp); + } + }); + + it("Should not have t_page (if NavigationTiming is supported)", function() { + if (t.isNavigationTimingSupported()) { + var b = tf.lastBeacon(); + + assert.isUndefined(b.t_page); + } + }); + + it("Should have nt_bad (if NavigationTiming is supported)", function() { + if (t.isNavigationTimingSupported()) { + var b = tf.lastBeacon(); + + assert.isDefined(b.nt_bad); + } + }); +}); diff --git a/tests/page-templates/06-bugs/03-non-epoch-navtiming.html b/tests/page-templates/06-bugs/03-non-epoch-navtiming.html new file mode 100644 index 000000000..18c16836f --- /dev/null +++ b/tests/page-templates/06-bugs/03-non-epoch-navtiming.html @@ -0,0 +1,39 @@ +<%= header %> +<%= boomerangScript %> + + + +<%= footer %> diff --git a/tests/page-templates/06-bugs/03-non-epoch-navtiming.js b/tests/page-templates/06-bugs/03-non-epoch-navtiming.js new file mode 100644 index 000000000..fa24b6481 --- /dev/null +++ b/tests/page-templates/06-bugs/03-non-epoch-navtiming.js @@ -0,0 +1,37 @@ +/* eslint-env mocha */ +/* global assert */ + +describe("e2e/06-bugs/03-non-epoch-navtiming", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should not have t_resp (if NavigationTiming is supported)", function() { + if (t.isNavigationTimingSupported()) { + var b = tf.lastBeacon(); + + assert.isUndefined(b.t_resp); + } + }); + + it("Should not have t_page (if NavigationTiming is supported)", function() { + if (t.isNavigationTimingSupported()) { + var b = tf.lastBeacon(); + + assert.isUndefined(b.t_page); + } + }); + + it("Should have nt_bad (if NavigationTiming is supported)", function() { + if (t.isNavigationTimingSupported()) { + var b = tf.lastBeacon(); + + assert.isDefined(b.nt_bad); + } + }); + + it("Should not have an error param on the beacon", function() { + var b = tf.lastBeacon(); + + assert.isUndefined(b.errors); + }); +}); diff --git a/tests/page-templates/06-bugs/04-resend-far-future.html b/tests/page-templates/06-bugs/04-resend-far-future.html new file mode 100644 index 000000000..2b1696968 --- /dev/null +++ b/tests/page-templates/06-bugs/04-resend-far-future.html @@ -0,0 +1,29 @@ +<%= header %> +<%= boomerangScript %> + + + +<%= footer %> diff --git a/tests/page-templates/06-bugs/04-resend-far-future.js b/tests/page-templates/06-bugs/04-resend-far-future.js new file mode 100644 index 000000000..6e008f8ef --- /dev/null +++ b/tests/page-templates/06-bugs/04-resend-far-future.js @@ -0,0 +1,15 @@ +/* eslint-env mocha */ +/* global assert */ + +describe("e2e/06-bugs/04-resend-far-future", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should have nt_bad (if NavigationTiming is supported)", function() { + if (t.isNavigationTimingSupported()) { + var b = tf.lastBeacon(); + + assert.isDefined(b.nt_bad); + } + }); +}); diff --git a/tests/page-templates/06-bugs/100506.html b/tests/page-templates/06-bugs/100506.html new file mode 100644 index 000000000..6993a8a41 --- /dev/null +++ b/tests/page-templates/06-bugs/100506.html @@ -0,0 +1,94 @@ +<%= header %> +<%= boomerangScript %> + + + + + + + + + + + + + + + +<%= footer %> diff --git a/tests/page-templates/06-bugs/100506.js b/tests/page-templates/06-bugs/100506.js new file mode 100644 index 000000000..2b9bcc7b5 --- /dev/null +++ b/tests/page-templates/06-bugs/100506.js @@ -0,0 +1,46 @@ +/* eslint-env mocha */ +/* global assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["readyToSend", "image1", "timer1", "image2", "timer2", "image3", "timer3", "image4", "timer4"]); + +describe("e2e/06-bugs/100506", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have 5 beacons: 1 page load, 5 manual beacons", function(done) { + t.ensureBeaconCount(done, 5); + }); + + it("Should have the first beacon as rt.start navigation (if NavigationTiming is supported)", function(){ + if (t.isNavigationTimingSupported()) { + var beacon = tf.beacons[0]; + + assert.equal("navigation", beacon["rt.start"]); + } + }); + + it("Should have the first beacon as rt.start none (if NavigationTiming is not supported)", function(){ + if (!t.isNavigationTimingSupported()) { + var beacon = tf.beacons[0]; + + assert.equal("none", beacon["rt.start"]); + } + }); + + it("Should have all of the rest of the beacons having the image* page group", function() { + for (var i = 1; i < 4; i++) { + var beacon = tf.beacons[i]; + + assert.include(beacon["xhr.pg"], "image"); + } + }); + + it("Should have all of the rest of the beacons having rt.start = manual", function() { + for (var i = 1; i < 4; i++) { + var beacon = tf.beacons[i]; + + assert.equal("manual", beacon["rt.start"]); + } + }); +}); diff --git a/tests/page-templates/06-bugs/101405.html b/tests/page-templates/06-bugs/101405.html new file mode 100644 index 000000000..def569e77 --- /dev/null +++ b/tests/page-templates/06-bugs/101405.html @@ -0,0 +1,53 @@ +<%= header %> + + + + +<%= boomerangScript %> + + + +<%= footer %> diff --git a/tests/page-templates/06-bugs/101405.js b/tests/page-templates/06-bugs/101405.js new file mode 100644 index 000000000..7e057f5d6 --- /dev/null +++ b/tests/page-templates/06-bugs/101405.js @@ -0,0 +1,58 @@ +/* eslint-env mocha */ +/* global assert */ + +describe("e2e/06-bugs/101405", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should get 3 beacons: 1 onload, 2 xhr (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 3); + }); + }); + + it("Should have the first beacon be a Page Load beacon", function(done) { + t.ifAutoXHR(done, function() { + assert.isUndefined(tf.beacons[0]["http.initiator"]); + done(); + }); + }); + + it("Should have the second beacon be an XHR", function(done) { + t.ifAutoXHR(done, function() { + assert.equal(tf.beacons[1]["http.initiator"], "xhr"); + done(); + }); + }); + + it("Should have the second beacon have http.errno = 404", function(done) { + t.ifAutoXHR(done, function() { + assert.equal(tf.beacons[1]["http.errno"], "404"); + done(); + }); + }); + + it("Should have the third beacon be an XHR", function(done) { + t.ifAutoXHR(done, function() { + assert.equal(tf.beacons[2]["http.initiator"], "xhr"); + done(); + }); + }); + + it("Should have the third beacon have http.errno null", function(done) { + t.ifAutoXHR(done, function() { + assert.isUndefined(tf.beacons[2]["http.errno"]); + done(); + }); + }); + + it("Should have called the page-instrumented XHR", function(done) { + t.ifAutoXHR(done, function() { + assert.equal(XMLHttpRequest.prototype.oldOpenCalled, true); + done(); + }); + }); +}); diff --git a/tests/page-templates/06-bugs/101515-00.html b/tests/page-templates/06-bugs/101515-00.html new file mode 100644 index 000000000..087788d4e --- /dev/null +++ b/tests/page-templates/06-bugs/101515-00.html @@ -0,0 +1,53 @@ +<%= header %> + + +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/06-bugs/101515-00.js b/tests/page-templates/06-bugs/101515-00.js new file mode 100644 index 000000000..bddc367ef --- /dev/null +++ b/tests/page-templates/06-bugs/101515-00.js @@ -0,0 +1,30 @@ +/* eslint-env mocha */ +/* global assert */ + +describe("e2e/06-bugs/101515-00", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should get 2 beacons: 1 xhr, 1 post-'prerender'", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 2); + }); + + it("Should have the first beacon be a Page Load beacon", function() { + assert.isUndefined(tf.beacons[0]["http.initiator"]); + }); + + it("Should have the first beacon contain vis.pre", function() { + assert.equal(tf.beacons[0]["vis.pre"], 1); + }); + + it("Should have the second beacon be a xhr beacon", function() { + assert.equal(tf.beacons[1]["http.initiator"], "xhr"); + }); + + it("Should have the second beacon with non negative t_page", function() { + if (tf.beacons[1].t_page) { + assert.operator(parseInt(tf.beacons[1].t_page, 10), ">=", 0, "t_page is not negative"); + } + }); +}); diff --git a/tests/page-templates/06-bugs/101515-01.html b/tests/page-templates/06-bugs/101515-01.html new file mode 100644 index 000000000..186385ba8 --- /dev/null +++ b/tests/page-templates/06-bugs/101515-01.html @@ -0,0 +1,37 @@ +<%= header %> + +<%= boomerangScript %> + + + +<%= footer %> diff --git a/tests/page-templates/06-bugs/101515-01.js b/tests/page-templates/06-bugs/101515-01.js new file mode 100644 index 000000000..04b163c9c --- /dev/null +++ b/tests/page-templates/06-bugs/101515-01.js @@ -0,0 +1,41 @@ +/* eslint-env mocha */ +/* global assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["img"]); + +describe("e2e/06-bugs/101515-01", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should get 2 beacons: 1 onload, 1 xhr", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 2); + }); + + it("Should have the first beacon be a Page Load beacon", function() { + assert.isUndefined(tf.beacons[0]["http.initiator"]); + }); + + it("Should have the second beacon be a xhr beacon", function() { + assert.equal(tf.beacons[1]["http.initiator"], "xhr"); + }); + + it("Should have non negative timers", function() { + for (var i = 0; i < tf.beacons.length; i++) { + var b = tf.beacons[i]; + + if (b.t_other) { + var timers = b.t_other.split(","); + + if (timers && timers.length) { + for (var j = 0; j < timers.length; j++) { + var timer = timers[j].split("|"); + + assert.operator(parseInt(timer[1], 10), ">=", 0, timer[0] + " is not negative"); + } + } + } + } + }); +}); diff --git a/tests/page-templates/06-bugs/104197.html b/tests/page-templates/06-bugs/104197.html new file mode 100644 index 000000000..3ed27eed2 --- /dev/null +++ b/tests/page-templates/06-bugs/104197.html @@ -0,0 +1,56 @@ +<%= header %> + +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/06-bugs/104197.js b/tests/page-templates/06-bugs/104197.js new file mode 100644 index 000000000..5995e491d --- /dev/null +++ b/tests/page-templates/06-bugs/104197.js @@ -0,0 +1,51 @@ +/* eslint-env mocha */ +/* global assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["ga"]); + +describe("e2e/06-bugs/104197", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should get 3 beacons: 1 onload (ignored), 1 manual, 1 post-'prerender'", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 3); + }); + + it("Should have the first beacon be a Page Load beacon", function() { + assert.isUndefined(tf.beacons[0]["http.initiator"]); + }); + + it("Should have the second beacon be a manual beacon", function() { + assert.equal(tf.beacons[1]["rt.start"], "manual"); + }); + + it("Should have the third beacon be a Page Load beacon", function() { + assert.isUndefined(tf.beacons[2]["http.initiator"]); + }); + + it("Should have the third beacon contain vis.pre", function() { + assert.equal(tf.beacons[2]["vis.pre"], 1); + }); + + it("Should have the third beacon have NavigationTiming data (if available)", function() { + if (t.isNavigationTimingSupported()) { + assert.isDefined(tf.beacons[2].nt_nav_st); + assert.isDefined(tf.beacons[2].nt_load_st); + } + }); + + it("Should not have the third beacon have ResourceTiming data (if available)", function() { + // NOTE: In a non-faked preload scenario, ResourceTiming should be on the beacon + // But this test case is faking preload (after a regular load beacon has already fired, + // so restiming won't be on the beacon) + if (t.isResourceTimingSupported()) { + assert.isUndefined(tf.beacons[2].restiming); + } + }); + + it("Should have the third beacon have TPAnalytics data", function() { + assert.equal(tf.beacons[2]["tp.ga.clientid"], "XXXXXXXXXX.YYYYYYYYYY"); + }); +}); diff --git a/tests/page-templates/06-bugs/106359.html b/tests/page-templates/06-bugs/106359.html new file mode 100644 index 000000000..9109e93e8 --- /dev/null +++ b/tests/page-templates/06-bugs/106359.html @@ -0,0 +1,43 @@ +<%= header %> +<%= boomerangSnippet %> + + +
+<%= footer %> diff --git a/tests/page-templates/06-bugs/106359.js b/tests/page-templates/06-bugs/106359.js new file mode 100644 index 000000000..2b4ebbb87 --- /dev/null +++ b/tests/page-templates/06-bugs/106359.js @@ -0,0 +1,15 @@ +/* eslint-env mocha */ +/* global assert */ + +describe("e2e/06-bugs/106359", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have '#foo'", function() { + assert.isDefined(document.getElementById("foo"), "#foo exists"); + }); + + it("Should have '#foo' have the root page's prototype", function() { + assert.equal(document.getElementById("foo").foo, 1, "#foo has a '.foo' property"); + }); +}); diff --git a/tests/page-templates/06-bugs/111096-1.html b/tests/page-templates/06-bugs/111096-1.html new file mode 100644 index 000000000..0e7bab2c7 --- /dev/null +++ b/tests/page-templates/06-bugs/111096-1.html @@ -0,0 +1,34 @@ +<%= header %> +<%= boomerangSnippet %> + + +
+<%= footer %> diff --git a/tests/page-templates/06-bugs/111096-1.js b/tests/page-templates/06-bugs/111096-1.js new file mode 100644 index 000000000..2fb20db4f --- /dev/null +++ b/tests/page-templates/06-bugs/111096-1.js @@ -0,0 +1,20 @@ +/* eslint-env mocha */ +/* global assert */ + +describe("e2e/06-bugs/111096-1", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a beacon (if ResourceTiming is enabled and Boomerang loaded in an IFRAME)", function() { + if (t.isResourceTimingSupported() && BOOMR.window !== BOOMR.boomerang_frame) { + assert.notEqual(null, t.findResourceTimingBeacon()); + } + else { + return this.skip(); + } + }); + + it("Should not have used sendBeacon", function() { + assert.isUndefined(tf.beacons[0].sb); + }); +}); diff --git a/tests/page-templates/06-bugs/111096-2.html b/tests/page-templates/06-bugs/111096-2.html new file mode 100644 index 000000000..c33535de6 --- /dev/null +++ b/tests/page-templates/06-bugs/111096-2.html @@ -0,0 +1,40 @@ +<%= header %> +<%= boomerangSnippet %> + + +
+<%= footer %> diff --git a/tests/page-templates/06-bugs/111096-2.js b/tests/page-templates/06-bugs/111096-2.js new file mode 100644 index 000000000..4ea6ec34e --- /dev/null +++ b/tests/page-templates/06-bugs/111096-2.js @@ -0,0 +1,20 @@ +/* eslint-env mocha */ +/* global assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["XMLHttpRequestCopy", "orig_XMLHttpRequest", "origXhrCalled"]); + +describe("e2e/06-bugs/111096-2", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a beacon (if ResourceTiming is enabled)", function() { + if (t.isResourceTimingSupported()) { + assert.notEqual(null, t.findResourceTimingBeacon()); + } + }); + + it("Should have called window.orig_XMLHttpRequest instead of window.XMLHttpRequest", function() { + assert.equal(1, window.origXhrCalled); + }); +}); diff --git a/tests/page-templates/06-bugs/122358-document-write.html b/tests/page-templates/06-bugs/122358-document-write.html new file mode 100644 index 000000000..d016b3c09 --- /dev/null +++ b/tests/page-templates/06-bugs/122358-document-write.html @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + diff --git a/tests/page-templates/06-bugs/122358-document-write.js b/tests/page-templates/06-bugs/122358-document-write.js new file mode 100644 index 000000000..69d9c467d --- /dev/null +++ b/tests/page-templates/06-bugs/122358-document-write.js @@ -0,0 +1,11 @@ +/* eslint-env mocha */ +/* global assert */ + +describe("e2e/06-bugs/122358-document-write", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a beacon", function(done) { + t.ensureBeaconCount(done, 1); + }); +}); diff --git a/tests/page-templates/06-bugs/84041.html b/tests/page-templates/06-bugs/84041.html new file mode 100644 index 000000000..9381a495a --- /dev/null +++ b/tests/page-templates/06-bugs/84041.html @@ -0,0 +1,26 @@ +<%= header %> + + +<%= boomerangScript %> + +<%= boomerangScript %> +<%= footer %> diff --git a/tests/page-templates/06-bugs/84041.js b/tests/page-templates/06-bugs/84041.js new file mode 100644 index 000000000..46b476721 --- /dev/null +++ b/tests/page-templates/06-bugs/84041.js @@ -0,0 +1,11 @@ +/* eslint-env mocha */ +/* global assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["BOOMR_start"]); + +describe("e2e/06-bugs/84041", function() { + it("Should have only been initialized once", function() { + assert.equal(1, BOOMR.plugins.Test.initCount); + }); +}); diff --git a/tests/page-templates/06-bugs/87987.html b/tests/page-templates/06-bugs/87987.html new file mode 100644 index 000000000..cf1e6646d --- /dev/null +++ b/tests/page-templates/06-bugs/87987.html @@ -0,0 +1,20 @@ +<%= header %> + + +<%= boomerangSnippet %> + +<%= footer %> diff --git a/tests/page-templates/06-bugs/87987.js b/tests/page-templates/06-bugs/87987.js new file mode 100644 index 000000000..f0f9d96f2 --- /dev/null +++ b/tests/page-templates/06-bugs/87987.js @@ -0,0 +1,10 @@ +/* eslint-env mocha */ +/* global assert */ + +describe("e2e/06-bugs/87987", function() { + var tf = BOOMR.plugins.TestFramework; + + it("Should not have any errors", function() { + assert.isUndefined(tf.lastBeacon().errors); + }); +}); diff --git a/tests/page-templates/06-bugs/92560.html b/tests/page-templates/06-bugs/92560.html new file mode 100644 index 000000000..4c29e92f9 --- /dev/null +++ b/tests/page-templates/06-bugs/92560.html @@ -0,0 +1,36 @@ +<%= header %> + + +<%= boomerangScript %> + +<%= footer %> diff --git a/tests/page-templates/06-bugs/92560.js b/tests/page-templates/06-bugs/92560.js new file mode 100644 index 000000000..3684ddc34 --- /dev/null +++ b/tests/page-templates/06-bugs/92560.js @@ -0,0 +1,74 @@ +/* eslint-env mocha */ +/* global assert */ + +describe("e2e/06-bugs/92560", function() { + function test(href, expected) { + var anchor = document.createElement("a"); + + anchor.href = href; + assert.equal( + BOOMR.plugins.AutoXHR.getPathname(anchor), + expected); + } + + var t = BOOMR_test; + + it("Should get only 2 beacons: 1 onload, 1 xhr (2nd xhr should be excluded)", function(done) { + // because the XHRs might take over a second + this.timeout(10000); + + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 2); + }); + }); + + it("getPathname test - path/file.js", function() { + t.ifAutoXHR(null, function() { + test("path/file.js", "/pages/06-bugs/path/file.js"); + }); + }); + + it("getPathname test - /path/file.js", function() { + t.ifAutoXHR(null, function() { + test("/path/file.js", "/path/file.js"); + }); + }); + + it("getPathname test - //path/file.js", function() { + t.ifAutoXHR(null, function() { + test("//path/file.js", "/file.js"); + }); + }); + + it("getPathname test - ./path/file.js", function() { + t.ifAutoXHR(null, function() { + test("./path/file.js", "/pages/06-bugs/path/file.js"); + }); + }); + + it("getPathname test - ../path/file.js", function() { + t.ifAutoXHR(null, function() { + test("../path/file.js", "/pages/path/file.js"); + }); + }); + + it("getPathname test - #ref", function() { + t.ifAutoXHR(null, function() { + test("#ref", "/pages/06-bugs/92560.html"); + }); + }); + + it("getPathname test - ?val=1", function() { + t.ifAutoXHR(null, function() { + test("?val=1", "/pages/06-bugs/92560.html"); + }); + }); + + it("getPathname test - ../../../../file.js", function() { + t.ifAutoXHR(null, function() { + test("../../../../file.js", "/file.js"); + }); + }); +}); diff --git a/tests/page-templates/06-bugs/95726.html b/tests/page-templates/06-bugs/95726.html new file mode 100644 index 000000000..2a282988f --- /dev/null +++ b/tests/page-templates/06-bugs/95726.html @@ -0,0 +1,46 @@ +<%= header %> + + + +<%= footer %> diff --git a/tests/page-templates/06-bugs/95726.js b/tests/page-templates/06-bugs/95726.js new file mode 100644 index 000000000..3e8898f68 --- /dev/null +++ b/tests/page-templates/06-bugs/95726.js @@ -0,0 +1,13 @@ +/* eslint-env mocha */ +/* global assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["s"]); + +describe("e2e/06-bugs/95726", function() { + var tf = BOOMR.plugins.TestFramework; + + it("Should send a beacon", function(){ + assert.isTrue(tf.fired_onbeacon); + }); +}); diff --git a/tests/page-templates/06-bugs/99702.html b/tests/page-templates/06-bugs/99702.html new file mode 100644 index 000000000..40341efd6 --- /dev/null +++ b/tests/page-templates/06-bugs/99702.html @@ -0,0 +1,27 @@ +<%= header %> +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/06-bugs/99702.js b/tests/page-templates/06-bugs/99702.js new file mode 100644 index 000000000..29242bf71 --- /dev/null +++ b/tests/page-templates/06-bugs/99702.js @@ -0,0 +1,27 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert,BOOMR */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["timestamp"]); + +describe("e2e/06-bugs/99702", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should have sent two beacons", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 2); + }); + + it("The second beacon shouldn't have a t_page", function() { + var b = tf.lastBeacon(); + + assert.isUndefined(b.t_page); + }); + + it("The second beacon should have t_page.inv set", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["t_page.inv"]); + }); +}); diff --git a/tests/page-templates/06-bugs/issue-1014.html b/tests/page-templates/06-bugs/issue-1014.html new file mode 100644 index 000000000..968c10e3f --- /dev/null +++ b/tests/page-templates/06-bugs/issue-1014.html @@ -0,0 +1,18 @@ +<%= header %> +<%= boomerangSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/06-bugs/issue-1014.js b/tests/page-templates/06-bugs/issue-1014.js new file mode 100644 index 000000000..486d9f702 --- /dev/null +++ b/tests/page-templates/06-bugs/issue-1014.js @@ -0,0 +1,13 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/06-bug/issue-1014", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should not log error if we get error with message that contains NS_ERROR_FAILURE", function() { + var b = tf.lastBeacon(); + + assert.isUndefined(b.errors); + }); +}); diff --git a/tests/page-templates/06-bugs/issue-545.html b/tests/page-templates/06-bugs/issue-545.html new file mode 100644 index 000000000..89d58b333 --- /dev/null +++ b/tests/page-templates/06-bugs/issue-545.html @@ -0,0 +1,18 @@ +<%= header %> + +<%= boomerangScript %> + + + diff --git a/tests/page-templates/06-bugs/issue-545.js b/tests/page-templates/06-bugs/issue-545.js new file mode 100644 index 000000000..03de66a05 --- /dev/null +++ b/tests/page-templates/06-bugs/issue-545.js @@ -0,0 +1,17 @@ +/* eslint-env mocha */ +/* global assert,it,describe */ + +describe("e2e/06-bugs/issue-545", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should get 1 beacon: 1 onload", function(done) { + t.ensureBeaconCount(done, 1); + }); + + it("Should use Date.now if window.performance.now was a bogus function", function() { + if (typeof Date.now === "function") { + assert.equal(Date.now, BOOMR.now); + } + }); +}); diff --git a/tests/page-templates/06-bugs/issue-606.html b/tests/page-templates/06-bugs/issue-606.html new file mode 100644 index 000000000..d047247e5 --- /dev/null +++ b/tests/page-templates/06-bugs/issue-606.html @@ -0,0 +1,16 @@ +<%= header %> + +<%= boomerangSnippet %> + + + + +<%= footer %> diff --git a/tests/page-templates/06-bugs/issue-606.js b/tests/page-templates/06-bugs/issue-606.js new file mode 100644 index 000000000..9a1c42082 --- /dev/null +++ b/tests/page-templates/06-bugs/issue-606.js @@ -0,0 +1,35 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["ResourceTimingDecompression"]); + +describe("e2e/06-bug/issue-606", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should only have one iframe and one CSS in the filter (if ResourceTiming is supported)", function() { + if (t.isResourceTimingSupported()) { + var b = tf.lastBeacon(); + + var resources = ResourceTimingDecompression.decompressResources(JSON.parse(b.restiming)); + + assert.equal(resources.length, 2); + + var frameIndex = resources[0].initiatorType === "frame" ? 0 : 1; + var cssIndex = frameIndex === 0 ? 1 : 0; + + // find our iframe + assert.equal(resources[frameIndex].initiatorType, "frame"); + assert.include(resources[frameIndex].name, "support/92542-iframe.html"); + + // find our css + assert.equal(resources[cssIndex].initiatorType, "css"); + assert.include(resources[cssIndex].name, "support/img.jpg"); + } + }); +}); diff --git a/tests/page-templates/06-bugs/support/09.css b/tests/page-templates/06-bugs/support/09.css new file mode 100644 index 000000000..39c519269 --- /dev/null +++ b/tests/page-templates/06-bugs/support/09.css @@ -0,0 +1,3 @@ +body { + background-image: url("img.jpg") +} diff --git a/tests/page-templates/06-bugs/support/92542-iframe.html b/tests/page-templates/06-bugs/support/92542-iframe.html new file mode 100644 index 000000000..05a1c093d --- /dev/null +++ b/tests/page-templates/06-bugs/support/92542-iframe.html @@ -0,0 +1,8 @@ + + + + IFRAME w/out UserTiming polyfill + + + + diff --git a/tests/page-templates/06-bugs/support/img.jpg b/tests/page-templates/06-bugs/support/img.jpg new file mode 100644 index 000000000..de3a1f7e9 Binary files /dev/null and b/tests/page-templates/06-bugs/support/img.jpg differ diff --git a/tests/page-templates/06-bugs/tests.json5 b/tests/page-templates/06-bugs/tests.json5 new file mode 100644 index 000000000..bd5e9f729 --- /dev/null +++ b/tests/page-templates/06-bugs/tests.json5 @@ -0,0 +1,20 @@ +{ + "101515-00": { + requires: ["auto-xhr", "restiming"] + }, + "101515-01": { + requires: ["auto-xhr", "restiming"] + }, + "104197": { + requires: ["third-party-analytics", "restiming"] + }, + "104197-akamai": { + requires: ["third-party-analytics", "restiming"] + }, + "106359": { + requires: ["auto-xhr"] + }, + "issue-606": { + requires: ["restiming"] + } +} diff --git a/tests/page-templates/07-autoxhr/00-xhrs.html b/tests/page-templates/07-autoxhr/00-xhrs.html new file mode 100644 index 000000000..c84ecbacf --- /dev/null +++ b/tests/page-templates/07-autoxhr/00-xhrs.html @@ -0,0 +1,105 @@ +<%= header %> + + +<%= boomerangScript %> + +<%= footer %> diff --git a/tests/page-templates/07-autoxhr/00-xhrs.js b/tests/page-templates/07-autoxhr/00-xhrs.js new file mode 100644 index 000000000..e32463bfa --- /dev/null +++ b/tests/page-templates/07-autoxhr/00-xhrs.js @@ -0,0 +1,422 @@ +/* eslint-env mocha */ +/* global assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["xoUrl"]); + +describe("e2e/07-autoxhr/00-xhrs", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should get 13 beacons: 1 onload, 12 xhr (XMLHttpRequest is supported)", function(done) { + this.timeout(10000); + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 13); + }, + this.skip.bind(this) + ); + }); + + it("Should get 1 beacons: 1 onload, 0 xhr (XMLHttpRequest is not supported)", function(done) { + t.ifAutoXHR( + done, + this.skip.bind(this), + function() { + t.ensureBeaconCount(done, 1); + }); + }); + + it("Should have all XHR beacons set rt.nstart = navigationTiming (if NavigationTiming is supported)", function(done) { + var that = this; + + t.ifAutoXHR( + done, + function() { + if (typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + for (var i = 1; i <= 9; i++) { + assert.equal(tf.beacons[i]["rt.nstart"], BOOMR.plugins.RT.navigationStart()); + } + + done(); + } + else { + that.skip(); + } + }, + this.skip.bind(this) + ); + }); + + describe("Beacon 1 (onload)", function() { + it("Should have correct url", function(done) { + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[0].u, "00-xhrs.html"); + done(); + }, + this.skip.bind(this) + ); + }); + }); + + describe("Beacon 2 (xhr) for 200 async XHR (XMLHttpRequest is supported)", function() { + var i = 1; + + it("Should have correct url", function(done) { + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[i].u, "script200.js"); + done(); + }, + this.skip.bind(this) + ); + }); + + it("Should not contain status", function(done) { + t.ifAutoXHR( + done, + function() { + assert.isUndefined(tf.beacons[i]["http.errno"]); + done(); + }, + this.skip.bind(this) + ); + }); + }); + + describe("Beacon 3 (xhr) for 200 sync (XMLHttpRequest is supported)", function() { + var i = 2; + + it("Should have correct url", function(done) { + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[i].u, "script200.js"); + done(); + }, + this.skip.bind(this) + ); + }); + + it("Should not contain status (XMLHttpRequest is supported)", function(done) { + t.ifAutoXHR( + done, + function() { + assert.isUndefined(tf.beacons[i]["http.errno"]); + done(); + }, + this.skip.bind(this) + ); + }); + }); + + describe("Beacon 4 (xhr) for 404 async XHR (XMLHttpRequest is supported)", function() { + var i = 3; + + it("Should have correct url", function(done) { + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[i].u, "/404"); + done(); + }, + this.skip.bind(this) + ); + }); + + it("Should contain 404 status", function(done) { + t.ifAutoXHR( + done, + function() { + assert.equal(tf.beacons[i]["http.errno"], "404"); + done(); + }, + this.skip.bind(this) + ); + }); + }); + + describe("Beacon 5 (xhr) for 404 sync XHR (XMLHttpRequest is supported)", function() { + var i = 4; + + it("Should have correct url", function(done) { + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[i].u, "/404"); + done(); + }, + this.skip.bind(this) + ); + }); + + it("Should contain 404 status", function(done) { + t.ifAutoXHR( + done, + function() { + assert.equal(tf.beacons[i]["http.errno"], "404"); + done(); + }, + this.skip.bind(this) + ); + }); + }); + + describe("Beacon 6 (xhr) for X-O XHR (XMLHttpRequest is supported)", function() { + var i = 5; + + it("Should have correct url", function(done) { + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[i].u, window.xoUrl); + done(); + }, + this.skip.bind(this) + ); + }); + + it("Should contain XHR_STATUS_ERROR status", function(done) { + t.ifAutoXHR( + done, + function() { + assert.equal(tf.beacons[i]["http.errno"], "-998"); + done(); + }, + this.skip.bind(this) + ); + }); + }); + + describe("Beacon 7 (xhr) for Aborted XHR (XMLHttpRequest is supported)", function() { + var i = 6; + + it("Should have correct url", function(done) { + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[i].u, "script200.js"); + done(); + }, + this.skip.bind(this) + ); + }); + + it("Should contain XHR_STATUS_ABORT status", function(done) { + t.ifAutoXHR( + done, + function() { + assert.equal(tf.beacons[i]["http.errno"], "-999"); + done(); + }, + this.skip.bind(this) + ); + }); + }); + + describe("Beacon 8 (xhr) for timedout XHR (XMLHttpRequest is supported)", function() { + var i = 7; + + it("Should have correct url", function(done) { + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[i].u, "script200.js"); + done(); + }, + this.skip.bind(this) + ); + }); + + it("Should contain XHR_STATUS_TIMEOUT status", function(done) { + t.ifAutoXHR( + done, + function() { + assert.equal(tf.beacons[i]["http.errno"], "-1001"); + done(); + }, + this.skip.bind(this) + ); + }); + }); + + describe("Beacon 9 (xhr) for POST XHR (XMLHttpRequest is supported)", function() { + var i = 8; + + it("Should have correct url", function(done) { + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[i].u, "/blackhole"); + done(); + }, + this.skip.bind(this) + ); + }); + + it("Should not contain status", function(done) { + t.ifAutoXHR( + done, + function() { + assert.isUndefined(tf.beacons[i]["http.errno"]); + done(); + }, + this.skip.bind(this) + ); + }); + + it("Should contain POST method", function(done) { + t.ifAutoXHR( + done, + function() { + assert.equal(tf.beacons[i]["http.method"], "POST"); + done(); + }, + this.skip.bind(this) + ); + }); + }); + + describe("Beacon 10 (xhr) for timedout POST XHR (XMLHttpRequest is supported)", function() { + var i = 9; + + it("Should have correct url", function(done) { + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[i].u, "/blackhole"); + done(); + }, + this.skip.bind(this) + ); + }); + + it("Should contain XHR_STATUS_TIMEOUT status", function(done) { + t.ifAutoXHR( + done, + function() { + assert.equal(tf.beacons[i]["http.errno"], "-1001"); + done(); + }, + this.skip.bind(this) + ); + }); + + it("Should contain POST method", function(done) { + t.ifAutoXHR( + done, + function() { + assert.equal(tf.beacons[i]["http.method"], "POST"); + done(); + }, + this.skip.bind(this) + ); + }); + }); + + describe("Beacon 11 (xhr) for server dropped connection XHR (XMLHttpRequest is supported)", function() { + var i = 10; + + it("Should have correct url", function(done) { + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[i].u, "/drop"); + done(); + }, + this.skip.bind(this) + ); + }); + + it("Should contain XHR_STATUS_ERROR status", function(done) { + var that = this; + + t.ifAutoXHR( + done, + function() { + // proxied requests will return 502 + if (tf.beacons[i]["http.errno"] === "502") { + that.skip(); + } + + assert.equal(tf.beacons[i]["http.errno"], "-998"); + done(); + }, + this.skip.bind(this) + ); + }); + + it("Should contain 502 status if requested through a proxy", function(done) { + var that = this; + + t.ifAutoXHR( + done, + function() { + if (tf.beacons[i]["http.errno"] !== "502") { + that.skip(); + } + + assert.equal(tf.beacons[i]["http.errno"], "502"); + done(); + }, + this.skip.bind(this) + ); + }); + }); + + describe("Beacon 12 (xhr) for 500 async XHR (XMLHttpRequest is supported)", function() { + var i = 11; + + it("Should have correct url", function(done) { + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[i].u, "/500"); + done(); + }, + this.skip.bind(this) + ); + }); + + it("Should contain 500 status", function(done) { + t.ifAutoXHR( + done, + function() { + assert.equal(tf.beacons[i]["http.errno"], "500"); + done(); + }, + this.skip.bind(this) + ); + }); + }); + + describe("Beacon 13 (xhr) for 200 chunked async XHR (XMLHttpRequest is supported)", function() { + var i = 12; + + it("Should have correct url", function(done) { + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[i].u, "/chunked"); + done(); + }, + this.skip.bind(this) + ); + }); + + it("Should not contain status", function(done) { + t.ifAutoXHR( + done, + function() { + assert.isUndefined(tf.beacons[i]["http.errno"]); + done(); + }, + this.skip.bind(this) + ); + }); + }); +}); diff --git a/tests/page-templates/07-autoxhr/01-img-src-change.html b/tests/page-templates/07-autoxhr/01-img-src-change.html new file mode 100644 index 000000000..31b4ad4c3 --- /dev/null +++ b/tests/page-templates/07-autoxhr/01-img-src-change.html @@ -0,0 +1,53 @@ +<%= header %> +<%= boomerangScript %> + + + + +<%= footer %> diff --git a/tests/page-templates/07-autoxhr/01-img-src-change.js b/tests/page-templates/07-autoxhr/01-img-src-change.js new file mode 100644 index 000000000..61dc14982 --- /dev/null +++ b/tests/page-templates/07-autoxhr/01-img-src-change.js @@ -0,0 +1,84 @@ +/* eslint-env mocha */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["i"]); + +describe("e2e/07-autoxhr/01-img-src-change", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should get 2 beacons: 1 onload, 1 spa (AutoXHR is supported)", function(done) { + this.timeout(10000); + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 2); + }, + this.skip.bind(this)); + }); + + it("Should get 1 beacon: 1 onload (AutoXHR is not supported)", function(done) { + this.timeout(10000); + t.ifAutoXHR( + done, + this.skip.bind(this), + function() { + t.ensureBeaconCount(done, 1); + }, + this.skip.bind(this)); + }); + + describe("Beacon 1", function() { + }); + + describe("Beacon 2", function() { + var i = 1; + + it("Should have spa beacon type", function(done) { + t.ifAutoXHR( + done, + function() { + var b = tf.beacons[i]; + + assert.equal(b["http.initiator"], "spa"); + done(); + }, + this.skip.bind(this)); + }); + + it("Should have t_done time include image load times (if MutationObserver is supported)", function(done) { + if (t.isMutationObserverSupported()) { + t.ifAutoXHR( + done, + function() { + var b = tf.beacons[i]; + // ~ 500 img + 2000 img + 300 of setTimeout delays + + assert.closeTo(b.t_done, 2800, 250); + done(); + }, + this.skip.bind(this)); + } + else { + this.skip(); + } + }); + + it("Should not have t_done time include image load times (if MutationObserver is not supported)", function(done) { + if (!t.isMutationObserverSupported()) { + t.ifAutoXHR( + done, + function() { + var b = tf.beacons[i]; + + assert.closeTo(b.t_done, 0, 250); + done(); + }, + this.skip.bind(this)); + } + else { + this.skip(); + } + }); + }); +}); diff --git a/tests/page-templates/07-autoxhr/02-onclick.html b/tests/page-templates/07-autoxhr/02-onclick.html new file mode 100644 index 000000000..b51ad5ab6 --- /dev/null +++ b/tests/page-templates/07-autoxhr/02-onclick.html @@ -0,0 +1,70 @@ +<%= header %> +<%= boomerangScript %> + +
Click Me!
+
+ + + + + +<%= footer %> diff --git a/tests/page-templates/07-autoxhr/02-onclick.js b/tests/page-templates/07-autoxhr/02-onclick.js new file mode 100644 index 000000000..fe15b60fa --- /dev/null +++ b/tests/page-templates/07-autoxhr/02-onclick.js @@ -0,0 +1,70 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["eventFired"]); + +describe("e2e/07-autoxhr/02-onclick", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should have sent at 2 beacons, 1x onload, 1x xhr (if MutationObserver is supported)", function(done) { + this.timeout(10000); + + if (t.isMutationObserverSupported()) { + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 2); + }, + this.skip.bind(this)); + } + else { + this.skip(); + } + }); + + it("Should have sent 1 beacon, 1x onload, 0x xhr (if MutationObserver is not supported)", function(done) { + this.timeout(10000); + + if (!t.isMutationObserverSupported()) { + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 1); + }, + this.skip.bind(this)); + } + else { + this.skip(); + } + }); + + describe("Beacon 1", function() { + it("Should have the first beacon URL of the page as 'u'", function(){ + assert.include(tf.beacons[0].u, "02-onclick.html"); + }); + }); + + describe("Beacon 2 (click)", function() { + var i = 1; + + it("Should have the second beacon URL of the image as 'u' (if MutationObserver is supported)", function() { + if (BOOMR.plugins.AutoXHR && t.isMutationObserverSupported()) { + assert.include(tf.beacons[i].u, "img.jpg"); + } + else { + this.skip(); + } + }); + + it("Should have the second beacon http.initiator = 'click' (if MutationObserver is supported)", function() { + if (BOOMR.plugins.AutoXHR && t.isMutationObserverSupported()) { + assert.include(tf.beacons[i]["http.initiator"], "click"); + } + else { + this.skip(); + } + }); + }); +}); diff --git a/tests/page-templates/07-autoxhr/03-xhrs-overlapping.html b/tests/page-templates/07-autoxhr/03-xhrs-overlapping.html new file mode 100644 index 000000000..fe0107966 --- /dev/null +++ b/tests/page-templates/07-autoxhr/03-xhrs-overlapping.html @@ -0,0 +1,43 @@ +<%= header %> + +<%= boomerangScript %> + +<%= footer %> diff --git a/tests/page-templates/07-autoxhr/03-xhrs-overlapping.js b/tests/page-templates/07-autoxhr/03-xhrs-overlapping.js new file mode 100644 index 000000000..96471f807 --- /dev/null +++ b/tests/page-templates/07-autoxhr/03-xhrs-overlapping.js @@ -0,0 +1,141 @@ +/* eslint-env mocha */ +/* global assert */ + +describe("e2e/07-autoxhr/03-xhrs-overlapping", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should get 3 beacons: 1 onload, 2 xhr (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 3); + }, + this.skip.bind(this)); + }); + + describe("Beacon 2", function() { + it("Should contain a time of close to 0ms when the XHR is aborted", function(done) { + this.timeout(10000); + t.ifAutoXHR( + done, + function() { + assert.closeTo(tf.beacons[1].t_done, 0, 50); + done(); + }, + this.skip.bind(this)); + }); + + it("Should contain http.initiator = xhr", function(done) { + this.timeout(10000); + t.ifAutoXHR( + done, + function() { + assert.equal(tf.beacons[1]["http.initiator"], "xhr"); + done(); + }, + this.skip.bind(this)); + }); + + it("Should contain http.errno = -999", function(done) { + this.timeout(10000); + t.ifAutoXHR( + done, + function() { + assert.equal(tf.beacons[1]["http.errno"], -999); + done(); + }, + this.skip.bind(this)); + }); + + it("Should contain pgu of the page", function(done) { + this.timeout(10000); + t.ifAutoXHR( + done, + function() { + assert.equal(tf.beacons[1].pgu, document.location.href); + done(); + }, + this.skip.bind(this)); + }); + + it("Should contain u of the first XHR", function(done) { + this.timeout(10000); + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[1].u, "boomerang-latest-debug.js?2"); + done(); + }, + this.skip.bind(this)); + }); + }); + + describe("Beacon 3", function() { + it("Should contain a time of around 3,000ms when the XHR is aborted", function(done) { + this.timeout(10000); + t.ifAutoXHR( + done, + function() { + assert.closeTo(tf.beacons[2].t_done, 3000, 100); + done(); + }, + this.skip.bind(this)); + }); + + it("Should contain http.initiator = xhr", function(done) { + this.timeout(10000); + t.ifAutoXHR( + done, + function() { + assert.equal(tf.beacons[2]["http.initiator"], "xhr"); + done(); + }, + this.skip.bind(this)); + }); + + it("Should contain http.errno = -999", function(done) { + this.timeout(10000); + t.ifAutoXHR( + done, + function() { + assert.equal(tf.beacons[2]["http.errno"], -999); + done(); + }, + this.skip.bind(this)); + }); + + it("Should contain pgu of the page", function(done) { + this.timeout(10000); + t.ifAutoXHR( + done, + function() { + assert.equal(tf.beacons[2].pgu, document.location.href); + done(); + }, + this.skip.bind(this)); + }); + + it("Should contain u of the first XHR", function(done) { + this.timeout(10000); + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[2].u, "boomerang-latest-debug.js?1"); + done(); + }, + this.skip.bind(this)); + }); + }); + + it("Should get 1 beacons: 1 onload, 0 xhr (XMLHttpRequest === null)", function(done) { + this.timeout(10000); + t.ifAutoXHR( + done, + this.skip.bind(this), + function() { + t.ensureBeaconCount(done, 1); + }); + }); +}); diff --git a/tests/page-templates/07-autoxhr/04-xhr-use-rt.html b/tests/page-templates/07-autoxhr/04-xhr-use-rt.html new file mode 100644 index 000000000..a6f2c59ed --- /dev/null +++ b/tests/page-templates/07-autoxhr/04-xhr-use-rt.html @@ -0,0 +1,81 @@ +<%= header %> +<%= boomerangScript %> + + + +<%= footer %> diff --git a/tests/page-templates/07-autoxhr/04-xhr-use-rt.js b/tests/page-templates/07-autoxhr/04-xhr-use-rt.js new file mode 100644 index 000000000..ec7d577a1 --- /dev/null +++ b/tests/page-templates/07-autoxhr/04-xhr-use-rt.js @@ -0,0 +1,208 @@ +/* eslint-env mocha */ +/* global assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["t", "XHR_URL", "FETCH_URL"]); + +describe("e2e/07-autoxhr/04-xhr-use-rt", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + var a = document.createElement("a"); + + it("Should get 2 beacons: 1 onload, 2 xhr (XMLHttpRequest !== null and Fetch API is supported)", function(done) { + if (!t.isFetchApiSupported()) { + return this.skip(); + } + + this.timeout(10000); + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 3); + }, + this.skip.bind(this)); + }); + + it("Should get 2 beacons: 1 onload, 1 xhr (XMLHttpRequest !== null and Fetch API is not supported)", function(done) { + if (t.isFetchApiSupported()) { + return this.skip(); + } + + this.timeout(10000); + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 2); + }, + this.skip.bind(this)); + }); + + it("Should get 1 beacon: 1 onload, 0 xhr (XMLHttpRequest === null)", function(done) { + if (t.isFetchApiSupported()) { + return this.skip(); + } + + this.timeout(10000); + t.ifAutoXHR( + done, + this.skip.bind(this), + function() { + t.ensureBeaconCount(done, 1); + }); + }); + + describe("Beacon 1 (onload)", function() { + var i = 0; + + it("Should be an onload beacon", function() { + if (t.isNavigationTimingSupported()) { + assert.equal(tf.beacons[i]["rt.start"], "navigation"); + } + else { + assert.equal(tf.beacons[i]["rt.start"], "none"); + } + }); + }); + + describe("Beacon 2 (xhr)", function() { + var i = 1; + + it("Should have the beacon contain the URL of the XHR", function() { + assert.include(tf.beacons[i].u, "&id=xhr"); + }); + + it("Should have the beacon contain t_done of at least 3 seconds", function() { + assert.operator(tf.beacons[i].t_done, ">=", 3000, "t_done is at least 1 second"); + }); + + it("Should have the beacon contain t_resp of exactly the ResourceTiming time (if ResourceTiming is enabled)", function() { + if (t.isResourceTimingSupported()) { + a.href = window.XHR_URL; + var url = a.href; + var entry = t.findFirstResource(url); + + if (entry) { + assert.closeTo(tf.beacons[i].t_resp, entry.duration, 2); + } + else { + this.skip(); + } + } + else { + this.skip(); + } + }); + + it("Should have the beacon contain t_resp of at least 3 seconds (if ResourceTiming not enabled)", function() { + if (!t.isResourceTimingSupported()) { + assert.operator(tf.beacons[i].t_resp, ">=", 3000, "t_resp is at least 3 second"); + } + else { + this.skip(); + } + }); + + it("Should have the beacon contain t_page of at least 0 ms (if ResourceTiming is enabled)", function() { + if (t.isResourceTimingSupported()) { + assert.operator(tf.beacons[i].t_page, ">=", 0, "t_page is not negative"); + + if (tf.beacons[i].t_page >= 1000) { + // Some Chrome and IE versions incorrectly report RT if the page is busy + // See: https://bugs.chromium.org/p/chromium/issues/detail?id=824155 + this.skip(); + } + } + else { + this.skip(); + } + }); + + it("Should have the second beacon contain t_resp + t_page ~= t_done", function() { + assert.closeTo(tf.beacons[i].t_done, tf.beacons[i].t_page + tf.beacons[i].t_resp, 2); + }); + }); + + describe("Beacon 3 (xhr) (if Fetch API is supported)", function() { + var i = 2; + + it("Should have the beacon contain the URL of the fetch", function() { + if (!t.isFetchApiSupported()) { + return this.skip(); + } + + assert.include(tf.beacons[i].u, "&id=fetch"); + }); + + it("Should have the beacon contain t_done of at least 3 seconds", function() { + if (!t.isFetchApiSupported()) { + return this.skip(); + } + + assert.operator(tf.beacons[i].t_done, ">=", 3000, "t_done is at least 3 seconds"); + }); + + it("Should have the beacon contain t_resp of exactly the ResourceTiming time (if ResourceTiming is enabled)", function() { + if (!t.isFetchApiSupported()) { + return this.skip(); + } + + if (t.isResourceTimingSupported()) { + a.href = window.FETCH_URL; + var url = a.href; + var entry = t.findFirstResource(url); + + if (entry) { + assert.closeTo(tf.beacons[i].t_resp, entry.duration, 2); + } + else { + this.skip(); + } + } + else { + this.skip(); + } + }); + + it("Should have the beacon contain t_resp near 3 seconds (if ResourceTiming not enabled)", function() { + if (!t.isFetchApiSupported()) { + return this.skip(); + } + + if (!t.isResourceTimingSupported()) { + assert.closeTo(tf.beacons[i].t_resp, 3000, 250); + } + else { + this.skip(); + } + }); + + it("Should have the beacon contain t_page of at least 1000 ms (if MutationObserver is supported)", function() { + if (!t.isFetchApiSupported()) { + return this.skip(); + } + + if (t.isMutationObserverSupported()) { + // 3s in Chrome 67, 1s in other browsers + assert.operator(tf.beacons[i].t_page, ">=", 1000, "t_page is at least 1s"); + } + else { + this.skip(); + } + }); + + it("Should have the second beacon contain t_resp + t_page ~= t_done", function() { + if (!t.isFetchApiSupported()) { + return this.skip(); + } + + if (t.isResourceTimingSupported()) { + // unlike XHR, we don't get readystate events and can't fake timings if RT is not avail + assert.closeTo(tf.beacons[i].t_done, tf.beacons[i].t_page + tf.beacons[i].t_resp, 2); + } + else { + this.skip(); + } + }); + }); +}); diff --git a/tests/page-templates/07-autoxhr/05-xhr-before-onload-alwayssendxhr.html b/tests/page-templates/07-autoxhr/05-xhr-before-onload-alwayssendxhr.html new file mode 100644 index 000000000..241019895 --- /dev/null +++ b/tests/page-templates/07-autoxhr/05-xhr-before-onload-alwayssendxhr.html @@ -0,0 +1,25 @@ +<%= header %> + +<%= boomerangSnippet %> + + + +<%= footer %> diff --git a/tests/page-templates/07-autoxhr/05-xhr-before-onload-alwayssendxhr.js b/tests/page-templates/07-autoxhr/05-xhr-before-onload-alwayssendxhr.js new file mode 100644 index 000000000..851218812 --- /dev/null +++ b/tests/page-templates/07-autoxhr/05-xhr-before-onload-alwayssendxhr.js @@ -0,0 +1,83 @@ +/* eslint-env mocha */ +/* global assert */ + +describe("05-xhr-before-onload-alwayssendxhr", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should get 2 beacons: 1 onload, 1 xhr (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 2); + }, + this.skip.bind(this)); + }); + + it("Should have the first beacon be 'navigation' (if NavigationTiming is supported)", function() { + if (t.isNavigationTimingSupported()) { + assert.equal(tf.beacons[0]["rt.start"], "navigation"); + } + else { + this.skip(); + } + }); + + it("Should have the first beacon be 'none' (if NavigationTiming is not supported)", function() { + if (!t.isNavigationTimingSupported()) { + assert.equal(tf.beacons[0]["rt.start"], "none"); + } + else { + this.skip(); + } + }); + + it("Should have the first beacon have a restiming parameter (if ResourceTiming is supported)", function() { + if (t.isResourceTimingSupported()) { + assert.isDefined(tf.beacons[0].restiming); + } + else { + this.skip(); + } + }); + + it("Should have the first beacon have a rt.bmr parameter (if ResourceTiming is supported)", function() { + if (t.isResourceTimingSupported()) { + assert.isDefined(tf.beacons[0]["rt.bmr"]); + } + else { + this.skip(); + } + }); + + it("Should have the first beacon have a t_other parameter", function() { + assert.isDefined(tf.beacons[0].t_other); + + assert.include(tf.beacons[0].t_other, "boomerang"); + + if (t.isNavigationTimingSupported()) { + // these timers are never started, when we check to add them to beacon, + // because they don't have `start` on them, we check `cached_t_start` - which won't + // have a value becaus there's no nav timing + assert.include(tf.beacons[0].t_other, "boomr_fb"); + assert.include(tf.beacons[0].t_other, "boomr_ld"); + assert.include(tf.beacons[0].t_other, "boomr_lat"); + } + else { + this.skip(); + } + }); + + it("Should have the second beacon be an XHR", function() { + assert.equal(tf.beacons[1]["http.initiator"], "xhr"); + }); + + it("Should have the second beacon be missing the rt.bmr parameter", function() { + assert.isUndefined(tf.beacons[1]["rt.bmr"]); + }); + + it("Should have the second beacon be missing the t_other parameter", function() { + assert.isUndefined(tf.beacons[1].t_other); + }); +}); diff --git a/tests/page-templates/07-autoxhr/06-xhr-before-config-alwayssendxhr.html b/tests/page-templates/07-autoxhr/06-xhr-before-config-alwayssendxhr.html new file mode 100644 index 000000000..805fc18b9 --- /dev/null +++ b/tests/page-templates/07-autoxhr/06-xhr-before-config-alwayssendxhr.html @@ -0,0 +1,44 @@ +<%= header %> + +<%= boomerangSnippet %> +<%= instrumentXHRSnippet %> + +<%= footer %> diff --git a/tests/page-templates/07-autoxhr/06-xhr-before-config-alwayssendxhr.js b/tests/page-templates/07-autoxhr/06-xhr-before-config-alwayssendxhr.js new file mode 100644 index 000000000..0810999d3 --- /dev/null +++ b/tests/page-templates/07-autoxhr/06-xhr-before-config-alwayssendxhr.js @@ -0,0 +1,65 @@ +/* eslint-env mocha */ +/* global assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["xhr"]); + +describe("06-xhr-before-config-alwayssendxhr", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should get 4 beacons: 1 onload, 3 xhr (XMLHttpRequest !== null)", function(done) { + t.ifAutoXHR( + done, + function() { + assert.lengthOf(tf.beacons, 4); + done(); + }); + }); + + it("First beacon should be the navigation beacon", function() { + var beacon = t.findNavBeacon(); + + assert.isUndefined(beacon["http.initiator"]); + }); + + it("First beacon (navigation) should have rt.start = 'navigation' (if NavigationTiming is supported)", function() { + if (t.isNavigationTimingSupported()) { + var beacon = t.findNavBeacon(); + + assert.equal(beacon["rt.start"], "navigation"); + } + }); + + it("First beacon (navigation) should have rt.start = 'none' (if NavigationTiming is not supported)", function() { + if (!t.isNavigationTimingSupported()) { + var beacon = t.findNavBeacon(); + + assert.equal(beacon["rt.start"], "none"); + } + }); + + it("Second beacon should be an XHR, opened and completed before config", function() { + var beacon = t.findXhrBeacon(); + + assert.equal(beacon["http.initiator"], "xhr"); + assert.equal(beacon["rt.start"], "manual"); + assert.include(beacon.u, "boomerang-latest-debug.js&xhr1"); + }); + + it("Third beacon should be an XHR, opened before config, completed after config", function() { + var beacon = tf.beacons[2]; + + assert.equal(beacon["http.initiator"], "xhr"); + assert.equal(beacon["rt.start"], "manual"); + assert.include(beacon.u, "boomerang-latest-debug.js&xhr2"); + }); + + it("Fourth beacon should be an XHR, opened and completed after config", function() { + var beacon = tf.beacons[3]; + + assert.equal(beacon["http.initiator"], "xhr"); + assert.equal(beacon["rt.start"], "manual"); + assert.include(beacon.u, "boomerang-latest-debug.js&xhr3"); + }); +}); diff --git a/tests/page-templates/07-autoxhr/07-iframes.html b/tests/page-templates/07-autoxhr/07-iframes.html new file mode 100644 index 000000000..d1b0abb6b --- /dev/null +++ b/tests/page-templates/07-autoxhr/07-iframes.html @@ -0,0 +1,37 @@ +<%= header %> + +<%= boomerangScript %> + +<%= footer %> diff --git a/tests/page-templates/07-autoxhr/07-iframes.js b/tests/page-templates/07-autoxhr/07-iframes.js new file mode 100644 index 000000000..2ef9a2ce6 --- /dev/null +++ b/tests/page-templates/07-autoxhr/07-iframes.js @@ -0,0 +1,51 @@ +/* eslint-env mocha */ +/* global assert */ + +describe("e2e/07-autoxhr/07-iframes", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should get 2 beacons: 1 onload, 1 xhr (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 2); + }, + this.skip.bind(this)); + }); + + it("Should get 1 beacons: 1 onload (XMLHttpRequest === null)", function(done) { + this.timeout(10000); + t.ifAutoXHR( + done, + this.skip.bind(this), + function() { + t.ensureBeaconCount(done, 1); + }); + }); + + describe("Beacon 2", function() { + var i = 1; + + it("Should have the second beacon be an XHR", function(done) { + t.ifAutoXHR( + done, + function() { + assert.equal(tf.beacons[i]["http.initiator"], "xhr"); + done(); + }, + this.skip.bind(this)); + }); + + it("Should have t_done not include iframe time (be less than 1s)", function(done) { + t.ifAutoXHR( + done, + function() { + assert.operator(tf.beacons[i].t_done, "<", 1000); + done(); + }, + this.skip.bind(this)); + }); + }); +}); diff --git a/tests/page-templates/07-autoxhr/08-alwayssendxhr-errors.html b/tests/page-templates/07-autoxhr/08-alwayssendxhr-errors.html new file mode 100644 index 000000000..7f1bf4fb1 --- /dev/null +++ b/tests/page-templates/07-autoxhr/08-alwayssendxhr-errors.html @@ -0,0 +1,32 @@ +<%= header %> + +<%= boomerangScript %> + +<%= footer %> diff --git a/tests/page-templates/07-autoxhr/08-alwayssendxhr-errors.js b/tests/page-templates/07-autoxhr/08-alwayssendxhr-errors.js new file mode 100644 index 000000000..ea8faa1f8 --- /dev/null +++ b/tests/page-templates/07-autoxhr/08-alwayssendxhr-errors.js @@ -0,0 +1,51 @@ +/* eslint-env mocha */ +/* global assert */ + +describe("08-alwayssendxhr-errors", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should get 3 beacons: 1 onload, 2 xhr (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 3); + }); + }); + + it("Should have the first beacon be a Page Load beacon", function(done) { + t.ifAutoXHR(done, function() { + assert.isUndefined(tf.beacons[0]["http.initiator"]); + done(); + }); + }); + + it("Should have the second beacon be an XHR", function(done) { + t.ifAutoXHR(done, function() { + assert.equal(tf.beacons[1]["http.initiator"], "xhr"); + done(); + }); + }); + + it("Should have the second beacon have http.errno = 404", function(done) { + t.ifAutoXHR(done, function() { + assert.equal(tf.beacons[1]["http.errno"], "404"); + done(); + }); + }); + + it("Should have the third beacon be an XHR", function(done) { + t.ifAutoXHR(done, function() { + assert.equal(tf.beacons[2]["http.initiator"], "xhr"); + done(); + }); + }); + + it("Should have the third beacon have http.errno = -999", function(done) { + t.ifAutoXHR(done, function() { + assert.equal(tf.beacons[2]["http.errno"], "-999"); + done(); + }); + }); +}); diff --git a/tests/page-templates/07-autoxhr/10-img-src-change.html b/tests/page-templates/07-autoxhr/10-img-src-change.html new file mode 100644 index 000000000..323809142 --- /dev/null +++ b/tests/page-templates/07-autoxhr/10-img-src-change.html @@ -0,0 +1,51 @@ +<%= header %> +<%= boomerangScript %> + + + +<%= footer %> diff --git a/tests/page-templates/07-autoxhr/10-img-src-change.js b/tests/page-templates/07-autoxhr/10-img-src-change.js new file mode 100644 index 000000000..d85cf0a9c --- /dev/null +++ b/tests/page-templates/07-autoxhr/10-img-src-change.js @@ -0,0 +1,27 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["img", "listenersAdded", "listenersRemoved"]); + +describe("e2e/07-autoxhr/10-img-src-change", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent 1 beacon", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 1); + }); + + it("Should have removed event listeners from main-image (if MutationObserver is supported)", function() { + if (t.isMutationObserverSupported()) { + var img = document.getElementById("main-image"); + + assert.equal(window.listenersAdded, 6); // 3 load + 3 error listeners + assert.equal(window.listenersRemoved, 6); + } + else { + this.skip(); + } + }); +}); diff --git a/tests/page-templates/07-autoxhr/10-non-network-urls.html b/tests/page-templates/07-autoxhr/10-non-network-urls.html new file mode 100644 index 000000000..fa43aa4cd --- /dev/null +++ b/tests/page-templates/07-autoxhr/10-non-network-urls.html @@ -0,0 +1,37 @@ +<%= header %> + +<%= boomerangSnippet %> + +<%= footer %> diff --git a/tests/page-templates/07-autoxhr/10-non-network-urls.js b/tests/page-templates/07-autoxhr/10-non-network-urls.js new file mode 100644 index 000000000..36260251e --- /dev/null +++ b/tests/page-templates/07-autoxhr/10-non-network-urls.js @@ -0,0 +1,16 @@ +/* eslint-env mocha */ +/* global assert */ + +describe("e2e/07-autoxhr/10-non-network-urls", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should get 1 beacons", function(done) { + this.timeout(10000); + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 1); + }); + }); +}); diff --git a/tests/page-templates/07-autoxhr/20-xhrs-duplicate-no-tao.html b/tests/page-templates/07-autoxhr/20-xhrs-duplicate-no-tao.html new file mode 100644 index 000000000..c69825b8e --- /dev/null +++ b/tests/page-templates/07-autoxhr/20-xhrs-duplicate-no-tao.html @@ -0,0 +1,61 @@ +<%= header %> + + + +<%= boomerangScript %> + +<%= footer %> diff --git a/tests/page-templates/07-autoxhr/20-xhrs-duplicate-no-tao.js b/tests/page-templates/07-autoxhr/20-xhrs-duplicate-no-tao.js new file mode 100644 index 000000000..5706381b5 --- /dev/null +++ b/tests/page-templates/07-autoxhr/20-xhrs-duplicate-no-tao.js @@ -0,0 +1,10 @@ +/* eslint-env mocha */ +/* global assert */ +/* eslint no-loop-func:0 */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["xhrTimes"]); + +describe("e2e/07-autoxhr/20-xhrs-duplicate-no-tao", function() { + BOOMR_test.templates.XHR["00-xhrs-duplicate"](); +}); diff --git a/tests/page-templates/07-autoxhr/21-xhrs-duplicate-tao.html b/tests/page-templates/07-autoxhr/21-xhrs-duplicate-tao.html new file mode 100644 index 000000000..09aeefa75 --- /dev/null +++ b/tests/page-templates/07-autoxhr/21-xhrs-duplicate-tao.html @@ -0,0 +1,61 @@ +<%= header %> + + + +<%= boomerangScript %> + +<%= footer %> diff --git a/tests/page-templates/07-autoxhr/21-xhrs-duplicate-tao.js b/tests/page-templates/07-autoxhr/21-xhrs-duplicate-tao.js new file mode 100644 index 000000000..c16bb4a15 --- /dev/null +++ b/tests/page-templates/07-autoxhr/21-xhrs-duplicate-tao.js @@ -0,0 +1,10 @@ +/* eslint-env mocha */ +/* global assert */ +/* eslint no-loop-func:0 */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["xhrTimes"]); + +describe("e2e/07-autoxhr/21-xhrs-duplicate-tao", function() { + BOOMR_test.templates.XHR["00-xhrs-duplicate"](); +}); diff --git a/tests/page-templates/07-autoxhr/22-xhrs-duplicate-no-tao.html b/tests/page-templates/07-autoxhr/22-xhrs-duplicate-no-tao.html new file mode 100644 index 000000000..701800f9b --- /dev/null +++ b/tests/page-templates/07-autoxhr/22-xhrs-duplicate-no-tao.html @@ -0,0 +1,64 @@ +<%= header %> + + + +<%= boomerangScript %> + +<%= footer %> diff --git a/tests/page-templates/07-autoxhr/22-xhrs-duplicate-no-tao.js b/tests/page-templates/07-autoxhr/22-xhrs-duplicate-no-tao.js new file mode 100644 index 000000000..5ad3f6360 --- /dev/null +++ b/tests/page-templates/07-autoxhr/22-xhrs-duplicate-no-tao.js @@ -0,0 +1,10 @@ +/* eslint-env mocha */ +/* global assert */ +/* eslint no-loop-func:0 */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["xhrTimes"]); + +describe("e2e/07-autoxhr/22-xhrs-duplicate-no-tao", function() { + BOOMR_test.templates.XHR["00-xhrs-duplicate"](); +}); diff --git a/tests/page-templates/07-autoxhr/23-xhrs-duplicate-tao.html b/tests/page-templates/07-autoxhr/23-xhrs-duplicate-tao.html new file mode 100644 index 000000000..359d2690d --- /dev/null +++ b/tests/page-templates/07-autoxhr/23-xhrs-duplicate-tao.html @@ -0,0 +1,64 @@ +<%= header %> + + + +<%= boomerangScript %> + +<%= footer %> diff --git a/tests/page-templates/07-autoxhr/23-xhrs-duplicate-tao.js b/tests/page-templates/07-autoxhr/23-xhrs-duplicate-tao.js new file mode 100644 index 000000000..0d6ac8378 --- /dev/null +++ b/tests/page-templates/07-autoxhr/23-xhrs-duplicate-tao.js @@ -0,0 +1,10 @@ +/* eslint-env mocha */ +/* global assert */ +/* eslint no-loop-func:0 */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["xhrTimes"]); + +describe("e2e/07-autoxhr/23-xhrs-duplicate-tao", function() { + BOOMR_test.templates.XHR["00-xhrs-duplicate"](); +}); diff --git a/tests/page-templates/07-autoxhr/24-xhrs-duplicate-no-tao.html b/tests/page-templates/07-autoxhr/24-xhrs-duplicate-no-tao.html new file mode 100644 index 000000000..620a98fb0 --- /dev/null +++ b/tests/page-templates/07-autoxhr/24-xhrs-duplicate-no-tao.html @@ -0,0 +1,65 @@ +<%= header %> + + + +<%= boomerangScript %> + +<%= footer %> diff --git a/tests/page-templates/07-autoxhr/24-xhrs-duplicate-no-tao.js b/tests/page-templates/07-autoxhr/24-xhrs-duplicate-no-tao.js new file mode 100644 index 000000000..d3de93beb --- /dev/null +++ b/tests/page-templates/07-autoxhr/24-xhrs-duplicate-no-tao.js @@ -0,0 +1,10 @@ +/* eslint-env mocha */ +/* global assert */ +/* eslint no-loop-func:0 */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["xhrTimes"]); + +describe("e2e/07-autoxhr/24-xhrs-duplicate-no-tao", function() { + BOOMR_test.templates.XHR["00-xhrs-duplicate"](); +}); diff --git a/tests/page-templates/07-autoxhr/25-xhrs-duplicate-tao.html b/tests/page-templates/07-autoxhr/25-xhrs-duplicate-tao.html new file mode 100644 index 000000000..acabfcd19 --- /dev/null +++ b/tests/page-templates/07-autoxhr/25-xhrs-duplicate-tao.html @@ -0,0 +1,65 @@ +<%= header %> + + + +<%= boomerangScript %> + +<%= footer %> diff --git a/tests/page-templates/07-autoxhr/25-xhrs-duplicate-tao.js b/tests/page-templates/07-autoxhr/25-xhrs-duplicate-tao.js new file mode 100644 index 000000000..867832641 --- /dev/null +++ b/tests/page-templates/07-autoxhr/25-xhrs-duplicate-tao.js @@ -0,0 +1,10 @@ +/* eslint-env mocha */ +/* global assert */ +/* eslint no-loop-func:0 */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["xhrTimes"]); + +describe("e2e/07-autoxhr/25-xhrs-duplicate-tao", function() { + BOOMR_test.templates.XHR["00-xhrs-duplicate"](); +}); diff --git a/tests/page-templates/07-autoxhr/26-double-open.html b/tests/page-templates/07-autoxhr/26-double-open.html new file mode 100644 index 000000000..f54a5ccf9 --- /dev/null +++ b/tests/page-templates/07-autoxhr/26-double-open.html @@ -0,0 +1,25 @@ +<%= header %> + +<%= boomerangScript %> + +<%= footer %> diff --git a/tests/page-templates/07-autoxhr/26-double-open.js b/tests/page-templates/07-autoxhr/26-double-open.js new file mode 100644 index 000000000..9c2332158 --- /dev/null +++ b/tests/page-templates/07-autoxhr/26-double-open.js @@ -0,0 +1,41 @@ +/* eslint-env mocha */ +/* global assert */ +/* eslint no-loop-func:0 */ + +describe("e2e/07-autoxhr/26-double-open", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should get 2 beacons", function(done) { + this.timeout(10000); + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 2); + }); + }); + + it("Should have a second beacon that is an XHR", function() { + if (BOOMR.plugins.AutoXHR) { + var beacon = tf.beacons[1]; + + assert.equal(beacon["http.initiator"], "xhr"); + } + }); + + it("Should have a second beacon with a URL of 404", function() { + if (BOOMR.plugins.AutoXHR) { + var beacon = tf.beacons[1]; + + assert.include(beacon.u, "404"); + } + }); + + it("Should have a second beacon with a method of POST", function() { + if (BOOMR.plugins.AutoXHR) { + var beacon = tf.beacons[1]; + + assert.equal(beacon["http.method"], "POST"); + } + }); +}); diff --git a/tests/page-templates/07-autoxhr/27-onclick-iframe.html b/tests/page-templates/07-autoxhr/27-onclick-iframe.html new file mode 100644 index 000000000..ce912e608 --- /dev/null +++ b/tests/page-templates/07-autoxhr/27-onclick-iframe.html @@ -0,0 +1,69 @@ +<%= header %> +<%= boomerangScript %> + +
Click Me!
+ + + + + +<%= footer %> diff --git a/tests/page-templates/07-autoxhr/27-onclick-iframe.js b/tests/page-templates/07-autoxhr/27-onclick-iframe.js new file mode 100644 index 000000000..462880e58 --- /dev/null +++ b/tests/page-templates/07-autoxhr/27-onclick-iframe.js @@ -0,0 +1,70 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["eventFired"]); + +describe("e2e/07-autoxhr/27-onclick-iframe", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should have sent at least 2 beacons, 1x onload, 1x xhr", function(done) { + if (t.isMutationObserverSupported()) { + t.ifAutoXHR( + done, + function() { + assert.lengthOf(tf.beacons, 2); + done(); + } + ); + } + else { + assert.lengthOf(tf.beacons, 1); + done(); + } + }); + + describe("Beacon 2 (xhr)", function() { + it("Should not have '[object Object]' as the xhr.pg", function(done){ + if (t.isMutationObserverSupported()) { + t.ifAutoXHR( + done, + function() { + assert.notEqual(typeof tf.beacons[1]["xhr.pg"], "object"); + done(); + }); + } + else { + this.skip(); + } + }); + + it("Should have the IFRAME URL as the 'u'", function(done){ + if (t.isMutationObserverSupported()) { + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[1].u, "/delay"); + done(); + }); + } + else { + this.skip(); + } + }); + + it("Should have 'click' as the 'http.initiator'", function(done){ + if (t.isMutationObserverSupported()) { + t.ifAutoXHR( + done, + function() { + assert.equal(tf.beacons[1]["http.initiator"], "click"); + done(); + }); + } + else { + this.skip(); + } + }); + }); +}); diff --git a/tests/page-templates/07-autoxhr/28-wait-for-all-nodes.html b/tests/page-templates/07-autoxhr/28-wait-for-all-nodes.html new file mode 100644 index 000000000..9fa706d8a --- /dev/null +++ b/tests/page-templates/07-autoxhr/28-wait-for-all-nodes.html @@ -0,0 +1,47 @@ +<%= header %> +<%= boomerangScript %> + + + diff --git a/tests/page-templates/07-autoxhr/28-wait-for-all-nodes.js b/tests/page-templates/07-autoxhr/28-wait-for-all-nodes.js new file mode 100644 index 000000000..cb5e8b77d --- /dev/null +++ b/tests/page-templates/07-autoxhr/28-wait-for-all-nodes.js @@ -0,0 +1,71 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["beaconCount"]); + +describe("e2e/07-autoxhr/28-wait-for-all-nodes", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should have sent at least 2 beacons, 1x onload, 1x xhr", function(done) { + if (t.isMutationObserverSupported()) { + t.ifAutoXHR( + done, + function() { + assert.lengthOf(tf.beacons, 2); + done(); + }, + this.skip.bind(this) + ); + } + else { + // Will still send 2 beacons but the second one will only contain the XHR timing not MO... + t.ifAutoXHR( + done, + function() { + assert.lengthOf(tf.beacons, 2); + done(); + }, + this.skip.bind(this) + ); + } + }); + + describe("Beacon 2", function() { + var i = 1; + + it("Should have a t_done of at least 1600ms based on the duration of the XHR and images being fetched (if MutationObserver is supported)", function(done) { + // 400ms (XHR) + 1000ms (IMG2) + 200 (IMG3) + if (t.isMutationObserverSupported()) { + t.ifAutoXHR( + done, + function() { + assert.closeTo(tf.beacons[i].t_done, 1600, 300, "t_done was not close to 1600ms"); + done(); + }, + this.skip.bind(this) + ); + } + else { + this.skip(); + } + }); + + it("Should have a t_done of at least 400ms based on the duration of the XHR being fetched (if MutationObserver is not supported)", function(done) { + if (!t.isMutationObserverSupported()) { + t.ifAutoXHR( + done, + function() { + assert.closeTo(tf.beacons[i].t_done, 400, 50, "t_done is not close to 400ms"); + done(); + }, + this.skip.bind(this) + ); + } + else { + this.skip(); + } + }); + }); +}); diff --git a/tests/page-templates/07-autoxhr/29-xhrs-in-succession.html b/tests/page-templates/07-autoxhr/29-xhrs-in-succession.html new file mode 100644 index 000000000..b009d1844 --- /dev/null +++ b/tests/page-templates/07-autoxhr/29-xhrs-in-succession.html @@ -0,0 +1,56 @@ +<%= header %> + +<%= boomerangScript %> + +<%= footer %> diff --git a/tests/page-templates/07-autoxhr/29-xhrs-in-succession.js b/tests/page-templates/07-autoxhr/29-xhrs-in-succession.js new file mode 100644 index 000000000..23f3ffada --- /dev/null +++ b/tests/page-templates/07-autoxhr/29-xhrs-in-succession.js @@ -0,0 +1,143 @@ +/* eslint-env mocha */ +/* global assert */ + +describe("e2e/07-autoxhr/29-xhrs-in-succession", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should get 5 beacons: 1 onload, 4 xhr (XMLHttpRequest !== null)", function(done) { + this.timeout(5000); + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 5); + }, + this.skip.bind(this)); + }); + + describe("Beacon 2 (xhr)", function() { + var i = 1; + + it("Should have a time of around 0s", function(done) { + t.ifAutoXHR( + done, + function() { + assert.closeTo(tf.beacons[i].t_done, 0, 100); + done(); + }, + this.skip.bind(this)); + }); + + it("Should have the URL of the third XHR (aborted)", function(done) { + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[i].u, "id=xhr3"); + done(); + }, + this.skip.bind(this)); + }); + + it("Should have an error status (aborted)", function(done) { + t.ifAutoXHR( + done, + function() { + assert.equal(tf.beacons[i]["http.errno"], "-999"); + done(); + }, + this.skip.bind(this)); + }); + }); + + describe("Beacon 3 (xhr)", function() { + var i = 2; + + it("Should have a time of around 0s", function(done) { + t.ifAutoXHR( + done, + function() { + assert.closeTo(tf.beacons[i].t_done, 0, 100); + done(); + }, + this.skip.bind(this)); + }); + + it("Should have the URL of the fourth XHR (aborted)", function(done) { + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[i].u, "id=xhr4"); + done(); + }, + this.skip.bind(this)); + }); + + it("Should have an error status (aborted)", function(done) { + t.ifAutoXHR( + done, + function() { + assert.equal(tf.beacons[i]["http.errno"], "-999"); + done(); + }, + this.skip.bind(this)); + }); + }); + + describe("Beacon 4 (xhr)", function() { + var i = 3; + + it("Should have a time of around 2s", function(done) { + t.ifAutoXHR( + done, + function() { + assert.closeTo(tf.beacons[i].t_done, 2000, 100); + done(); + }, + this.skip.bind(this)); + }); + + it("Should have the URL of the second XHR", function(done) { + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[i].u, "id=xhr2"); + done(); + }, + this.skip.bind(this)); + }); + }); + + describe("Beacon 5 (xhr)", function() { + var i = 4; + + it("Should have a time of around 3s", function(done) { + t.ifAutoXHR( + done, + function() { + assert.closeTo(tf.beacons[i].t_done, 3000, 100); + done(); + }, + this.skip.bind(this)); + }); + + it("Should have a t_resp time of around 2s", function(done) { + t.ifAutoXHR( + done, + function() { + assert.closeTo(tf.beacons[i].t_resp, 2000, 100); + done(); + }, + this.skip.bind(this)); + }); + + it("Should have the URL of the first XHR", function(done) { + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[i].u, "id=xhr1"); + done(); + }, + this.skip.bind(this)); + }); + }); +}); diff --git a/tests/page-templates/07-autoxhr/30-ie1011-bug.html b/tests/page-templates/07-autoxhr/30-ie1011-bug.html new file mode 100644 index 000000000..08cc07421 --- /dev/null +++ b/tests/page-templates/07-autoxhr/30-ie1011-bug.html @@ -0,0 +1,50 @@ +<%= header %> + + +<%= boomerangScript %> + +<%= footer %> diff --git a/tests/page-templates/07-autoxhr/30-ie1011-bug.js b/tests/page-templates/07-autoxhr/30-ie1011-bug.js new file mode 100644 index 000000000..bd03e34fd --- /dev/null +++ b/tests/page-templates/07-autoxhr/30-ie1011-bug.js @@ -0,0 +1,72 @@ +/* eslint-env mocha */ +/* global assert */ + +describe("e2e/07-autoxhr/30-ie1011-bug", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should get 2 beacons: 1 onload, 1 xhr (XMLHttpRequest !== null)", function(done) { + this.timeout(5000); + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 2); + }, + this.skip.bind(this)); + }); + + it("Should have the XHR beacon have the complete data", function(done) { + t.ifAutoXHR( + done, + function() { + assert.isTrue(t.xhrValue.indexOf("1340") !== -1, "XHR data is complete"); + done(); + }, + this.skip.bind(this)); + }); + + it("Should have waited for the IMG to load (if MutationObserver is supported)", function(done) { + t.ifAutoXHR( + done, + function() { + if (t.isMutationObserverSupported()) { + assert.operator(tf.beacons[1].t_done, ">=", 3000); + done(); + } + else { + this.skip(); + } + }.bind(this), + this.skip.bind(this)); + }); + + it("Should not have waited for the IMG to load (if MutationObserver is not supported)", function(done) { + t.ifAutoXHR( + done, + function() { + if (!t.isMutationObserverSupported()) { + assert.operator(tf.beacons[1].t_done, "<", 1000); + done(); + } + else { + this.skip(); + } + }.bind(this), + this.skip.bind(this)); + }); + + it("Should have loaded right away (if ResourceTiming is not supported)", function(done) { + t.ifAutoXHR( + done, + function() { + if (!t.isResourceTimingSupported()) { + assert.operator(tf.beacons[1].t_done, ">=", 0); + done(); + } + else { + this.skip(); + } + }.bind(this), + this.skip.bind(this)); + }); +}); diff --git a/tests/page-templates/07-autoxhr/31-xhr-excludes.html b/tests/page-templates/07-autoxhr/31-xhr-excludes.html new file mode 100644 index 000000000..b250b336f --- /dev/null +++ b/tests/page-templates/07-autoxhr/31-xhr-excludes.html @@ -0,0 +1,39 @@ +<%= header %> +<%= boomerangScript %> + + + diff --git a/tests/page-templates/07-autoxhr/31-xhr-excludes.js b/tests/page-templates/07-autoxhr/31-xhr-excludes.js new file mode 100644 index 000000000..0e9b89fa3 --- /dev/null +++ b/tests/page-templates/07-autoxhr/31-xhr-excludes.js @@ -0,0 +1,28 @@ +/* eslint-env mocha */ +/* global assert,it,describe */ + +describe("e2e/07-autoxhr/31-xhr-excludes", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should get 2 beacons: 1 onload, 1 xhr (XMLHttpRequest !== null)", function(done) { + this.timeout(3000); + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 2); + }); + }); + + it("Should have the second beacon contain the URL of the second XHR", function(done) { + this.timeout(5000); + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(function() { + assert.include(tf.beacons[1].u, "script200.js"); + done(); + }, 2); + }); + }); +}); diff --git a/tests/page-templates/07-autoxhr/32-xhr-simple-filters.html b/tests/page-templates/07-autoxhr/32-xhr-simple-filters.html new file mode 100644 index 000000000..efc2e2617 --- /dev/null +++ b/tests/page-templates/07-autoxhr/32-xhr-simple-filters.html @@ -0,0 +1,49 @@ +<%= header %> +<%= boomerangScript %> + + diff --git a/tests/page-templates/07-autoxhr/32-xhr-simple-filters.js b/tests/page-templates/07-autoxhr/32-xhr-simple-filters.js new file mode 100644 index 000000000..77ca0e2f7 --- /dev/null +++ b/tests/page-templates/07-autoxhr/32-xhr-simple-filters.js @@ -0,0 +1,28 @@ +/* eslint-env mocha */ +/* global assert,it,describe */ + +describe("e2e/07-autoxhr/32-xhr-simple-filters", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should get 2 beacons: 1 onload, 1 xhr (XMLHttpRequest !== null)", function(done) { + this.timeout(3000); + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 2); + }); + }); + + it("Should have the second beacon contain the URL of the second XHR", function(done) { + this.timeout(5000); + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(function() { + assert.include(tf.beacons[1].u, "script200.js"); + done(); + }, 2); + }); + }); +}); diff --git a/tests/page-templates/07-autoxhr/33-xhr-filters-custom-context.html b/tests/page-templates/07-autoxhr/33-xhr-filters-custom-context.html new file mode 100644 index 000000000..4ba28ef6c --- /dev/null +++ b/tests/page-templates/07-autoxhr/33-xhr-filters-custom-context.html @@ -0,0 +1,64 @@ +<%= header %> +<%= boomerangScript %> + + + + + diff --git a/tests/page-templates/07-autoxhr/33-xhr-filters-custom-context.js b/tests/page-templates/07-autoxhr/33-xhr-filters-custom-context.js new file mode 100644 index 000000000..c42d2b834 --- /dev/null +++ b/tests/page-templates/07-autoxhr/33-xhr-filters-custom-context.js @@ -0,0 +1,31 @@ +/* eslint-env mocha */ +/* global assert,it,describe */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["AppManager", "manager", "result"]); + +describe("e2e/07-autoxhr/33-xhr-filters-custom-context", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should get 2 beacons: 1 onload, 1 xhr (XMLHttpRequest !== null)", function(done) { + this.timeout(3000); + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 2); + }); + }); + + it("Should have the second beacon contain the URL of the second XHR", function(done) { + this.timeout(5000); + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(function() { + assert.include(tf.beacons[1].u, "script200.js"); + done(); + }, 2); + }); + }); +}); diff --git a/tests/page-templates/07-autoxhr/34-xhrs-rt-buffer-full.html b/tests/page-templates/07-autoxhr/34-xhrs-rt-buffer-full.html new file mode 100644 index 000000000..9562e0cd2 --- /dev/null +++ b/tests/page-templates/07-autoxhr/34-xhrs-rt-buffer-full.html @@ -0,0 +1,66 @@ +<%= header %> +<%= boomerangScript %> + + diff --git a/tests/page-templates/07-autoxhr/34-xhrs-rt-buffer-full.js b/tests/page-templates/07-autoxhr/34-xhrs-rt-buffer-full.js new file mode 100644 index 000000000..8160f7bea --- /dev/null +++ b/tests/page-templates/07-autoxhr/34-xhrs-rt-buffer-full.js @@ -0,0 +1,66 @@ +/* eslint-env mocha */ +/* global assert,it,describe */ + +describe("e2e/07-autoxhr/34-xhrs-rt-buffer-full", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should get 5 beacons: 1 onload, 4 xhr (XMLHttpRequest !== null)", function(done) { + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 5); + }); + }); + + it("Should have each XHR beacon have http.initiator = 'xhr'", function(done) { + t.ifAutoXHR( + done, + function() { + for (var i = 1; i < tf.beacons.length; i++) { + assert.equal(tf.beacons[i]["http.initiator"], "xhr"); + } + + done(); + }); + }); + + it("Should have each XHR beacon have a t_page of less than 500ms", function(done) { + t.ifAutoXHR( + done, + function() { + for (var i = 1; i < tf.beacons.length; i++) { + assert.operator(tf.beacons[i].t_page, "<=", 500); + assert.operator(tf.beacons[i].t_page, ">=", 0); + } + + done(); + }); + }); + + it("Should have each XHR beacon have a t_resp of less than 500ms", function(done) { + t.ifAutoXHR( + done, + function() { + for (var i = 1; i < tf.beacons.length; i++) { + assert.operator(tf.beacons[i].t_resp, "<=", 500); + assert.operator(tf.beacons[i].t_resp, ">=", 0); + } + + done(); + }); + }); + + it("Should have each XHR beacon have a t_done of less than 500ms", function(done) { + t.ifAutoXHR( + done, + function() { + for (var i = 1; i < tf.beacons.length; i++) { + assert.operator(tf.beacons[i].t_done, "<=", 500); + assert.operator(tf.beacons[i].t_done, ">=", 0); + } + + done(); + }); + }); +}); diff --git a/tests/page-templates/07-autoxhr/35-hold-after-onload.html b/tests/page-templates/07-autoxhr/35-hold-after-onload.html new file mode 100644 index 000000000..2eefe52b5 --- /dev/null +++ b/tests/page-templates/07-autoxhr/35-hold-after-onload.html @@ -0,0 +1,39 @@ +<%= header %> +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/07-autoxhr/35-hold-after-onload.js b/tests/page-templates/07-autoxhr/35-hold-after-onload.js new file mode 100644 index 000000000..d7623fd7b --- /dev/null +++ b/tests/page-templates/07-autoxhr/35-hold-after-onload.js @@ -0,0 +1,75 @@ +/* eslint-env mocha */ +/* global assert,it,describe */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["isComplete"]); + +describe("e2e/07-autoxhr/35-hold-after-onload", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should get 2 beacons: 1 onload, 1 xhr (XMLHttpRequest !== null)", function(done) { + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 2); + }); + }); + + it("Should have the first beacon be a page load beacon", function() { + if (t.isNavigationTimingSupported()) { + assert.equal(tf.beacons[0]["rt.start"], "navigation"); + } + else { + assert.equal(tf.beacons[0]["rt.start"], "none"); + } + }); + + it("Should have the first beacon not have a http.initiator", function() { + assert.isUndefined(tf.beacons[0]["http.initiator"]); + }); + + it("Should have the first beacon have the URL of the page", function() { + assert.equal(tf.beacons[0].u, window.location.href); + }); + + it("Should have the second beacon have http.initiator = 'xhr'", function(done) { + t.ifAutoXHR( + done, + function() { + assert.equal(tf.beacons[1]["http.initiator"], "xhr"); + + done(); + }); + }); + + it("Should have the second beacon have URL of the XHR", function(done) { + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[1].u, "/assets/img.jpg"); + + done(); + }); + }); + + it("Should have the second beacon have pgu of the XHR", function(done) { + t.ifAutoXHR( + done, + function() { + assert.equal(tf.beacons[1].pgu, window.location.href); + + done(); + }); + }); + + it("Should have the second beacon have t_done of less than 500ms", function(done) { + t.ifAutoXHR( + done, + function() { + assert.operator(parseInt(tf.beacons[1].t_done, 10), "<", 500); + + done(); + }); + }); +}); diff --git a/tests/page-templates/07-autoxhr/36-response-type.html b/tests/page-templates/07-autoxhr/36-response-type.html new file mode 100644 index 000000000..992485cb6 --- /dev/null +++ b/tests/page-templates/07-autoxhr/36-response-type.html @@ -0,0 +1,50 @@ +<%= header %> +<%= boomerangScript %> + + + +<%= footer %> diff --git a/tests/page-templates/07-autoxhr/36-response-type.js b/tests/page-templates/07-autoxhr/36-response-type.js new file mode 100644 index 000000000..5eba06bb8 --- /dev/null +++ b/tests/page-templates/07-autoxhr/36-response-type.js @@ -0,0 +1,119 @@ +/* eslint-env mocha */ +/* global assert,it,describe */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["errorCount"]); + +describe("e2e/07-autoxhr/36-response-type", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should get 3 beacons: 1 onload, 2 xhr (XMLHttpRequest !== null)", function(done) { + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 3); + }); + }); + + it("Should have the first beacon be a page load beacon", function() { + if (t.isNavigationTimingSupported()) { + assert.equal(tf.beacons[0]["rt.start"], "navigation"); + } + else { + assert.equal(tf.beacons[0]["rt.start"], "none"); + } + }); + + it("Should have the first beacon not have a http.initiator", function() { + assert.isUndefined(tf.beacons[0]["http.initiator"]); + }); + + it("Should have the first beacon have the URL of the page", function() { + assert.equal(tf.beacons[0].u, window.location.href); + }); + + it("Should have the second beacon have http.initiator = 'xhr'", function(done) { + t.ifAutoXHR( + done, + function() { + assert.equal(tf.beacons[1]["http.initiator"], "xhr"); + + done(); + }); + }); + + it("Should have the second beacon have URL of the XHR", function(done) { + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[1].u, "support/data.json"); + + done(); + }); + }); + + it("Should have the second beacon have pgu of the XHR", function(done) { + t.ifAutoXHR( + done, + function() { + assert.equal(tf.beacons[1].pgu, window.location.href); + + done(); + }); + }); + + it("Should have the second beacon have t_done of less than 500ms", function(done) { + t.ifAutoXHR( + done, + function() { + assert.operator(parseInt(tf.beacons[1].t_done, 10), "<", 500); + + done(); + }); + }); + + it("Should have the third beacon have http.initiator = 'xhr'", function(done) { + t.ifAutoXHR( + done, + function() { + assert.equal(tf.beacons[2]["http.initiator"], "xhr"); + + done(); + }); + }); + + it("Should have the third beacon have URL of the XHR", function(done) { + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[2].u, "support/data.xml"); + + done(); + }); + }); + + it("Should have the third beacon have pgu of the XHR", function(done) { + t.ifAutoXHR( + done, + function() { + assert.equal(tf.beacons[2].pgu, window.location.href); + + done(); + }); + }); + + it("Should have the third beacon have t_done of less than 500ms", function(done) { + t.ifAutoXHR( + done, + function() { + assert.operator(parseInt(tf.beacons[2].t_done, 10), "<", 500); + + done(); + }); + }); + + it("Should have not had any errors", function() { + assert.equal(window.errorCount, 0); + }); +}); diff --git a/tests/page-templates/07-autoxhr/37-xhr-after-click.html b/tests/page-templates/07-autoxhr/37-xhr-after-click.html new file mode 100644 index 000000000..0bdf798db --- /dev/null +++ b/tests/page-templates/07-autoxhr/37-xhr-after-click.html @@ -0,0 +1,73 @@ +<%= header %> +<%= boomerangScript %> + +
Click Me!
+
+ + + + + +<%= footer %> diff --git a/tests/page-templates/07-autoxhr/37-xhr-after-click.js b/tests/page-templates/07-autoxhr/37-xhr-after-click.js new file mode 100644 index 000000000..7e4399c9b --- /dev/null +++ b/tests/page-templates/07-autoxhr/37-xhr-after-click.js @@ -0,0 +1,48 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["eventFired"]); + +describe("e2e/07-autoxhr/37-xhr-after-click", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should have sent at least 2 beacons, 1x onload, 1x xhr", function(done) { + t.ifAutoXHR( + done, + function() { + assert.lengthOf(tf.beacons, 2); + done(); + } + ); + }); + + it("Should have a first beacon have the URL of the page", function(done){ + if (t.isMutationObserverSupported()) { + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[0].u, "37-xhr-after-click.html"); + done(); + }); + } + else { + done(); + } + }); + + it("Should have a second beacon with the XHR URL in it", function(done){ + if (t.isMutationObserverSupported()) { + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[1].u, "img.jpg"); + done(); + }); + } + else { + done(); + } + }); +}); diff --git a/tests/page-templates/07-autoxhr/38-xhr-uninteresting-mo.html b/tests/page-templates/07-autoxhr/38-xhr-uninteresting-mo.html new file mode 100644 index 000000000..be7ed9095 --- /dev/null +++ b/tests/page-templates/07-autoxhr/38-xhr-uninteresting-mo.html @@ -0,0 +1,85 @@ +<%= header %> + + +<%= boomerangScript %> + +<%= footer %> diff --git a/tests/page-templates/07-autoxhr/38-xhr-uninteresting-mo.js b/tests/page-templates/07-autoxhr/38-xhr-uninteresting-mo.js new file mode 100644 index 000000000..84fedcb2a --- /dev/null +++ b/tests/page-templates/07-autoxhr/38-xhr-uninteresting-mo.js @@ -0,0 +1,126 @@ +/* eslint-env mocha */ +/* global assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["addDiv"]); + +describe("e2e/07-autoxhr/38-xhr-uninteresting-mo", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should get 8 beacons: 1 onload, 7 xhr (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 8); + }, + this.skip.bind(this)); + }); + + it("Should get 8 beacons: 1st onload beacon (XMLHttpRequest !== null)", function(done) { + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[0].u, "38-xhr-uninteresting-mo.html"); + done(); + }, + this.skip.bind(this)); + }); + + it("Should get 8 beacons: 2nd XHR 200 async beacon (XMLHttpRequest !== null)", function(done) { + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[1].u, "script200.js"); + done(); + }, + this.skip.bind(this)); + }); + + it("Should get 8 beacons: 3rd XHR 200 sync beacon (XMLHttpRequest !== null)", function(done) { + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[2].u, "script200.js"); + done(); + }, + this.skip.bind(this)); + }); + + it("Should get 8 beacons: 4th XHR 404 async beacon (XMLHttpRequest !== null)", function(done) { + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[3].u, "script404.js"); + done(); + }, + this.skip.bind(this)); + }); + + it("Should get 8 beacons: 5th XHR 404 sync beacon (XMLHttpRequest !== null)", function(done) { + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[4].u, "script404.js"); + done(); + }, + this.skip.bind(this)); + }); + + it("Should get 8 beacons: 6th X-O beacon (XMLHttpRequest !== null)", function(done) { + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[5].u, "akamai.com"); + done(); + }, + this.skip.bind(this)); + }); + + it("Should get 8 beacons: 7th abort beacon (XMLHttpRequest !== null)", function(done) { + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[6].u, "script200.js"); + done(); + }, + this.skip.bind(this)); + }); + + it("Should get 8 beacons: 8th timeout beacon (XMLHttpRequest !== null)", function(done) { + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[7].u, "script200.js"); + done(); + }, + this.skip.bind(this)); + }); + + it("Should get 1 beacons: 1 onload, 0 xhr (XMLHttpRequest === null)", function(done) { + t.ifAutoXHR( + done, + this.skip.bind(this), + function() { + t.ensureBeaconCount(done, 1); + }); + }); + + it("Should have all beacons set rt.nstart = navigationTiming (if NavigationTiming is supported)", function(done) { + t.ifAutoXHR( + done, + this.skip.bind(this), + function() { + if (typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + for (var i = 0; i <= 7; i++) { + assert.equal(tf.beacons[i]["rt.nstart"], BOOMR.plugins.RT.navigationStart()); + } + } + else { + return this.skip.bind(this); + } + } + ); + }); +}); diff --git a/tests/page-templates/07-autoxhr/39-uninteresting-mo-followed-by-interesting.html b/tests/page-templates/07-autoxhr/39-uninteresting-mo-followed-by-interesting.html new file mode 100644 index 000000000..4858bc22a --- /dev/null +++ b/tests/page-templates/07-autoxhr/39-uninteresting-mo-followed-by-interesting.html @@ -0,0 +1,48 @@ +<%= header %> + + +<%= boomerangScript %> + +<%= footer %> diff --git a/tests/page-templates/07-autoxhr/39-uninteresting-mo-followed-by-interesting.js b/tests/page-templates/07-autoxhr/39-uninteresting-mo-followed-by-interesting.js new file mode 100644 index 000000000..b664048ae --- /dev/null +++ b/tests/page-templates/07-autoxhr/39-uninteresting-mo-followed-by-interesting.js @@ -0,0 +1,110 @@ +/* eslint-env mocha */ +/* global assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["createXHREvent"]); + +describe("e2e/07-autoxhr/39-uninteresting-mo-followed-by-interesting", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should get 3 beacons: 1 onload, 2 xhr (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 3); + }); + }); + + it("Should have the first beacon be a page load beacon (XMLHttpRequest !== null)", function(done) { + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[0].u, "39-uninteresting-mo-followed-by-interesting.html"); + done(); + }); + }); + + describe("Beacon 2 (xhr)", function() { + var i = 1; + + it("Should be a XHR beacon (XMLHttpRequest !== null)", function(done) { + t.ifAutoXHR( + done, + function() { + assert.equal(tf.beacons[i]["http.initiator"], "xhr"); + done(); + }, + this.skip.bind(this)); + }); + + it("Should have the URL of the XHR (XMLHttpRequest !== null)", function(done) { + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[i].u, "blank.html"); + done(); + }, + this.skip.bind(this)); + }); + + it("Should have a t_done time that includes the image download", function(done) { + if (t.isMutationObserverSupported()) { + t.ifAutoXHR( + done, + function() { + var b = tf.beacons[i]; + + assert.closeTo(b.t_done, 1650, 150); // 1500-1800 : 500ms timer delay + 1000ms image delay + done(); + }, + this.skip.bind(this)); + } + else { + this.skip(); + } + }); + }); + + describe("Beacon 3 (xhr)", function() { + var i = 2; + + it("Should be a XHR beacon (XMLHttpRequest !== null)", function(done) { + t.ifAutoXHR( + done, + function() { + assert.equal(tf.beacons[i]["http.initiator"], "xhr"); + done(); + }, + this.skip.bind(this)); + }); + + it("Should have the URL of the XHR (XMLHttpRequest !== null)", function(done) { + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[i].u, "blank.html"); + done(); + }, + this.skip.bind(this)); + }); + + it("Should have a t_done time that includes the image download", function(done) { + if (t.isMutationObserverSupported()) { + t.ifAutoXHR( + done, + function() { + var b = tf.beacons[i]; + + assert.closeTo(b.t_done, 1650, 150); // 1500-1800 : 500ms timer delay + 1000ms image delay + done(); + }, + this.skip.bind(this)); + } + else { + this.skip(); + } + }); + }); +}); diff --git a/tests/page-templates/07-autoxhr/40-payload-xhr.html b/tests/page-templates/07-autoxhr/40-payload-xhr.html new file mode 100644 index 000000000..8b3cefe0e --- /dev/null +++ b/tests/page-templates/07-autoxhr/40-payload-xhr.html @@ -0,0 +1,58 @@ +<%= header %> +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/07-autoxhr/40-payload-xhr.js b/tests/page-templates/07-autoxhr/40-payload-xhr.js new file mode 100644 index 000000000..b38ab390c --- /dev/null +++ b/tests/page-templates/07-autoxhr/40-payload-xhr.js @@ -0,0 +1,56 @@ +/* eslint-env mocha */ +/* global assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["xhrResponse1", "xhrRequestPayload1", "xhrResponse2", "xhrRequestPayload2"]); + +describe("e2e/07-autoxhr/40-payload-xhr", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should get 3 beacons: 1 onload, 2 xhr (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 3); + }); + }); + + it("Should have not included the request payload data on the first XHR event", function(done) { + t.ifAutoXHR( + done, + function() { + assert.isUndefined(window.xhrRequestPayload1); + done(); + }); + }); + + it("Should have not included the response payload data on the first XHR event", function(done) { + t.ifAutoXHR( + done, + function() { + assert.isUndefined(window.xhrResponse1); + done(); + }); + }); + + it("Should have included the request payload data on the second XHR event", function(done) { + t.ifAutoXHR( + done, + function() { + assert.isDefined(window.xhrRequestPayload2); + done(); + }); + }); + + it("Should have included the response payload data on the second XHR event", function(done) { + t.ifAutoXHR( + done, + function() { + assert.isDefined(window.xhrResponse2); + done(); + }); + }); +}); diff --git a/tests/page-templates/07-autoxhr/41-xhr-abort-on-load.html b/tests/page-templates/07-autoxhr/41-xhr-abort-on-load.html new file mode 100644 index 000000000..b5cf27769 --- /dev/null +++ b/tests/page-templates/07-autoxhr/41-xhr-abort-on-load.html @@ -0,0 +1,37 @@ +<%= header %> +<%= boomerangScript %> + + + +<%= footer %> diff --git a/tests/page-templates/07-autoxhr/41-xhr-abort-on-load.js b/tests/page-templates/07-autoxhr/41-xhr-abort-on-load.js new file mode 100644 index 000000000..d45e21777 --- /dev/null +++ b/tests/page-templates/07-autoxhr/41-xhr-abort-on-load.js @@ -0,0 +1,41 @@ +/* eslint-env mocha */ +/* global assert */ + +describe("e2e/07-autoxhr/41-xhr-abort-on-load", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should get 2 beacons: 1 onload, 1 xhr (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 2); + }); + }); + + it("Should have the second beacon be an XHR (XMLHttpRequest !== null)", function(done) { + t.ifAutoXHR( + done, + function() { + var b = tf.lastBeacon(); + + assert.equal(b["http.initiator"], "xhr"); + + done(); + }); + }); + + it("Should have the second beacon with http.errno set to XHR_STATUS_ABORT (XMLHttpRequest !== null)", function(done) { + t.ifAutoXHR( + done, + function() { + var b = tf.lastBeacon(); + + assert.equal(b["http.errno"], -999); + + done(); + }); + }); +}); diff --git a/tests/page-templates/07-autoxhr/42-fetch-api.html b/tests/page-templates/07-autoxhr/42-fetch-api.html new file mode 100644 index 000000000..2654ca4e6 --- /dev/null +++ b/tests/page-templates/07-autoxhr/42-fetch-api.html @@ -0,0 +1,223 @@ +<%= header %> +<%= boomerangScript %> + + + + + + + +<%= footer %> diff --git a/tests/page-templates/07-autoxhr/42-fetch-api.js b/tests/page-templates/07-autoxhr/42-fetch-api.js new file mode 100644 index 000000000..475efd52d --- /dev/null +++ b/tests/page-templates/07-autoxhr/42-fetch-api.js @@ -0,0 +1,639 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,describe,it,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["t", "afterFirstBeacon", "xoUrl", "results"]); + +describe("e2e/07-autoxhr/42-fetch-api", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent 17 beacons (if Fetch API is supported)", function(done) { + this.timeout(10000); + + if (!t.isFetchApiSupported()) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 17); + }, + function() { + t.ensureBeaconCount(done, 1); + } + ); + }); + + it("Should have sent 1 beacon (if Fetch API is not supported)", function(done) { + this.timeout(10000); + + if (t.isFetchApiSupported()) { + return this.skip(); + } + + t.ensureBeaconCount(done, 1); + }); + + it("Should have http.type = f on all XHR beacons (if Fetch API is supported)", function() { + if (!t.isFetchApiSupported()) { + return this.skip(); + } + for (var i = 1; i < tf.beacons.length; i++) { + assert.equal(tf.beacons[i]["http.type"], "f"); + } + }); + + describe("Beacon 1 (onload)", function() { + it("Should be an onload beacon", function() { + assert.include(tf.beacons[0].u, "42-fetch-api.html"); + + if (t.isNavigationTimingSupported()) { + assert.equal(tf.beacons[0]["rt.start"], "navigation"); + } + else { + assert.equal(tf.beacons[0]["rt.start"], "none"); + } + }); + }); + + describe("Beacon 2 (xhr) for fetch with string url (if Fetch API is supported)", function() { + it("Should contain 'script200.js'", function(done) { + if (!t.isFetchApiSupported()) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[1].u, "script200.js"); + assert.include(tf.beacons[1].u, "req=0"); + done(); + }, + this.skip.bind(this) + ); + }); + }); + + describe("Beacon 3 (xhr) for fetch with Request object (if Fetch API is supported)", function() { + it("Should contain 'script200.js'", function(done) { + if (!t.isFetchApiSupported()) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[2].u, "script200.js"); + assert.include(tf.beacons[2].u, "req=1"); + done(); + }, + this.skip.bind(this) + ); + }); + }); + + describe("Beacon 4 (xhr) for chunked response fetch (if Fetch API and ReadableStream are supported)", function() { + it("Should contain '/chunked'", function(done) { + if (!t.isFetchApiSupported() || !window.ReadableStream) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[3].u, "/chunk"); + done(); + }, + this.skip.bind(this) + ); + }); + }); + + describe("Beacon 5 (xhr) for 404 status fetch (if Fetch API is supported)", function() { + it("Should contain '/404'", function(done) { + if (!t.isFetchApiSupported()) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[4].u, "/404"); + done(); + }, + this.skip.bind(this) + ); + }); + + it("Should contain 404 status", function(done) { + if (!t.isFetchApiSupported()) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + assert.equal(tf.beacons[4]["http.errno"], "404"); + done(); + }, + this.skip.bind(this) + ); + }); + }); + + describe("Beacon 6 (xhr) for 500 status fetch (if Fetch API is supported)", function() { + it("Should contain '/500'", function(done) { + if (!t.isFetchApiSupported()) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[5].u, "/500"); + done(); + }, + this.skip.bind(this) + ); + }); + + it("Should contain 500 status", function(done) { + if (!t.isFetchApiSupported()) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + assert.equal(tf.beacons[5]["http.errno"], "500"); + done(); + }, + this.skip.bind(this) + ); + }); + }); + + describe("Beacon 7 (xhr) for server dropped connection fetch (if Fetch API is supported)", function() { + var i = 6; + + it("Should contain '/drop'", function(done) { + if (!t.isFetchApiSupported()) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[i].u, "/drop"); + done(); + }, + this.skip.bind(this) + ); + }); + + it("Should contain XHR_STATUS_ERROR status", function(done) { + var that = this; + + t.ifAutoXHR( + done, + function() { + // proxied requests will return 502 + if (tf.beacons[i]["http.errno"] === "502") { + that.skip(); + } + + assert.equal(tf.beacons[i]["http.errno"], "-998"); + done(); + }, + this.skip.bind(this) + ); + }); + + it("Should contain 502 status if requested through a proxy", function(done) { + var that = this; + + t.ifAutoXHR( + done, + function() { + if (tf.beacons[i]["http.errno"] !== "502") { + that.skip(); + } + + assert.equal(tf.beacons[i]["http.errno"], "502"); + done(); + }, + this.skip.bind(this) + ); + }); + }); + + describe("Beacon 8 (xhr) for POST fetch with string url and response body not used (if Fetch API is supported)", function() { + var i = 7; + + it("Should contain '/chunked'", function(done) { + if (!t.isFetchApiSupported()) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[i].u, "/chunked"); + assert.include(tf.beacons[i].u, "req=0"); + done(); + }, + this.skip.bind(this) + ); + }); + + it("Should have http.method = POST", function(done) { + if (!t.isFetchApiSupported()) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + assert.equal(tf.beacons[i]["http.method"], "POST"); + done(); + }, + this.skip.bind(this) + ); + }); + + it("Should contain fetch.bnu=1", function(done) { + if (!t.isFetchApiSupported()) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + assert.equal(tf.beacons[i]["fetch.bnu"], "1"); + done(); + }, + this.skip.bind(this) + ); + }); + + it("Should contain t_page and t_resp (if ResourceTiming is supported)", function(done) { + // response should have been fast enough for us to capture the RT entry + if (!t.isFetchApiSupported() || !t.isResourceTimingSupported()) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + assert.isDefined(tf.beacons[i].t_page); + assert.isDefined(tf.beacons[i].t_resp); + done(); + }, + this.skip.bind(this) + ); + }); + }); + + describe("Beacon 9 (xhr) for POST fetch with Request object and response body not used (if Fetch API is supported)", function() { + var i = 8; + + it("Should contain '/chunked'", function(done) { + if (!t.isFetchApiSupported()) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[i].u, "/chunked"); + assert.include(tf.beacons[i].u, "req=1"); + done(); + }, + this.skip.bind(this) + ); + }); + + it("Should have http.method = POST", function(done) { + if (!t.isFetchApiSupported()) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + assert.equal(tf.beacons[i]["http.method"], "POST"); + done(); + }, + this.skip.bind(this) + ); + }); + + it("Should contain fetch.bnu=1", function(done) { + if (!t.isFetchApiSupported()) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + assert.equal(tf.beacons[i]["fetch.bnu"], "1"); + done(); + }, + this.skip.bind(this) + ); + }); + + it("Should not contain t_page and t_resp (if ResourceTiming is supported)", function(done) { + // response should have been received too late for us to capture the RT entry + if (!t.isFetchApiSupported() || !t.isResourceTimingSupported()) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + assert.isUndefined(tf.beacons[i].t_page); + assert.isUndefined(tf.beacons[i].t_resp); + done(); + }, + this.skip.bind(this) + ); + }); + }); + + describe("Beacon 10 (xhr) for POST fetch with Request object and init override (if Fetch API is supported)", function() { + var i = 9; + + it("Should contain '/blackhole'", function(done) { + if (!t.isFetchApiSupported()) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[i].u, "/blackhole"); + assert.include(tf.beacons[i].u, "req=1"); + done(); + }, + this.skip.bind(this) + ); + }); + + it("Should have http.method = POST", function(done) { + if (!t.isFetchApiSupported()) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + assert.equal(tf.beacons[i]["http.method"], "POST"); + done(); + }, + this.skip.bind(this) + ); + }); + }); + + describe("Beacon 11 (xhr) for aborted fetch (if Fetch API is supported)", function() { + var i = 10; + + it("Should contain '/delay'", function(done) { + if (!t.isFetchApiSupported()) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[i].u, "/delay"); + done(); + }, + this.skip.bind(this) + ); + }); + + it("Should contain XHR_STATUS_ABORT status (if AbortController is supported)", function(done) { + if (!t.isFetchApiSupported() || !window.AbortController) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + assert.equal(tf.beacons[i]["http.errno"], "-999"); + done(); + }, + this.skip.bind(this) + ); + }); + }); + + describe("Beacon 12 (xhr) for X-O fetch with same-origin policy (if Fetch API is supported)", function() { + var i = 11; + + it("Should contain correct url", function(done) { + if (!t.isFetchApiSupported()) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[i].u, window.xoUrl); + done(); + }, + this.skip.bind(this) + ); + }); + + it("Should contain XHR_STATUS_ERROR status", function(done) { + if (!t.isFetchApiSupported()) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + assert.equal(tf.beacons[i]["http.errno"], "-998"); + done(); + }, + this.skip.bind(this) + ); + }); + }); + + describe("Beacon 13 (xhr) for fetch read with `text` (if Fetch API is supported)", function() { + var i = 12; + + it("Should contain correct url", function(done) { + if (!t.isFetchApiSupported()) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[i].u, "req=text"); + done(); + }, + this.skip.bind(this) + ); + }); + + it("Should have read the correct data", function(done) { + if (!t.isFetchApiSupported()) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + assert.equal(window.results.text.length, 18); + done(); + }, + this.skip.bind(this) + ); + }); + }); + + describe("Beacon 14 (xhr) for fetch read with `json` (if Fetch API is supported)", function() { + var i = 13; + + it("Should contain correct url", function(done) { + if (!t.isFetchApiSupported()) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[i].u, "req=json"); + done(); + }, + this.skip.bind(this) + ); + }); + + it("Should have read the correct data", function(done) { + if (!t.isFetchApiSupported() || !window.JSON) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + assert.equal(window.results.json.data, 1); + done(); + }, + this.skip.bind(this) + ); + }); + }); + + describe("Beacon 15 (xhr) for fetch read with `blob` (if Fetch API is supported)", function() { + var i = 14; + + it("Should contain correct url", function(done) { + if (!t.isFetchApiSupported()) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[i].u, "req=blob"); + done(); + }, + this.skip.bind(this) + ); + }); + + it("Should have read the correct data", function(done) { + if (!t.isFetchApiSupported() || !window.JSON) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + assert.equal(window.results.blob.size, 18); + done(); + }, + this.skip.bind(this) + ); + }); + }); + + describe("Beacon 16 (xhr) for fetch read with `arrayBuffer` (if Fetch API is supported)", function() { + var i = 15; + + it("Should contain correct url", function(done) { + if (!t.isFetchApiSupported()) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[i].u, "req=arrayBuffer"); + done(); + }, + this.skip.bind(this) + ); + }); + + it("Should have read the correct data", function(done) { + if (!t.isFetchApiSupported() || !window.JSON) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + assert.equal(window.results.arrayBuffer.byteLength, 18); + done(); + }, + this.skip.bind(this) + ); + }); + }); + + describe("Beacon 17 (xhr) for fetch read with `formData` (if Fetch API is supported)", function() { + var i = 16; + + it("Should contain correct url", function(done) { + if (!t.isFetchApiSupported()) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[i].u, "req=formData"); + done(); + }, + this.skip.bind(this) + ); + }); + + it("Should have read the correct data", function(done) { + if (!t.isFetchApiSupported() || !window.JSON) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + assert.equal(window.results.formData.byteLength, 18); + done(); + }, + this.skip.bind(this) + ); + }); + }); +}); + diff --git a/tests/page-templates/07-autoxhr/43-payload-fetch.html b/tests/page-templates/07-autoxhr/43-payload-fetch.html new file mode 100644 index 000000000..3314f0ac1 --- /dev/null +++ b/tests/page-templates/07-autoxhr/43-payload-fetch.html @@ -0,0 +1,41 @@ +<%= header %> +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/07-autoxhr/43-payload-fetch.js b/tests/page-templates/07-autoxhr/43-payload-fetch.js new file mode 100644 index 000000000..ec7878744 --- /dev/null +++ b/tests/page-templates/07-autoxhr/43-payload-fetch.js @@ -0,0 +1,86 @@ +/* eslint-env mocha */ +/* global assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["t", "p", "xhrResponse1", "xhrRequestPayload1", "xhrResponse2", "xhrRequestPayload2"]); + +describe("e2e/07-autoxhr/43-payload-fetch", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should get 3 beacons: 1 onload, 2 xhr (XMLHttpRequest !== null and Fetch API is supported)", function(done) { + this.timeout(10000); + + if (!t.isFetchApiSupported()) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 3); + }); + }); + + it("Should have sent 1 beacon (if Fetch API is not supported)", function(done) { + this.timeout(10000); + + if (t.isFetchApiSupported()) { + return this.skip(); + } + + t.ensureBeaconCount(done, 1); + }); + + it("Should have not included the request payload data on the first Fetch event", function(done) { + if (!t.isFetchApiSupported()) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + assert.isUndefined(window.xhrRequestPayload1); + done(); + }); + }); + + it("Should have not included the response payload data on the first Fetch event", function(done) { + if (!t.isFetchApiSupported()) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + assert.isUndefined(window.xhrResponse1); + done(); + }); + }); + + it("Should have included the request payload data on the second Fetch event", function(done) { + if (!t.isFetchApiSupported()) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + assert.isDefined(window.xhrRequestPayload2); + done(); + }); + }); + + it("Should have included the response payload data on the second Fetch event", function(done) { + if (!t.isFetchApiSupported()) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + assert.isDefined(window.xhrResponse2); + done(); + }); + }); +}); diff --git a/tests/page-templates/07-autoxhr/44-fetch-polyfill.html b/tests/page-templates/07-autoxhr/44-fetch-polyfill.html new file mode 100644 index 000000000..fefed153b --- /dev/null +++ b/tests/page-templates/07-autoxhr/44-fetch-polyfill.html @@ -0,0 +1,55 @@ + +<%= header %> +<%= boomerangSnippet %> + + + + + + + + + +<%= footer %> diff --git a/tests/page-templates/07-autoxhr/44-fetch-polyfill.js b/tests/page-templates/07-autoxhr/44-fetch-polyfill.js new file mode 100644 index 000000000..779d652ba --- /dev/null +++ b/tests/page-templates/07-autoxhr/44-fetch-polyfill.js @@ -0,0 +1,82 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,describe,it,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["removeFetch", "i", "t", "afterFirstBeacon"]); + +describe("e2e/07-autoxhr/44-fetch-polyfill", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent 2 beacons (if Fetch API is supported)", function(done) { + if (!t.isFetchApiSupported()) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 2); + }, + this.skip.bind(this) + ); + }); + + it("Should have sent 1 beacon (if Fetch API is not supported)", function(done) { + if (t.isFetchApiSupported()) { + return this.skip(); + } + + t.ensureBeaconCount(done, 1); + }); + + describe("Beacon 1 (onload)", function() { + it("Should be an onload beacon", function() { + assert.include(tf.beacons[0].u, "44-fetch-polyfill.html"); + assert.equal(tf.beacons[0]["rt.start"], "navigation"); + }); + }); + + describe("Beacon 2 (xhr) for polyfilled fetch (if Fetch API is supported)", function() { + var i = 1; + + it("Should contain correct url", function(done) { + if (!t.isFetchApiSupported()) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[i].u, "support/script200.js"); + done(); + }, + this.skip.bind(this) + ); + }); + + it("Should not have http.type = f (if Fetch API is supported)", function() { + if (!t.isFetchApiSupported()) { + return this.skip(); + } + + assert.isUndefined(tf.beacons[i]["http.type"]); + }); + + it("Should not contain error status", function(done) { + if (!t.isFetchApiSupported()) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + assert.isUndefined(tf.beacons[i]["http.errno"]); + done(); + }, + this.skip.bind(this) + ); + }); + }); +}); + diff --git a/tests/page-templates/07-autoxhr/45-sync-xhr-no-error.html b/tests/page-templates/07-autoxhr/45-sync-xhr-no-error.html new file mode 100644 index 000000000..8f7ce325e --- /dev/null +++ b/tests/page-templates/07-autoxhr/45-sync-xhr-no-error.html @@ -0,0 +1,32 @@ +<%= header %> + +<%= boomerangScript %> + +<%= footer %> diff --git a/tests/page-templates/07-autoxhr/45-sync-xhr-no-error.js b/tests/page-templates/07-autoxhr/45-sync-xhr-no-error.js new file mode 100644 index 000000000..ab2d6c3a4 --- /dev/null +++ b/tests/page-templates/07-autoxhr/45-sync-xhr-no-error.js @@ -0,0 +1,71 @@ +/* eslint-env mocha */ +/* global assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["xhrErrorFired", "xhr"]); + +describe("e2e/07-autoxhr/45-sync-xhr-no-error", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should get 2 beacons: 1 onload, 1 xhr (XMLHttpRequest is supported)", function(done) { + this.timeout(10000); + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 2); + }, + this.skip.bind(this) + ); + }); + + describe("Beacon 1 (onload)", function() { + it("Should have correct url", function(done) { + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[0].u, "45-sync-xhr-no-error.html"); + done(); + }, + this.skip.bind(this) + ); + }); + }); + + describe("Beacon 2 (xhr) for 200 async XHR (XMLHttpRequest is supported)", function() { + var i = 1; + + it("Should have correct url", function(done) { + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[i].u, "/blackhole"); + done(); + }, + this.skip.bind(this) + ); + }); + + it("Should not contain status", function(done) { + t.ifAutoXHR( + done, + function() { + assert.isUndefined(tf.beacons[i]["http.errno"]); + done(); + }, + this.skip.bind(this) + ); + }); + + it("Should not have caused xhr_error to fire", function(done) { + t.ifAutoXHR( + done, + function() { + assert.isFalse(window.xhrErrorFired); + done(); + }, + this.skip.bind(this) + ); + }); + }); +}); diff --git a/tests/page-templates/07-autoxhr/46-iframes-reused.html b/tests/page-templates/07-autoxhr/46-iframes-reused.html new file mode 100644 index 000000000..1c002cd70 --- /dev/null +++ b/tests/page-templates/07-autoxhr/46-iframes-reused.html @@ -0,0 +1,49 @@ +<%= header %> + +<%= boomerangScript %> + +<%= footer %> diff --git a/tests/page-templates/07-autoxhr/46-iframes-reused.js b/tests/page-templates/07-autoxhr/46-iframes-reused.js new file mode 100644 index 000000000..bc902c9c7 --- /dev/null +++ b/tests/page-templates/07-autoxhr/46-iframes-reused.js @@ -0,0 +1,70 @@ +/* eslint-env mocha */ +/* global assert */ + +describe("e2e/07-autoxhr/46-iframes-reused", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should get 2 beacons: 1 onload, 1 xhr (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 2); + }, + this.skip.bind(this)); + }); + + it("Should get 1 beacons: 1 onload (XMLHttpRequest === null)", function(done) { + this.timeout(10000); + t.ifAutoXHR( + done, + this.skip.bind(this), + function() { + t.ensureBeaconCount(done, 1); + }); + }); + + describe("Beacon 2", function() { + var i = 1; + + it("Should have the second beacon be an XHR", function(done) { + t.ifAutoXHR( + done, + function() { + assert.equal(tf.beacons[i]["http.initiator"], "xhr"); + done(); + }, + this.skip.bind(this)); + }); + + it("Should have t_done not include the re-used iframe time (be greater than 1s but less than 10s) (if MutationObserver is supported)", function(done) { + if (!t.isMutationObserverSupported()) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + assert.operator(tf.beacons[i].t_done, ">=", 1000); + assert.operator(tf.beacons[i].t_done, "<", 10000); + done(); + }, + this.skip.bind(this)); + }); + + it("Should have t_done close to 500ms (if MutationObserver is not supported)", function(done) { + if (t.isMutationObserverSupported()) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + assert.closeTo(tf.beacons[i].t_done, 500, 200); + done(); + }, + this.skip.bind(this)); + }); + }); +}); diff --git a/tests/page-templates/07-autoxhr/47-pixels.html b/tests/page-templates/07-autoxhr/47-pixels.html new file mode 100644 index 000000000..740b5980d --- /dev/null +++ b/tests/page-templates/07-autoxhr/47-pixels.html @@ -0,0 +1,88 @@ +<%= header %> + +<%= boomerangScript %> + +<%= footer %> diff --git a/tests/page-templates/07-autoxhr/47-pixels.js b/tests/page-templates/07-autoxhr/47-pixels.js new file mode 100644 index 000000000..7ca186bc8 --- /dev/null +++ b/tests/page-templates/07-autoxhr/47-pixels.js @@ -0,0 +1,70 @@ +/* eslint-env mocha */ +/* global assert */ + +describe("e2e/07-autoxhr/47-pixels", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should get 2 beacons: 1 onload, 1 xhr (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 2); + }, + this.skip.bind(this)); + }); + + it("Should get 1 beacons: 1 onload (XMLHttpRequest === null)", function(done) { + this.timeout(10000); + t.ifAutoXHR( + done, + this.skip.bind(this), + function() { + t.ensureBeaconCount(done, 1); + }); + }); + + describe("Beacon 2 (xhr)", function() { + var i = 1; + + it("Should have the second beacon be an XHR", function(done) { + t.ifAutoXHR( + done, + function() { + assert.equal(tf.beacons[i]["http.initiator"], "xhr"); + done(); + }, + this.skip.bind(this)); + }); + + it("Should have t_done only include the visible frame and not pixels (be greater than 2s but less than 10s) (if MutationObserver is supported)", function(done) { + if (!t.isMutationObserverSupported()) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + assert.operator(tf.beacons[i].t_done, ">=", 2000); + assert.operator(tf.beacons[i].t_done, "<", 5000); + done(); + }, + this.skip.bind(this)); + }); + + it("Should have t_done close to 500ms (if MutationObserver is not supported)", function(done) { + if (t.isMutationObserverSupported()) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + assert.closeTo(tf.beacons[i].t_done, 500, 200); + done(); + }, + this.skip.bind(this)); + }); + }); +}); diff --git a/tests/page-templates/07-autoxhr/48-zonejs-mo.html b/tests/page-templates/07-autoxhr/48-zonejs-mo.html new file mode 100644 index 000000000..e6fec515c --- /dev/null +++ b/tests/page-templates/07-autoxhr/48-zonejs-mo.html @@ -0,0 +1,48 @@ +<%= header %> + + +<%= boomerangScript %> + +<%= footer %> diff --git a/tests/page-templates/07-autoxhr/48-zonejs-mo.js b/tests/page-templates/07-autoxhr/48-zonejs-mo.js new file mode 100644 index 000000000..fbe5152d2 --- /dev/null +++ b/tests/page-templates/07-autoxhr/48-zonejs-mo.js @@ -0,0 +1,85 @@ +/* eslint-env mocha */ +/* global assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, [ + "Zone", + /__zone_symbol/, + "img", + "xhr", + "beaconCount", + "zoneSpec", + "Zone1", + "ZONE_MO_USED" +]); + +describe("e2e/07-autoxhr/48-zonejs-mo", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should get 2 beacons: 1 onload, 1 xhr (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 2); + }, + this.skip.bind(this)); + }); + + it("Should get 1 beacons: 1 onload (XMLHttpRequest === null)", function(done) { + this.timeout(10000); + t.ifAutoXHR( + done, + this.skip.bind(this), + function() { + t.ensureBeaconCount(done, 1); + }); + }); + + it("Should not have invoked our MutationObserver callback in a zone.js zone", function() { + if (t.isMutationObserverSupported()) { + assert.isFalse(window.ZONE_MO_USED); + } + else { + this.skip(); + } + }); + + describe("Beacon 2", function() { + var i = 1; + + it("Should have a t_done of at least 1400ms based on the duration of the XHR and image being fetched (if MutationObserver is supported)", function(done) { + // 400ms (XHR) + 1000ms (IMG) + if (t.isMutationObserverSupported()) { + t.ifAutoXHR( + done, + function() { + assert.closeTo(tf.beacons[i].t_done, 1400, 300, "t_done was not close to 1400ms"); + done(); + }, + this.skip.bind(this) + ); + } + else { + this.skip(); + } + }); + + it("Should have a t_done of at least 400ms based on the duration of the XHR being fetched (if MutationObserver is not supported)", function(done) { + if (!t.isMutationObserverSupported()) { + t.ifAutoXHR( + done, + function() { + assert.closeTo(tf.beacons[i].t_done, 400, 50, "t_done is not close to 400ms"); + done(); + }, + this.skip.bind(this) + ); + } + else { + this.skip(); + } + }); + }); +}); diff --git a/tests/page-templates/07-autoxhr/49-no-script-tags.html b/tests/page-templates/07-autoxhr/49-no-script-tags.html new file mode 100644 index 000000000..c8a935ebc --- /dev/null +++ b/tests/page-templates/07-autoxhr/49-no-script-tags.html @@ -0,0 +1,37 @@ +<%= header %> + +<%= boomerangScript %> + +<%= footer %> diff --git a/tests/page-templates/07-autoxhr/49-no-script-tags.js b/tests/page-templates/07-autoxhr/49-no-script-tags.js new file mode 100644 index 000000000..c3430c2b1 --- /dev/null +++ b/tests/page-templates/07-autoxhr/49-no-script-tags.js @@ -0,0 +1,51 @@ +/* eslint-env mocha */ +/* global assert */ + +describe("e2e/07-autoxhr/49-no-script-tags", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should get 2 beacons: 1 onload, 1 xhr (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 2); + }, + this.skip.bind(this)); + }); + + it("Should get 1 beacons: 1 onload (XMLHttpRequest === null)", function(done) { + this.timeout(10000); + t.ifAutoXHR( + done, + this.skip.bind(this), + function() { + t.ensureBeaconCount(done, 1); + }); + }); + + describe("Beacon 2", function() { + var i = 1; + + it("Should have the second beacon be an XHR", function(done) { + t.ifAutoXHR( + done, + function() { + assert.equal(tf.beacons[i]["http.initiator"], "xhr"); + done(); + }, + this.skip.bind(this)); + }); + + it("Should have t_done only included the XHR and not include the SCRIPT tag", function(done) { + t.ifAutoXHR( + done, + function() { + assert.operator(tf.beacons[i].t_done, "<", 3000); + done(); + }, + this.skip.bind(this)); + }); + }); +}); diff --git a/tests/page-templates/07-autoxhr/50-link-tag.html b/tests/page-templates/07-autoxhr/50-link-tag.html new file mode 100644 index 000000000..4fcae038a --- /dev/null +++ b/tests/page-templates/07-autoxhr/50-link-tag.html @@ -0,0 +1,35 @@ +<%= header %> + +<%= boomerangScript %> + +<%= footer %> diff --git a/tests/page-templates/07-autoxhr/50-link-tag.js b/tests/page-templates/07-autoxhr/50-link-tag.js new file mode 100644 index 000000000..49d7c9265 --- /dev/null +++ b/tests/page-templates/07-autoxhr/50-link-tag.js @@ -0,0 +1,81 @@ +/* eslint-env mocha */ +/* global assert */ + +describe("e2e/07-autoxhr/50-link-tag", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should get 2 beacons: 1 onload, 1 xhr (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 2); + }, + this.skip.bind(this)); + }); + + it("Should get 1 beacons: 1 onload (XMLHttpRequest === null)", function(done) { + this.timeout(10000); + t.ifAutoXHR( + done, + this.skip.bind(this), + function() { + t.ensureBeaconCount(done, 1); + }); + }); + + describe("Beacon 2 (xhr)", function() { + var i = 1; + + it("Should have the second beacon be an XHR", function(done) { + t.ifAutoXHR( + done, + function() { + assert.equal(tf.beacons[i]["http.initiator"], "xhr"); + done(); + }, + this.skip.bind(this)); + }); + + it("Should have t_done include the LINK tag (if MutationObserver is supported)", function(done) { + if (!t.isMutationObserverSupported()) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + assert.operator(tf.beacons[i].t_done, ">=", 2000); + assert.closeTo(tf.beacons[i].t_done, 2000, 300); + done(); + }, + this.skip.bind(this)); + }); + + it("Should have t_page include the LINK tag (if MutationObserver is supported)", function(done) { + if (!t.isMutationObserverSupported()) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + assert.operator(tf.beacons[i].t_page, ">=", 2000); + assert.closeTo(tf.beacons[i].t_page, 2000, 300); + done(); + }, + this.skip.bind(this)); + }); + + it("Should have t_resp should be near 0s", function(done) { + t.ifAutoXHR( + done, + function() { + assert.closeTo(tf.beacons[i].t_resp, 0, 300); + done(); + }, + this.skip.bind(this)); + }); + }); +}); diff --git a/tests/page-templates/07-autoxhr/51-alwayssendxhr-merging.html b/tests/page-templates/07-autoxhr/51-alwayssendxhr-merging.html new file mode 100644 index 000000000..5e2b1854f --- /dev/null +++ b/tests/page-templates/07-autoxhr/51-alwayssendxhr-merging.html @@ -0,0 +1,39 @@ +<%= header %> + +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/07-autoxhr/51-alwayssendxhr-merging.js b/tests/page-templates/07-autoxhr/51-alwayssendxhr-merging.js new file mode 100644 index 000000000..5bcd65b87 --- /dev/null +++ b/tests/page-templates/07-autoxhr/51-alwayssendxhr-merging.js @@ -0,0 +1,121 @@ +/* eslint-env mocha */ +/* global assert */ + +describe("e2e/07-autoxhr/51-alwayssendxhr-merging", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + var pathName = window.location.pathname; + + it("Should get 3 beacons: 1 onload, 2 xhr (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 3); + }, + this.skip.bind(this)); + }); + + it("Should get 1 beacons: 1 onload (XMLHttpRequest === null)", function(done) { + this.timeout(10000); + t.ifAutoXHR( + done, + this.skip.bind(this), + function() { + t.ensureBeaconCount(done, 1); + }); + }); + + describe("Beacon 1 (page load)", function() { + var i = 0; + + it("Should be a page load", function(done) { + t.ifAutoXHR( + done, + function() { + assert.isUndefined(tf.beacons[i]["http.initiator"]); + done(); + }, + this.skip.bind(this)); + }); + + it("Should have u of " + pathName, function(done) { + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[i].u, pathName); + done(); + }, + this.skip.bind(this)); + }); + }); + + describe("Beacon 2 (real XHR)", function() { + var i = 1; + + it("Should have the second beacon be an XHR", function(done) { + t.ifAutoXHR( + done, + function() { + assert.equal(tf.beacons[i]["http.initiator"], "xhr"); + done(); + }, + this.skip.bind(this)); + }); + + it("Should have u of /assets/img.jpg", function(done) { + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[i].u, "img.jpg"); + done(); + }, + this.skip.bind(this)); + }); + + it("Should have pgu of " + pathName, function(done) { + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[i].pgu, pathName); + done(); + }, + this.skip.bind(this)); + }); + }); + + describe("Beacon 3 (fake XHR)", function() { + var i = 2; + + it("Should be an XHR", function(done) { + t.ifAutoXHR( + done, + function() { + assert.equal(tf.beacons[i]["http.initiator"], "xhr"); + done(); + }, + this.skip.bind(this)); + }); + + it("Should have u of http://bad.com", function(done) { + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[i].u, "http://bad.com"); + done(); + }, + this.skip.bind(this)); + }); + + it("Should have pgu of " + pathName, function(done) { + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[i].pgu, pathName); + done(); + }, + this.skip.bind(this)); + }); + }); +}); diff --git a/tests/page-templates/07-autoxhr/51-concurrent-events.html b/tests/page-templates/07-autoxhr/51-concurrent-events.html new file mode 100644 index 000000000..7818f9e85 --- /dev/null +++ b/tests/page-templates/07-autoxhr/51-concurrent-events.html @@ -0,0 +1,57 @@ +<%= header %> + +<%= boomerangScript %> + + + +<%= footer %> diff --git a/tests/page-templates/07-autoxhr/51-concurrent-events.js b/tests/page-templates/07-autoxhr/51-concurrent-events.js new file mode 100644 index 000000000..40f75f0ac --- /dev/null +++ b/tests/page-templates/07-autoxhr/51-concurrent-events.js @@ -0,0 +1,78 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["i"]); + +describe("e2e/07-autoxhr/51-concurrent-events", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent 3 beacons (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 3); + }, + this.skip.bind(this)); + }); + + describe("Beacon 2 (spa)", function() { + var i = 1; + + it("Should be a spa beacon", function(done) { + t.ifAutoXHR( + done, + function() { + assert.equal(tf.beacons[i]["http.initiator"], "spa"); + done(); + }, + this.skip.bind(this)); + }); + + it("Should have a t_done close to 400ms (if MutationObserver is supported)", function(done) { + if (!t.isMutationObserverSupported()) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + assert.operator(tf.beacons[i].t_done, ">=", 400); + assert.closeTo(tf.beacons[i].t_done, 400, 200); + done(); + }, + this.skip.bind(this)); + }); + }); + + describe("Beacon 3 (xhr)", function() { + var i = 2; + + it("Should be a xhr beacon", function(done) { + t.ifAutoXHR( + done, + function() { + assert.equal(tf.beacons[i]["http.initiator"], "xhr"); + done(); + }, + this.skip.bind(this)); + }); + + it("Should have a t_done close to 100ms (if MutationObserver is supported)", function(done) { + if (!t.isMutationObserverSupported()) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + assert.operator(tf.beacons[i].t_done, ">=", 100); + assert.closeTo(tf.beacons[i].t_done, 100, 200); + done(); + }, + this.skip.bind(this)); + }); + }); +}); diff --git a/tests/page-templates/07-autoxhr/52-alwayssendxhr.html b/tests/page-templates/07-autoxhr/52-alwayssendxhr.html new file mode 100644 index 000000000..92514f601 --- /dev/null +++ b/tests/page-templates/07-autoxhr/52-alwayssendxhr.html @@ -0,0 +1,46 @@ +<%= header %> + +<%= boomerangScript %> + +<%= footer %> diff --git a/tests/page-templates/07-autoxhr/52-alwayssendxhr.js b/tests/page-templates/07-autoxhr/52-alwayssendxhr.js new file mode 100644 index 000000000..43b46b6b7 --- /dev/null +++ b/tests/page-templates/07-autoxhr/52-alwayssendxhr.js @@ -0,0 +1,68 @@ +/* eslint-env mocha */ +/* global assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["xhr"]); + +describe("e2e/07-autoxhr/52-alwayssendxhr", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should get 3 beacons: 1 onload, 2 xhr (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 3); + }); + }); + + describe("Beacon 1 (onload)", function() { + var i = 0; + + it("Should be a page load beacon", function() { + var b = tf.beacons[i]; + + assert.isUndefined(b["http.initiator"]); + }); + + it("Should have the URL of the page", function() { + var b = tf.beacons[i]; + + assert.include(b.u, "52-alwayssendxhr.html"); + }); + }); + + describe("Beacon 1 (XHR due to alwaysSendXhr)", function() { + var i = 1; + + it("Should be a XHR beacon", function() { + var b = tf.beacons[i]; + + assert.equal(b["http.initiator"], "xhr"); + }); + + it("Should have the URL of ?always", function() { + var b = tf.beacons[i]; + + assert.include(b.u, "?always"); + }); + }); + + describe("Beacon 2 (XHR due to DOM mutation)", function() { + var i = 2; + + it("Should be a XHR beacon", function() { + var b = tf.beacons[i]; + + assert.equal(b["http.initiator"], "xhr"); + }); + + it("Should have the URL of ?not-always-with-dom", function() { + var b = tf.beacons[i]; + + assert.include(b.u, "?not-always-with-dom"); + }); + }); +}); diff --git a/tests/page-templates/07-autoxhr/52-autoxhr-restiming.html b/tests/page-templates/07-autoxhr/52-autoxhr-restiming.html new file mode 100644 index 000000000..a2e0c23ed --- /dev/null +++ b/tests/page-templates/07-autoxhr/52-autoxhr-restiming.html @@ -0,0 +1,33 @@ +<%= header %> +<%= boomerangScript %> + + + +<%= footer %> diff --git a/tests/page-templates/07-autoxhr/52-autoxhr-restiming.js b/tests/page-templates/07-autoxhr/52-autoxhr-restiming.js new file mode 100644 index 000000000..b82005b9e --- /dev/null +++ b/tests/page-templates/07-autoxhr/52-autoxhr-restiming.js @@ -0,0 +1,40 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["xhr"]); + +describe("e2e/07-autoxhr/52-autoxhr-restiming", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should get 2 beacons: 1 onload, 1 xhr (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 2); + }, + this.skip.bind(this)); + }); + + it("Should get 1 beacons: 1 onload (XMLHttpRequest === null)", function(done) { + this.timeout(10000); + + t.ifAutoXHR( + done, + this.skip.bind(this), + function() { + t.ensureBeaconCount(done, 1); + }); + }); + + it("Should have sent beacon 1 with restiming", function() { + assert.isDefined(tf.beacons[0].restiming); + }); + + it("Should have sent beacon 2 with restiming", function() { + assert.isDefined(tf.beacons[1].restiming); + }); +}); diff --git a/tests/page-templates/07-autoxhr/52-img-lazy.html b/tests/page-templates/07-autoxhr/52-img-lazy.html new file mode 100644 index 000000000..f36538d70 --- /dev/null +++ b/tests/page-templates/07-autoxhr/52-img-lazy.html @@ -0,0 +1,46 @@ +<%= header %> +<%= boomerangScript %> + + + +<%= footer %> diff --git a/tests/page-templates/07-autoxhr/52-img-lazy.js b/tests/page-templates/07-autoxhr/52-img-lazy.js new file mode 100644 index 000000000..bace00cbf --- /dev/null +++ b/tests/page-templates/07-autoxhr/52-img-lazy.js @@ -0,0 +1,46 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +describe("e2e/07-autoxhr/52-img-lazy", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent 2 beacons (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 2); + }, + this.skip.bind(this)); + }); + + describe("Beacon 2 (xhr)", function() { + var i = 1; + + it("Should be a xhr beacon", function(done) { + t.ifAutoXHR( + done, + function() { + assert.equal(tf.beacons[i]["http.initiator"], "xhr"); + done(); + }, + this.skip.bind(this)); + }); + + it("Should have a t_done close to 2000ms (if MutationObserver is supported)", function(done) { + if (!t.isMutationObserverSupported()) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + assert.operator(tf.beacons[i].t_done, ">=", 2000); + assert.closeTo(tf.beacons[i].t_done, 2000, 250); + done(); + }, + this.skip.bind(this)); + }); + }); +}); diff --git a/tests/page-templates/07-autoxhr/52-xhr-response-url.html b/tests/page-templates/07-autoxhr/52-xhr-response-url.html new file mode 100644 index 000000000..19ffe1598 --- /dev/null +++ b/tests/page-templates/07-autoxhr/52-xhr-response-url.html @@ -0,0 +1,59 @@ +<%= header %> + +<%= boomerangScript %> + +<%= footer %> diff --git a/tests/page-templates/07-autoxhr/52-xhr-response-url.js b/tests/page-templates/07-autoxhr/52-xhr-response-url.js new file mode 100644 index 000000000..ae72ed05e --- /dev/null +++ b/tests/page-templates/07-autoxhr/52-xhr-response-url.js @@ -0,0 +1,88 @@ +/* eslint-env mocha */ +/* global assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["t"]); + +describe("e2e/07-autoxhr/52-xhr-response-url", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should have sent beacons for each XHR/Fetch call", function(done) { + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, t.isFetchApiSupported() ? 5 : 3); + }, + this.skip.bind(this) + ); + }); + + it("For XHR, beacon parameter 'xhr.ru' should be present, since redirect happened and final url is not equal to the source url", function(done) { + t.ifAutoXHR( + done, + function() { + var beacon = tf.beacons[1]; + + assert.equal(beacon["http.initiator"], "xhr"); + assert.include(beacon.u, "/302?to=/blackhole/test"); + assert.include(beacon["xhr.ru"], "/blackhole/test"); + done(); + }, + this.skip.bind(this) + ); + }); + + it("For XHR, beacon parameter 'xhr.ru' should NOT be present, since there was no redirect", function(done) { + t.ifAutoXHR( + done, + function() { + var beacon = tf.beacons[2]; + + assert.equal(beacon["http.initiator"], "xhr"); + assert.include(beacon.u, "/blackhole"); + assert.isUndefined(beacon["xhr.ru"]); + done(); + }, + this.skip.bind(this) + ); + }); + + it("For Fetch, beacon parameter 'xhr.ru' should be present, since redirect happened", function(done) { + if (!t.isFetchApiSupported()) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + var beacon = tf.beacons[3]; + + assert.equal(beacon["http.initiator"], "xhr"); + assert.include(beacon.u, "/302?to=/blackhole/test"); + assert.include(beacon["xhr.ru"], "/blackhole/test"); + done(); + }, + this.skip.bind(this) + ); + }); + + it("For Fetch, beacon parameter 'xhr.ru' should NOT be present, since there was no redirect", function(done) { + if (!t.isFetchApiSupported()) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + var beacon = tf.beacons[4]; + + assert.equal(beacon["http.initiator"], "xhr"); + assert.include(beacon.u, "/blackhole"); + assert.isUndefined(beacon["xhr.ru"]); + done(); + }, + this.skip.bind(this) + ); + }); +}); diff --git a/tests/page-templates/07-autoxhr/53-iframe-opacity.html b/tests/page-templates/07-autoxhr/53-iframe-opacity.html new file mode 100644 index 000000000..5feeec374 --- /dev/null +++ b/tests/page-templates/07-autoxhr/53-iframe-opacity.html @@ -0,0 +1,42 @@ +<%= header %> +<%= boomerangScript %> + + + +<%= footer %> diff --git a/tests/page-templates/07-autoxhr/53-iframe-opacity.js b/tests/page-templates/07-autoxhr/53-iframe-opacity.js new file mode 100644 index 000000000..6aa3d6fba --- /dev/null +++ b/tests/page-templates/07-autoxhr/53-iframe-opacity.js @@ -0,0 +1,70 @@ +/* eslint-env mocha */ +/* global assert */ + +describe("e2e/07-autoxhr/53-iframe-opacity", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should get 2 beacons: 1 onload, 1 xhr (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 2); + }, + this.skip.bind(this)); + }); + + it("Should get 1 beacons: 1 onload (XMLHttpRequest === null)", function(done) { + this.timeout(10000); + t.ifAutoXHR( + done, + this.skip.bind(this), + function() { + t.ensureBeaconCount(done, 1); + }); + }); + + describe("Beacon 2", function() { + var i = 1; + + it("Should have the second beacon be an XHR", function(done) { + t.ifAutoXHR( + done, + function() { + assert.equal(tf.beacons[i]["http.initiator"], "xhr"); + done(); + }, + this.skip.bind(this)); + }); + + it("Should have t_done be just for the tracked IFRAME (if MutationObserver is supported)", function(done) { + if (!t.isMutationObserverSupported()) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + assert.operator(tf.beacons[i].t_done, ">=", 1000); + assert.operator(tf.beacons[i].t_done, "<", 50000); + done(); + }, + this.skip.bind(this)); + }); + + it("Should have t_done close to 500ms (if MutationObserver is not supported)", function(done) { + if (t.isMutationObserverSupported()) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + assert.closeTo(tf.beacons[i].t_done, 500, 200); + done(); + }, + this.skip.bind(this)); + }); + }); +}); diff --git a/tests/page-templates/07-autoxhr/54-iframe-height-0.html b/tests/page-templates/07-autoxhr/54-iframe-height-0.html new file mode 100644 index 000000000..f8f374ff0 --- /dev/null +++ b/tests/page-templates/07-autoxhr/54-iframe-height-0.html @@ -0,0 +1,43 @@ +<%= header %> +<%= boomerangScript %> + + + +<%= footer %> diff --git a/tests/page-templates/07-autoxhr/54-iframe-height-0.js b/tests/page-templates/07-autoxhr/54-iframe-height-0.js new file mode 100644 index 000000000..e2be73095 --- /dev/null +++ b/tests/page-templates/07-autoxhr/54-iframe-height-0.js @@ -0,0 +1,70 @@ +/* eslint-env mocha */ +/* global assert */ + +describe("e2e/07-autoxhr/54-iframe-height-0", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should get 2 beacons: 1 onload, 1 xhr (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 2); + }, + this.skip.bind(this)); + }); + + it("Should get 1 beacons: 1 onload (XMLHttpRequest === null)", function(done) { + this.timeout(10000); + t.ifAutoXHR( + done, + this.skip.bind(this), + function() { + t.ensureBeaconCount(done, 1); + }); + }); + + describe("Beacon 2", function() { + var i = 1; + + it("Should have the second beacon be an XHR", function(done) { + t.ifAutoXHR( + done, + function() { + assert.equal(tf.beacons[i]["http.initiator"], "xhr"); + done(); + }, + this.skip.bind(this)); + }); + + it("Should have t_done be just for the tracked IFRAME (if MutationObserver is supported)", function(done) { + if (!t.isMutationObserverSupported()) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + assert.operator(tf.beacons[i].t_done, ">=", 1000); + assert.operator(tf.beacons[i].t_done, "<", 50000); + done(); + }, + this.skip.bind(this)); + }); + + it("Should have t_done close to 500ms (if MutationObserver is not supported)", function(done) { + if (t.isMutationObserverSupported()) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + assert.closeTo(tf.beacons[i].t_done, 500, 200); + done(); + }, + this.skip.bind(this)); + }); + }); +}); diff --git a/tests/page-templates/07-autoxhr/55-dom-filter.html b/tests/page-templates/07-autoxhr/55-dom-filter.html new file mode 100644 index 000000000..760fe1a43 --- /dev/null +++ b/tests/page-templates/07-autoxhr/55-dom-filter.html @@ -0,0 +1,49 @@ +<%= header %> +<%= boomerangScript %> + + + +<%= footer %> diff --git a/tests/page-templates/07-autoxhr/55-dom-filter.js b/tests/page-templates/07-autoxhr/55-dom-filter.js new file mode 100644 index 000000000..0701ef68f --- /dev/null +++ b/tests/page-templates/07-autoxhr/55-dom-filter.js @@ -0,0 +1,70 @@ +/* eslint-env mocha */ +/* global assert */ + +describe("e2e/07-autoxhr/55-dom-filter", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should get 2 beacons: 1 onload, 1 xhr (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 2); + }, + this.skip.bind(this)); + }); + + it("Should get 1 beacons: 1 onload (XMLHttpRequest === null)", function(done) { + this.timeout(10000); + t.ifAutoXHR( + done, + this.skip.bind(this), + function() { + t.ensureBeaconCount(done, 1); + }); + }); + + describe("Beacon 2", function() { + var i = 1; + + it("Should have the second beacon be an XHR", function(done) { + t.ifAutoXHR( + done, + function() { + assert.equal(tf.beacons[i]["http.initiator"], "xhr"); + done(); + }, + this.skip.bind(this)); + }); + + it("Should have t_done be just for the tracked IFRAME (if MutationObserver is supported)", function(done) { + if (!t.isMutationObserverSupported()) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + assert.operator(tf.beacons[i].t_done, ">=", 1000); + assert.operator(tf.beacons[i].t_done, "<", 50000); + done(); + }, + this.skip.bind(this)); + }); + + it("Should have t_done close to 500ms (if MutationObserver is not supported)", function(done) { + if (t.isMutationObserverSupported()) { + return this.skip(); + } + + t.ifAutoXHR( + done, + function() { + assert.closeTo(tf.beacons[i].t_done, 500, 200); + done(); + }, + this.skip.bind(this)); + }); + }); +}); diff --git a/tests/page-templates/07-autoxhr/56-svg-animated-string.html b/tests/page-templates/07-autoxhr/56-svg-animated-string.html new file mode 100644 index 000000000..6463e74bb --- /dev/null +++ b/tests/page-templates/07-autoxhr/56-svg-animated-string.html @@ -0,0 +1,49 @@ +<%= header %> +<%= boomerangScript %> + + + + + + + + + + + +<%= footer %> diff --git a/tests/page-templates/07-autoxhr/56-svg-animated-string.js b/tests/page-templates/07-autoxhr/56-svg-animated-string.js new file mode 100644 index 000000000..8a575dc5a --- /dev/null +++ b/tests/page-templates/07-autoxhr/56-svg-animated-string.js @@ -0,0 +1,24 @@ +/* eslint-env mocha */ +/* global assert */ + +describe("e2e/07-autoxhr/56-svg-animated-string", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should get 1 beacon", function(done) { + this.timeout(10000); + + t.ensureBeaconCount(done, 1); + }); + + it("Should not have fired an error", function(done) { + t.ifAutoXHR( + done, + function() { + assert.isUndefined(window.onerrorFired); + + done(); + }, + this.skip.bind(this)); + }); +}); diff --git a/tests/page-templates/07-autoxhr/57-a-href-change.html b/tests/page-templates/07-autoxhr/57-a-href-change.html new file mode 100644 index 000000000..ecb0aa0d8 --- /dev/null +++ b/tests/page-templates/07-autoxhr/57-a-href-change.html @@ -0,0 +1,80 @@ +<%= header %> + +<%= boomerangScript %> + +
+ +
+
+ + +<%= footer %> diff --git a/tests/page-templates/07-autoxhr/57-a-href-change.js b/tests/page-templates/07-autoxhr/57-a-href-change.js new file mode 100644 index 000000000..78c844b4e --- /dev/null +++ b/tests/page-templates/07-autoxhr/57-a-href-change.js @@ -0,0 +1,40 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["listenersAdded", "listenersRemoved", "countListeners", "beaconNum"]); + +describe("e2e/07-autoxhr/57-a-href-change", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent 2 beacons (if MutationObserver is supported)", function(done) { + if (t.isMutationObserverSupported()) { + this.timeout(10000); + t.ensureBeaconCount(done, 2); + } + else { + this.skip(); + } + }); + + it("Should have sent 1 beacon (if MutationObserver is not supported)", function(done) { + if (!t.isMutationObserverSupported()) { + this.timeout(10000); + t.ensureBeaconCount(done, 1); + } + else { + this.skip(); + } + }); + + it("Should have removed event listeners from main-image (if MutationObserver is supported)", function() { + if (t.isMutationObserverSupported()) { + assert.equal(window.listenersAdded, 2); // 1 load + 1 error listeners + assert.equal(window.listenersRemoved, 2); + } + else { + this.skip(); + } + }); +}); diff --git a/tests/page-templates/07-autoxhr/58-element-nodes.html b/tests/page-templates/07-autoxhr/58-element-nodes.html new file mode 100644 index 000000000..c9a1c1b63 --- /dev/null +++ b/tests/page-templates/07-autoxhr/58-element-nodes.html @@ -0,0 +1,58 @@ +<%= header %> + +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/07-autoxhr/58-element-nodes.js b/tests/page-templates/07-autoxhr/58-element-nodes.js new file mode 100644 index 000000000..02c7a2feb --- /dev/null +++ b/tests/page-templates/07-autoxhr/58-element-nodes.js @@ -0,0 +1,25 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["listenersAdded", "listenersRemoved", "countListeners", "appendElementNode"]); + +describe("e2e/07-autoxhr/58-element-nodes", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent 1 beacon", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 1); + }); + + it("Should have removed event listeners from images (if MutationObserver is supported)", function() { + if (t.isMutationObserverSupported()) { + assert.equal(window.listenersAdded, 6); // 3 load + 3 error listeners + assert.equal(window.listenersRemoved, 6); + } + else { + this.skip(); + } + }); +}); diff --git a/tests/page-templates/07-autoxhr/support/30-ie1011-bug.xml b/tests/page-templates/07-autoxhr/support/30-ie1011-bug.xml new file mode 100644 index 000000000..f1beb8157 --- /dev/null +++ b/tests/page-templates/07-autoxhr/support/30-ie1011-bug.xml @@ -0,0 +1,3 @@ + + + diff --git a/tests/page-templates/07-autoxhr/support/data.json b/tests/page-templates/07-autoxhr/support/data.json new file mode 100644 index 000000000..dd1b16135 --- /dev/null +++ b/tests/page-templates/07-autoxhr/support/data.json @@ -0,0 +1,3 @@ +{ + "data": 1 +} diff --git a/tests/page-templates/07-autoxhr/support/data.xml b/tests/page-templates/07-autoxhr/support/data.xml new file mode 100644 index 000000000..375763602 --- /dev/null +++ b/tests/page-templates/07-autoxhr/support/data.xml @@ -0,0 +1 @@ +1 diff --git a/tests/page-templates/07-autoxhr/support/img.jpg b/tests/page-templates/07-autoxhr/support/img.jpg new file mode 100644 index 000000000..de3a1f7e9 Binary files /dev/null and b/tests/page-templates/07-autoxhr/support/img.jpg differ diff --git a/tests/page-templates/07-autoxhr/support/script200.js b/tests/page-templates/07-autoxhr/support/script200.js new file mode 100644 index 000000000..e69de29bb diff --git a/tests/page-templates/07-autoxhr/tests.json5 b/tests/page-templates/07-autoxhr/tests.json5 new file mode 100644 index 000000000..ea16e30bd --- /dev/null +++ b/tests/page-templates/07-autoxhr/tests.json5 @@ -0,0 +1,5 @@ +{ + all: { + requires: ["auto-xhr"] + } +} diff --git a/tests/page-templates/08-ember/00-simple.html b/tests/page-templates/08-ember/00-simple.html new file mode 100644 index 000000000..c5c0872b5 --- /dev/null +++ b/tests/page-templates/08-ember/00-simple.html @@ -0,0 +1,35 @@ +<%= header %> +<%= boomerangScript %> + + + + + + + + + + + + + + + + +<%= footer %> diff --git a/tests/page-templates/08-ember/00-simple.js b/tests/page-templates/08-ember/00-simple.js new file mode 100644 index 000000000..784e02077 --- /dev/null +++ b/tests/page-templates/08-ember/00-simple.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["$", "jQuery", "Ember", "Em", "Handlebars", "App", "imgs", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2"]); + +describe("e2e/08-ember/00-simple", function() { + BOOMR_test.templates.SPA["00-simple"](); +}); diff --git a/tests/page-templates/08-ember/04-route-change.html b/tests/page-templates/08-ember/04-route-change.html new file mode 100644 index 000000000..f43f0ac53 --- /dev/null +++ b/tests/page-templates/08-ember/04-route-change.html @@ -0,0 +1,38 @@ +<%= header %> +<%= boomerangScript %> + + + + + + + + + + + + + + + + + +<%= footer %> diff --git a/tests/page-templates/08-ember/04-route-change.js b/tests/page-templates/08-ember/04-route-change.js new file mode 100644 index 000000000..1ae7aa852 --- /dev/null +++ b/tests/page-templates/08-ember/04-route-change.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["$", "jQuery", "Ember", "Em", "Handlebars", "ember_nav_routes", "html5_mode", "imgs", "App", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2", "i"]); + +describe("e2e/08-ember/04-route-change", function() { + BOOMR_test.templates.SPA["04-route-change"](); +}); diff --git a/tests/page-templates/08-ember/05-route-change-hashtags.html b/tests/page-templates/08-ember/05-route-change-hashtags.html new file mode 100644 index 000000000..1d5a478fd --- /dev/null +++ b/tests/page-templates/08-ember/05-route-change-hashtags.html @@ -0,0 +1,38 @@ +<%= header %> +<%= boomerangScript %> + + + + + + + + + + + + + + + + +<%= footer %> diff --git a/tests/page-templates/08-ember/05-route-change-hashtags.js b/tests/page-templates/08-ember/05-route-change-hashtags.js new file mode 100644 index 000000000..0fa33abac --- /dev/null +++ b/tests/page-templates/08-ember/05-route-change-hashtags.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["$", "jQuery", "Ember", "Em", "Handlebars", "ember_nav_routes", "html5_mode", "imgs", "App", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2", "i"]); + +describe("e2e/08-ember/05-route-change-hashtags", function() { + BOOMR_test.templates.SPA["05-route-change-hashtags"](); +}); diff --git a/tests/page-templates/08-ember/06-hard-nav-resources.html b/tests/page-templates/08-ember/06-hard-nav-resources.html new file mode 100644 index 000000000..846c9c48a --- /dev/null +++ b/tests/page-templates/08-ember/06-hard-nav-resources.html @@ -0,0 +1,38 @@ +<%= header %> +<%= boomerangScript %> + + + + + + + + + + + + + + + + + +<%= footer %> diff --git a/tests/page-templates/08-ember/06-hard-nav-resources.js b/tests/page-templates/08-ember/06-hard-nav-resources.js new file mode 100644 index 000000000..351a24d64 --- /dev/null +++ b/tests/page-templates/08-ember/06-hard-nav-resources.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["imgs", "$", "jQuery", "Ember", "Em", "Handlebars", "App", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2"]); + +describe("e2e/08-ember/06-hard-nav-resources", function() { + BOOMR_test.templates.SPA["06-hard-nav-resources"](); +}); diff --git a/tests/page-templates/08-ember/07-soft-nav-resources.html b/tests/page-templates/08-ember/07-soft-nav-resources.html new file mode 100644 index 000000000..065fc1e85 --- /dev/null +++ b/tests/page-templates/08-ember/07-soft-nav-resources.html @@ -0,0 +1,46 @@ +<%= header %> +<%= boomerangScript %> + + + + + + + + + + + + + + + + + +<%= footer %> diff --git a/tests/page-templates/08-ember/07-soft-nav-resources.js b/tests/page-templates/08-ember/07-soft-nav-resources.js new file mode 100644 index 000000000..f5b1d524e --- /dev/null +++ b/tests/page-templates/08-ember/07-soft-nav-resources.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["ember_nav_routes", "html5_mode", "imgs", "$", "jQuery", "Ember", "Em", "Handlebars", "App", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2", "i"]); + +describe("e2e/08-ember/07-soft-nav-resources", function() { + BOOMR_test.templates.SPA["07-soft-nav-resources"](); +}); diff --git a/tests/page-templates/08-ember/08-no-images.html b/tests/page-templates/08-ember/08-no-images.html new file mode 100644 index 000000000..d445f62d6 --- /dev/null +++ b/tests/page-templates/08-ember/08-no-images.html @@ -0,0 +1,30 @@ +<%= header %> +<%= boomerangScript %> + + + + + + + + + + + + + diff --git a/tests/page-templates/08-ember/08-no-images.js b/tests/page-templates/08-ember/08-no-images.js new file mode 100644 index 000000000..70cfabaa9 --- /dev/null +++ b/tests/page-templates/08-ember/08-no-images.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["$", "jQuery", "Ember", "Em", "Handlebars", "App", "imgs", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2"]); + +describe("e2e/08-ember/08-no-images", function() { + BOOMR_test.templates.SPA["08-no-images"](); +}); diff --git a/tests/page-templates/08-ember/09-autoxhr-after-load.html b/tests/page-templates/08-ember/09-autoxhr-after-load.html new file mode 100644 index 000000000..2cf2b3574 --- /dev/null +++ b/tests/page-templates/08-ember/09-autoxhr-after-load.html @@ -0,0 +1,59 @@ +<%= header %> +<%= boomerangScript %> + + + + + + + + + + + + + +<%= footer %> diff --git a/tests/page-templates/08-ember/09-autoxhr-after-load.js b/tests/page-templates/08-ember/09-autoxhr-after-load.js new file mode 100644 index 000000000..95c2530d7 --- /dev/null +++ b/tests/page-templates/08-ember/09-autoxhr-after-load.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["$", "jQuery", "Ember", "Em", "Handlebars", "App", "xhr", "beaconNum", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2"]); + +describe("e2e/08-ember/09-autoxhr-after-load", function() { + BOOMR_test.templates.SPA["09-autoxhr-after-load"](); +}); diff --git a/tests/page-templates/08-ember/10-autoxhr-overlapping.html b/tests/page-templates/08-ember/10-autoxhr-overlapping.html new file mode 100644 index 000000000..696aa23b0 --- /dev/null +++ b/tests/page-templates/08-ember/10-autoxhr-overlapping.html @@ -0,0 +1,55 @@ +<%= header %> +<%= boomerangScript %> + + + + + + + + + + + + + +<%= footer %> diff --git a/tests/page-templates/08-ember/10-autoxhr-overlapping.js b/tests/page-templates/08-ember/10-autoxhr-overlapping.js new file mode 100644 index 000000000..564940c83 --- /dev/null +++ b/tests/page-templates/08-ember/10-autoxhr-overlapping.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["$", "jQuery", "Ember", "Em", "Handlebars", "App", "imgs", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2"]); + +describe("e2e/08-ember/10-autoxhr-overlapping", function() { + BOOMR_test.templates.SPA["10-autoxhr-overlapping"](); +}); diff --git a/tests/page-templates/08-ember/11-autoxhr-trigger-additional.html b/tests/page-templates/08-ember/11-autoxhr-trigger-additional.html new file mode 100644 index 000000000..56bd45778 --- /dev/null +++ b/tests/page-templates/08-ember/11-autoxhr-trigger-additional.html @@ -0,0 +1,57 @@ +<%= header %> +<%= boomerangScript %> + + + + + + + + + + + + +<%= footer %> diff --git a/tests/page-templates/08-ember/11-autoxhr-trigger-additional.js b/tests/page-templates/08-ember/11-autoxhr-trigger-additional.js new file mode 100644 index 000000000..6e0d421bd --- /dev/null +++ b/tests/page-templates/08-ember/11-autoxhr-trigger-additional.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["$", "jQuery", "Ember", "Em", "Handlebars", "App", "imgs", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2"]); + +describe("e2e/08-ember/11-trigger-additional", function() { + BOOMR_test.templates.SPA["11-autoxhr-trigger-additional"](); +}); diff --git a/tests/page-templates/08-ember/12-autoxhr-trigger-additional-after-delay.html b/tests/page-templates/08-ember/12-autoxhr-trigger-additional-after-delay.html new file mode 100644 index 000000000..d599267ac --- /dev/null +++ b/tests/page-templates/08-ember/12-autoxhr-trigger-additional-after-delay.html @@ -0,0 +1,58 @@ +<%= header %> +<%= boomerangScript %> + + + + + + + + + + + + +<%= footer %> diff --git a/tests/page-templates/08-ember/12-autoxhr-trigger-additional-after-delay.js b/tests/page-templates/08-ember/12-autoxhr-trigger-additional-after-delay.js new file mode 100644 index 000000000..cdcac7f28 --- /dev/null +++ b/tests/page-templates/08-ember/12-autoxhr-trigger-additional-after-delay.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["$", "jQuery", "Ember", "Em", "Handlebars", "App", "imgs", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2"]); + +describe("e2e/08-ember/12-autoxhr-trigger-additional-after-delay", function() { + BOOMR_test.templates.SPA["12-autoxhr-trigger-additional-after-delay"](); +}); diff --git a/tests/page-templates/08-ember/13-autoxhr-disabled.html b/tests/page-templates/08-ember/13-autoxhr-disabled.html new file mode 100644 index 000000000..8774c1d94 --- /dev/null +++ b/tests/page-templates/08-ember/13-autoxhr-disabled.html @@ -0,0 +1,59 @@ +<%= header %> +<%= boomerangScript %> + + + + + + + + + + + + + + +<%= footer %> diff --git a/tests/page-templates/08-ember/13-autoxhr-disabled.js b/tests/page-templates/08-ember/13-autoxhr-disabled.js new file mode 100644 index 000000000..2cf66e506 --- /dev/null +++ b/tests/page-templates/08-ember/13-autoxhr-disabled.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["$", "jQuery", "Ember", "Em", "Handlebars", "App", "imgs", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2"]); + +describe("e2e/08-ember/13-autoxhr-disabled", function() { + BOOMR_test.templates.SPA["13-autoxhr-disabled"](); +}); diff --git a/tests/page-templates/08-ember/14-autoxhr-before-page-load.html b/tests/page-templates/08-ember/14-autoxhr-before-page-load.html new file mode 100644 index 000000000..fe44007e4 --- /dev/null +++ b/tests/page-templates/08-ember/14-autoxhr-before-page-load.html @@ -0,0 +1,37 @@ +<%= header %> +<%= boomerangScript %> + + + + + + + + + + + + +<%= footer %> diff --git a/tests/page-templates/08-ember/14-autoxhr-before-page-load.js b/tests/page-templates/08-ember/14-autoxhr-before-page-load.js new file mode 100644 index 000000000..0825da0b7 --- /dev/null +++ b/tests/page-templates/08-ember/14-autoxhr-before-page-load.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["$", "jQuery", "Ember", "Em", "Handlebars", "App", "xhr", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2"]); + +describe("e2e/08-ember/14-autoxhr-before-page-load", function() { + BOOMR_test.templates.SPA["14-autoxhr-before-page-load"](); +}); diff --git a/tests/page-templates/08-ember/15-delayed-boomerang.html b/tests/page-templates/08-ember/15-delayed-boomerang.html new file mode 100644 index 000000000..1d80c3eb3 --- /dev/null +++ b/tests/page-templates/08-ember/15-delayed-boomerang.html @@ -0,0 +1,32 @@ +<%= header %> + + +<%= boomerangDelayedSnippet %> + + + + + + + + + + +<%= footer %> diff --git a/tests/page-templates/08-ember/15-delayed-boomerang.js b/tests/page-templates/08-ember/15-delayed-boomerang.js new file mode 100644 index 000000000..167aea32c --- /dev/null +++ b/tests/page-templates/08-ember/15-delayed-boomerang.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["BOOMR_script_delay", "$", "jQuery", "Ember", "Em", "Handlebars", "App", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2"]); + +describe("e2e/08-ember/15-delayed-boomerang", function() { + BOOMR_test.templates.SPA["15-delayed-boomerang"](); +}); diff --git a/tests/page-templates/08-ember/17-wait.html b/tests/page-templates/08-ember/17-wait.html new file mode 100644 index 000000000..a05f1be34 --- /dev/null +++ b/tests/page-templates/08-ember/17-wait.html @@ -0,0 +1,80 @@ +<%= header %> +<%= boomerangScript %> + + + + + + + + + + + + + + + + +<%= footer %> diff --git a/tests/page-templates/08-ember/17-wait.js b/tests/page-templates/08-ember/17-wait.js new file mode 100644 index 000000000..729e489af --- /dev/null +++ b/tests/page-templates/08-ember/17-wait.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["$", "jQuery", "Ember", "Em", "Handlebars", "routeWaits", "routeNumber", "imgs", "html5_mode", "ember_nav_routes", "spaWaitCompleteTimes", "ember_route_wait", "App", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2", "i"]); + +describe("e2e/08-ember/17-wait", function() { + BOOMR_test.templates.SPA["17-wait"](); +}); diff --git a/tests/page-templates/08-ember/18-autoxhr-before-page-load-alwayssendxhr.html b/tests/page-templates/08-ember/18-autoxhr-before-page-load-alwayssendxhr.html new file mode 100644 index 000000000..a08dcab3f --- /dev/null +++ b/tests/page-templates/08-ember/18-autoxhr-before-page-load-alwayssendxhr.html @@ -0,0 +1,35 @@ +<%= header %> +<%= boomerangScript %> + + + + + + + + + + + + +<%= footer %> diff --git a/tests/page-templates/08-ember/18-autoxhr-before-page-load-alwayssendxhr.js b/tests/page-templates/08-ember/18-autoxhr-before-page-load-alwayssendxhr.js new file mode 100644 index 000000000..d35d7e3e5 --- /dev/null +++ b/tests/page-templates/08-ember/18-autoxhr-before-page-load-alwayssendxhr.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["$", "jQuery", "Ember", "Em", "Handlebars", "App", "imgs", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2"]); + +describe("e2e/08-ember/18-autoxhr-before-page-load-alwayssendxhr", function() { + BOOMR_test.templates.SPA["18-autoxhr-before-page-load-alwayssendxhr"](); +}); diff --git a/tests/page-templates/08-ember/19-autoxhr-during-nav-alwayssendxhr.html b/tests/page-templates/08-ember/19-autoxhr-during-nav-alwayssendxhr.html new file mode 100644 index 000000000..7959310ce --- /dev/null +++ b/tests/page-templates/08-ember/19-autoxhr-during-nav-alwayssendxhr.html @@ -0,0 +1,38 @@ +<%= header %> +<%= boomerangScript %> + + + + + + + + + + + + + +<%= footer %> diff --git a/tests/page-templates/08-ember/19-autoxhr-during-nav-alwayssendxhr.js b/tests/page-templates/08-ember/19-autoxhr-during-nav-alwayssendxhr.js new file mode 100644 index 000000000..8b8294e5b --- /dev/null +++ b/tests/page-templates/08-ember/19-autoxhr-during-nav-alwayssendxhr.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["$", "jQuery", "Ember", "Em", "Handlebars", "imgs", "ember_nav_routes", "App", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2", "i"]); + +describe("e2e/08-ember/19-autoxhr-during-nav-alwayssendxhr", function() { + BOOMR_test.templates.SPA["19-autoxhr-during-nav-alwayssendxhr"](); +}); diff --git a/tests/page-templates/08-ember/20-no-resources.html b/tests/page-templates/08-ember/20-no-resources.html new file mode 100644 index 000000000..d8f66536c --- /dev/null +++ b/tests/page-templates/08-ember/20-no-resources.html @@ -0,0 +1,38 @@ +<%= header %> +<%= boomerangScript %> + + + + + + + + + + + + + + + + + +<%= footer %> diff --git a/tests/page-templates/08-ember/20-no-resources.js b/tests/page-templates/08-ember/20-no-resources.js new file mode 100644 index 000000000..df261ddfd --- /dev/null +++ b/tests/page-templates/08-ember/20-no-resources.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["$", "jQuery", "Ember", "Em", "Handlebars", "ember_nav_routes", "html5_mode", "imgs", "App", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2", "i"]); + +describe("e2e/08-ember/20-no-resources", function() { + BOOMR_test.templates.SPA["20-no-resources"](); +}); diff --git a/tests/page-templates/08-ember/21-constant-mutations.html b/tests/page-templates/08-ember/21-constant-mutations.html new file mode 100644 index 000000000..dab4d2934 --- /dev/null +++ b/tests/page-templates/08-ember/21-constant-mutations.html @@ -0,0 +1,37 @@ +<%= header %> +<%= boomerangScript %> + + + + + + + + + + + + + + +<%= footer %> diff --git a/tests/page-templates/08-ember/21-constant-mutations.js b/tests/page-templates/08-ember/21-constant-mutations.js new file mode 100644 index 000000000..e6575b789 --- /dev/null +++ b/tests/page-templates/08-ember/21-constant-mutations.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["$", "jQuery", "Ember", "Em", "Handlebars", "App", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2"]); + +describe("e2e/08-ember/21-constant-mutations", function() { + BOOMR_test.templates.SPA["21-constant-mutations"](); +}); diff --git a/tests/page-templates/08-ember/23-hard-wait-for-onload.html b/tests/page-templates/08-ember/23-hard-wait-for-onload.html new file mode 100644 index 000000000..f000cb5ca --- /dev/null +++ b/tests/page-templates/08-ember/23-hard-wait-for-onload.html @@ -0,0 +1,36 @@ +<%= header %> +<%= boomerangScript %> + + + + + + + + + + + + + + + + + +<%= footer %> diff --git a/tests/page-templates/08-ember/23-hard-wait-for-onload.js b/tests/page-templates/08-ember/23-hard-wait-for-onload.js new file mode 100644 index 000000000..78150cf32 --- /dev/null +++ b/tests/page-templates/08-ember/23-hard-wait-for-onload.js @@ -0,0 +1,8 @@ +/* eslint-env mocha */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["$", "jQuery", "Ember", "Em", "Handlebars", "App", "imgs", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2"]); + +describe("e2e/08-ember/23-hard-wait-for-onload", function() { + BOOMR_test.templates.SPA["23-hard-wait-for-onload"](); +}); diff --git a/tests/page-templates/08-ember/24-route-filter.html b/tests/page-templates/08-ember/24-route-filter.html new file mode 100644 index 000000000..ba4507cf4 --- /dev/null +++ b/tests/page-templates/08-ember/24-route-filter.html @@ -0,0 +1,50 @@ +<%= header %> + +<%= boomerangScript %> + + + + + + + + + + + + + + + + + +<%= footer %> diff --git a/tests/page-templates/08-ember/24-route-filter.js b/tests/page-templates/08-ember/24-route-filter.js new file mode 100644 index 000000000..da9de76e5 --- /dev/null +++ b/tests/page-templates/08-ember/24-route-filter.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["$", "jQuery", "Ember", "Em", "Handlebars", "navs", "html5_mode", "imgs", "ember_nav_routes", "ember_route_filter", "App", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2", "i"]); + +describe("e2e/08-ember/24-route-filter", function() { + BOOMR_test.templates.SPA["24-route-filter"](); +}); diff --git a/tests/page-templates/08-ember/25-delayed-boomerang-pre-config-snippet.html b/tests/page-templates/08-ember/25-delayed-boomerang-pre-config-snippet.html new file mode 100644 index 000000000..9bc5b91d2 --- /dev/null +++ b/tests/page-templates/08-ember/25-delayed-boomerang-pre-config-snippet.html @@ -0,0 +1,47 @@ +<%= header %> +<%= boomerangDelayedSnippet %> + + + + + + + + + + +<%= footer %> diff --git a/tests/page-templates/08-ember/25-delayed-boomerang-pre-config-snippet.js b/tests/page-templates/08-ember/25-delayed-boomerang-pre-config-snippet.js new file mode 100644 index 000000000..c6f3851db --- /dev/null +++ b/tests/page-templates/08-ember/25-delayed-boomerang-pre-config-snippet.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["BOOMR_script_delay", "$", "jQuery", "Ember", "Em", "Handlebars", "timestamp", "App", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2"]); + +describe("e2e/08-ember/25-delayed-boomerang-pre-config-snippet", function() { + BOOMR_test.templates.SPA["25-delayed-boomerang-pre-config-snippet"](); +}); diff --git a/tests/page-templates/08-ember/27-route-change-back.html b/tests/page-templates/08-ember/27-route-change-back.html new file mode 100644 index 000000000..834d56bcd --- /dev/null +++ b/tests/page-templates/08-ember/27-route-change-back.html @@ -0,0 +1,55 @@ +<%= header %> +<%= boomerangScript %> + + + + + + + + + + + + + + + + + +<%= footer %> diff --git a/tests/page-templates/08-ember/27-route-change-back.js b/tests/page-templates/08-ember/27-route-change-back.js new file mode 100644 index 000000000..42ee834d9 --- /dev/null +++ b/tests/page-templates/08-ember/27-route-change-back.js @@ -0,0 +1,10 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert,angular */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["$", "jQuery", "Ember", "Em", "Handlebars", "ember_nav_routes", "html5_mode", "imgs", "App", "beaconNum", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2", "i"]); + +describe("e2e/08-ember/27-route-change-back", function() { + // use tests from #4 + BOOMR_test.templates.SPA["04-route-change"](); +}); diff --git a/tests/page-templates/08-ember/29-route-change-early-beacon.html b/tests/page-templates/08-ember/29-route-change-early-beacon.html new file mode 100644 index 000000000..92ef4ae83 --- /dev/null +++ b/tests/page-templates/08-ember/29-route-change-early-beacon.html @@ -0,0 +1,146 @@ +<%= header %> +<%= boomerangScript %> + + + + + + + + + + + + + + + +<%= footer %> diff --git a/tests/page-templates/08-ember/29-route-change-early-beacon.js b/tests/page-templates/08-ember/29-route-change-early-beacon.js new file mode 100644 index 000000000..1e68ad296 --- /dev/null +++ b/tests/page-templates/08-ember/29-route-change-early-beacon.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["$", "jQuery", "Ember", "Em", "Handlebars", "ember_nav_routes", "html5_mode", "imgs", "App", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2", "i"]); + +describe("e2e/08-ember/29-route-change-early-beacon", function() { + BOOMR_test.templates.SPA["29-route-change-early-beacon"](); +}); diff --git a/tests/page-templates/08-ember/support/app.js b/tests/page-templates/08-ember/support/app.js new file mode 100644 index 000000000..ae9ccc468 --- /dev/null +++ b/tests/page-templates/08-ember/support/app.js @@ -0,0 +1,203 @@ +/* global Ember,App */ +window.App = Ember.Application.create({ + LOG_TRANSITIONS: true, + LOG_TRANSITIONS_INTERNAL: true +}); + +Ember.Handlebars.helper("random", function() { + return new Ember.Handlebars.SafeString(Math.floor(Math.random() * 1000 * 1000) + ""); +}); +App.ApplicationRoute = Ember.Route.extend({ + init: function() { + var router = this; + + this._super.apply(arguments); + Ember.run.scheduleOnce("afterRender", function() { + if (typeof window.ember_nav_routes !== "undefined" && BOOMR.utils.isArray(window.ember_nav_routes)) { + BOOMR.subscribe("onbeacon", function(beacon) { + // only continue for non-early SPA beacons + if (!BOOMR.utils.inArray(beacon["http.initiator"], BOOMR.constants.BEACON_TYPE_SPAS) || + typeof beacon.early !== "undefined") { + return; + } + + if (window.ember_nav_routes.length > 0) { + var nextRoute = window.ember_nav_routes.shift(); + + setTimeout(function() { + router.transitionTo(nextRoute); + }, 100); + } + }); + } + }); + }, + actions: { + willTransition: function(transition) { + // Ember 1.x issues History API calls after the transition is complete (after XHRs and mutations). + // To work around this, we set auto:false and manually issue route changes + // for spa soft routes + BOOMR.plugins.SPA.route_change(); + } + } +}); + +App.WidgetsRoute = Ember.Route.extend({ + beforeModel: function() { + return Ember.$.get("support/widgets.html?rnd=" + Math.random()).then(function(data) { + Ember.TEMPLATES.widgets = Ember.Handlebars.compile(data); + }); + }, + model: function() { + return Ember.$.getJSON("support/widgets.json?rnd=" + Math.random()); + } +}); + +App.WidgetsWidgetRoute = Ember.Route.extend({ + beforeModel: function() { + return Ember.$.get("support/widget.html?rnd=" + Math.random()).then(function(data) { + Ember.TEMPLATES.widget = Ember.Handlebars.compile(data); + }); + }, + model: function(params) { + return Ember.$.getJSON("support/widgets.json?rnd=" + Math.random()).then(function(data) { + var model; + + // these overwrite what was in the HTML + window.custom_metric_1 = params.id; + window.custom_metric_2 = function() { + return 10 * params.id; + }; + + window.custom_timer_1 = params.id; + window.custom_timer_2 = function() { + return 10 * params.id; + }; + + model = data.filter(function(widget) { + return String(widget.id) === params.id; + })[0]; + model.carttotal = 11.11 * params.id; + + return model; + }); + } +}); + +App.WidgetRoute = App.WidgetsWidgetRoute; + +App.HomeRoute = Ember.Route.extend({ + beforeModel: function() { + if (!Ember.TEMPLATES.home) { + return Ember.$.get("support/home.html?rnd=" + Math.random()).then(function(data) { + Ember.TEMPLATES.home = Ember.Handlebars.compile(data); + }); + } + }, + model: function() { + return Ember.$.getJSON("/delay?delay=250&file=/pages/08-ember/support/widgets.json?rnd=" + Math.random()).then(function(data) { + var model = {}; + + model.widgets = data; + model.imgs = typeof window.imgs !== "undefined" ? window.imgs : [0]; + console.log(model.imgs); + model.hide = model.imgs[0] === -1; + model.rnd = Math.random(); + + // these overwrite what was in the HTML + window.custom_metric_1 = 11; + window.custom_metric_2 = function() { + return 22; + }; + + window.custom_timer_1 = 11; + window.custom_timer_2 = function() { + return 22; + }; + + if (typeof window.performance !== "undefined" && + typeof window.performance.mark === "function") { + window.performance.mark("mark_usertiming"); + } + + return model; + }); + } +}); + +App.EmptyRoute = Ember.Route.extend({ + beforeModel: function() { + }, + model: function() { + return {}; + } +}); + +App.Router.map(function() { + this.resource("widgets"); + this.resource("widget", {path: "widgets/:id"}); + + this.resource("empty", { path: "empty" }); + + this.route("home", { path: "" }); + + var hadRouteChange = false; + var hadRouteChangeToggle = function() { + hadRouteChange = true; + }; + + if (App.ApplicationRoute) { + App.ApplicationRoute.reopen({ + activate: hadRouteChangeToggle + }); + } + else { + App.ApplicationRoute = Ember.Route.extend({ + activate: hadRouteChangeToggle + }); + } + + var hookOptions = {}; + + if (window.ember_route_wait) { + hookOptions.routeChangeWaitFilter = window.ember_route_wait; + } + + if (window.ember_route_filter) { + hookOptions.routeFilter = window.ember_route_filter; + } + + function hookEmberBoomerang() { + if (window.BOOMR && BOOMR.version) { + if (BOOMR.plugins && BOOMR.plugins.Ember) { + BOOMR.plugins.Ember.hook(App, hadRouteChange, hookOptions); + } + + return true; + } + } + + if (!window.disableBoomerangHook) { + if (!hookEmberBoomerang()) { + if (document.addEventListener) { + document.addEventListener("onBoomerangLoaded", hookEmberBoomerang); + } + else if (document.attachEvent) { + document.attachEvent("onpropertychange", function(e) { + e = e || window.event; + + if (e && e.propertyName === "onBoomerangLoaded") { + hookEmberBoomerang(); + } + }); + } + } + } +}); + +if (window.html5_mode) { + App.Router.reopen({ + location: "history", + rootURL: "" + }); +} diff --git a/tests/page-templates/08-ember/support/home.html b/tests/page-templates/08-ember/support/home.html new file mode 100644 index 000000000..8a86eea86 --- /dev/null +++ b/tests/page-templates/08-ember/support/home.html @@ -0,0 +1,15 @@ + + +

Home

+ +{{#each delay in model.imgs}} + {{#unless model.hide}} + + {{/unless}} +{{/each}} + +
    + {{#each widget in model.widgets}} +
  • {{#link-to 'widget' widget}} {{widget.name}} {{/link-to}}
  • + {{/each}} +
diff --git a/tests/page-templates/08-ember/support/img.jpg b/tests/page-templates/08-ember/support/img.jpg new file mode 100644 index 000000000..de3a1f7e9 Binary files /dev/null and b/tests/page-templates/08-ember/support/img.jpg differ diff --git a/tests/page-templates/08-ember/support/widget.html b/tests/page-templates/08-ember/support/widget.html new file mode 100644 index 000000000..fb3f9545e --- /dev/null +++ b/tests/page-templates/08-ember/support/widget.html @@ -0,0 +1,4 @@ + + +

{{model.name}}

+ diff --git a/tests/page-templates/08-ember/support/widgets.html b/tests/page-templates/08-ember/support/widgets.html new file mode 100644 index 000000000..6ab88063e --- /dev/null +++ b/tests/page-templates/08-ember/support/widgets.html @@ -0,0 +1,8 @@ +

Widgets

+
    + {{#each widget in model}} +
  • {{#link-to 'widget' widget}} {{widget.name}} {{/link-to}}
  • + {{/each}} +
+ + diff --git a/tests/page-templates/08-ember/support/widgets.json b/tests/page-templates/08-ember/support/widgets.json new file mode 100644 index 000000000..736c8f1bf --- /dev/null +++ b/tests/page-templates/08-ember/support/widgets.json @@ -0,0 +1,14 @@ +[ + { + "id": 1, + "name": "Widget 1" + }, + { + "id": 2, + "name": "Widget 2" + }, + { + "id": 3, + "name": "Widget 3" + } +] diff --git a/tests/page-templates/08-ember/tests.json5 b/tests/page-templates/08-ember/tests.json5 new file mode 100644 index 000000000..38763e1e7 --- /dev/null +++ b/tests/page-templates/08-ember/tests.json5 @@ -0,0 +1,8 @@ +{ + all: { + requires: ["spa", "history", "auto-xhr"] + }, + "29-route-change-early-beacon": { + requires: ["early"] + } +} diff --git a/tests/page-templates/09-backbone/00-simple.html b/tests/page-templates/09-backbone/00-simple.html new file mode 100644 index 000000000..1474cba83 --- /dev/null +++ b/tests/page-templates/09-backbone/00-simple.html @@ -0,0 +1,35 @@ +<%= header %> +<%= boomerangSnippet %> + + + +
+ + + + + +<%= footer %> diff --git a/tests/page-templates/09-backbone/00-simple.js b/tests/page-templates/09-backbone/00-simple.js new file mode 100644 index 000000000..9136994e8 --- /dev/null +++ b/tests/page-templates/09-backbone/00-simple.js @@ -0,0 +1,30 @@ +/* eslint-env mocha */ + +/* +* This app uses a delayed angular.bootstrap (and no ng-app) +* directive. +*/ +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, [ + "$", + "jQuery", + "Handlebars", + "Backbone", + "backbone_imgs", + "backbone_delay_startup", + "app", + "Widgets", + "AppRouter", + "hookOptions", + "hadRouteChange", + "hookBackboneBoomerang", + "backbone_start", + "custom_metric_1", + "custom_metric_2", + "custom_timer_1", + "custom_timer_2" +]); + +describe("e2e/09-backbone/00-simple", function() { + BOOMR_test.templates.SPA["00-simple"](); +}); diff --git a/tests/page-templates/09-backbone/04-route-change.html b/tests/page-templates/09-backbone/04-route-change.html new file mode 100644 index 000000000..ae89d2543 --- /dev/null +++ b/tests/page-templates/09-backbone/04-route-change.html @@ -0,0 +1,29 @@ +<%= header %> +<%= boomerangScript %> + + + + +
+ + + + + +<%= footer %> diff --git a/tests/page-templates/09-backbone/04-route-change.js b/tests/page-templates/09-backbone/04-route-change.js new file mode 100644 index 000000000..dbba14d9c --- /dev/null +++ b/tests/page-templates/09-backbone/04-route-change.js @@ -0,0 +1,27 @@ +/* eslint-env mocha */ +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, [ + "$", + "jQuery", + "Handlebars", + "Backbone", + "backbone_imgs", + "backbone_html5_mode", + "backbone_nav_routes", + "app", + "Widgets", + "AppRouter", + "hookOptions", + "hadRouteChange", + "hookBackboneBoomerang", + "backbone_start", + "custom_metric_1", + "custom_metric_2", + "custom_timer_1", + "custom_timer_2", + "i" +]); + +describe("e2e/05-backbone/04-route-change", function() { + BOOMR_test.templates.SPA["04-route-change"](); +}); diff --git a/tests/page-templates/09-backbone/05-route-change-hashtags.html b/tests/page-templates/09-backbone/05-route-change-hashtags.html new file mode 100644 index 000000000..f6d6e451a --- /dev/null +++ b/tests/page-templates/09-backbone/05-route-change-hashtags.html @@ -0,0 +1,29 @@ +<%= header %> +<%= boomerangScript %> + + + + +
+ + + + + +<%= footer %> diff --git a/tests/page-templates/09-backbone/05-route-change-hashtags.js b/tests/page-templates/09-backbone/05-route-change-hashtags.js new file mode 100644 index 000000000..40861a9a6 --- /dev/null +++ b/tests/page-templates/09-backbone/05-route-change-hashtags.js @@ -0,0 +1,29 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, [ + "$", + "jQuery", + "Handlebars", + "Backbone", + "backbone_imgs", + "backbone_html5_mode", + "backbone_nav_routes", + "app", + "Widgets", + "AppRouter", + "hookOptions", + "hadRouteChange", + "hookBackboneBoomerang", + "backbone_start", + "custom_metric_1", + "custom_metric_2", + "custom_timer_1", + "custom_timer_2", + "i" +]); + +describe("e2e/09-backbone/05-route-change-hashtags", function() { + BOOMR_test.templates.SPA["05-route-change-hashtags"](); +}); diff --git a/tests/page-templates/09-backbone/06-hard-nav-resources.html b/tests/page-templates/09-backbone/06-hard-nav-resources.html new file mode 100644 index 000000000..a6f1cf2c6 --- /dev/null +++ b/tests/page-templates/09-backbone/06-hard-nav-resources.html @@ -0,0 +1,23 @@ +<%= header %> +<%= boomerangScript %> + + + + +
+ + + + +<%= footer %> diff --git a/tests/page-templates/09-backbone/06-hard-nav-resources.js b/tests/page-templates/09-backbone/06-hard-nav-resources.js new file mode 100644 index 000000000..8dd63a576 --- /dev/null +++ b/tests/page-templates/09-backbone/06-hard-nav-resources.js @@ -0,0 +1,13 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +/* +* This app uses a delayed angular.bootstrap (and no ng-app) +* directive. +*/ +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["$", "jQuery", "Handlebars", "Backbone", "app", "Widgets", "AppRouter", "hookOptions", "hadRouteChange", "hookBackboneBoomerang", "backbone_start", "backbone_imgs", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2"]); + +describe("e2e/09-backbone/06-hard-nav-resources", function() { + BOOMR_test.templates.SPA["06-hard-nav-resources"](); +}); diff --git a/tests/page-templates/09-backbone/07-soft-nav-resources.html b/tests/page-templates/09-backbone/07-soft-nav-resources.html new file mode 100644 index 000000000..2e3e1d5b0 --- /dev/null +++ b/tests/page-templates/09-backbone/07-soft-nav-resources.html @@ -0,0 +1,34 @@ +<%= header %> +<%= boomerangScript %> + + + + + +
+ + + + +<%= footer %> diff --git a/tests/page-templates/09-backbone/07-soft-nav-resources.js b/tests/page-templates/09-backbone/07-soft-nav-resources.js new file mode 100644 index 000000000..268f4feba --- /dev/null +++ b/tests/page-templates/09-backbone/07-soft-nav-resources.js @@ -0,0 +1,32 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +/* +* This app uses a delayed angular.bootstrap (and no ng-app) +* directive. +*/ +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, [ + "$", + "jQuery", + "Handlebars", + "Backbone", + "backbone_html5_mode", + "backbone_nav_routes", + "app", + "Widgets", + "AppRouter", + "hookOptions", + "hadRouteChange", + "hookBackboneBoomerang", + "backbone_start", + "custom_metric_1", + "custom_metric_2", + "custom_timer_1", + "custom_timer_2", + "i" +]); + +describe("e2e/09-backbone/07-soft-nav-resources", function() { + BOOMR_test.templates.SPA["07-soft-nav-resources"](); +}); diff --git a/tests/page-templates/09-backbone/08-no-images.html b/tests/page-templates/09-backbone/08-no-images.html new file mode 100644 index 000000000..1b3de6400 --- /dev/null +++ b/tests/page-templates/09-backbone/08-no-images.html @@ -0,0 +1,22 @@ +<%= header %> +<%= boomerangScript %> + + + +
+ + + + +<%= footer %> diff --git a/tests/page-templates/09-backbone/08-no-images.js b/tests/page-templates/09-backbone/08-no-images.js new file mode 100644 index 000000000..c9d51fad9 --- /dev/null +++ b/tests/page-templates/09-backbone/08-no-images.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["$", "jQuery", "Handlebars", "Backbone", "app", "Widgets", "AppRouter", "hookOptions", "hadRouteChange", "hookBackboneBoomerang", "backbone_start", "backbone_imgs", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2"]); + +describe("e2e/09-backbone/08-no-images", function() { + BOOMR_test.templates.SPA["08-no-images"](); +}); diff --git a/tests/page-templates/09-backbone/09-autoxhr-after-load.html b/tests/page-templates/09-backbone/09-autoxhr-after-load.html new file mode 100644 index 000000000..058ef8f9e --- /dev/null +++ b/tests/page-templates/09-backbone/09-autoxhr-after-load.html @@ -0,0 +1,51 @@ +<%= header %> +<%= boomerangScript %> + + + +
+ + + + +<%= footer %> diff --git a/tests/page-templates/09-backbone/09-autoxhr-after-load.js b/tests/page-templates/09-backbone/09-autoxhr-after-load.js new file mode 100644 index 000000000..aba858dc0 --- /dev/null +++ b/tests/page-templates/09-backbone/09-autoxhr-after-load.js @@ -0,0 +1,28 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, [ + "$", + "jQuery", + "Handlebars", + "Backbone", + "app", + "Widgets", + "AppRouter", + "hookOptions", + "hadRouteChange", + "hookBackboneBoomerang", + "backbone_start", + "xhr", + "beaconNum", + "backbone_imgs", + "custom_metric_1", + "custom_metric_2", + "custom_timer_1", + "custom_timer_2" +]); + +describe("e2e/09-backbone/09-autoxhr-after-load", function() { + BOOMR_test.templates.SPA["09-autoxhr-after-load"](); +}); diff --git a/tests/page-templates/09-backbone/10-autoxhr-overlapping.html b/tests/page-templates/09-backbone/10-autoxhr-overlapping.html new file mode 100644 index 000000000..c67995f74 --- /dev/null +++ b/tests/page-templates/09-backbone/10-autoxhr-overlapping.html @@ -0,0 +1,44 @@ +<%= header %> +<%= boomerangScript %> + + + +
+ + + + +<%= footer %> diff --git a/tests/page-templates/09-backbone/10-autoxhr-overlapping.js b/tests/page-templates/09-backbone/10-autoxhr-overlapping.js new file mode 100644 index 000000000..c1dbb189e --- /dev/null +++ b/tests/page-templates/09-backbone/10-autoxhr-overlapping.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["$", "jQuery", "Handlebars", "Backbone", "app", "Widgets", "AppRouter", "hookOptions", "hadRouteChange", "hookBackboneBoomerang", "backbone_start", "backbone_imgs", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2"]); + +describe("e2e/09-backbone/10-autoxhr-overlapping", function() { + BOOMR_test.templates.SPA["10-autoxhr-overlapping"](); +}); diff --git a/tests/page-templates/09-backbone/11-autoxhr-trigger-additional.html b/tests/page-templates/09-backbone/11-autoxhr-trigger-additional.html new file mode 100644 index 000000000..9689952be --- /dev/null +++ b/tests/page-templates/09-backbone/11-autoxhr-trigger-additional.html @@ -0,0 +1,47 @@ +<%= header %> +<%= boomerangScript %> + + + +
+ + + + +<%= footer %> diff --git a/tests/page-templates/09-backbone/11-autoxhr-trigger-additional.js b/tests/page-templates/09-backbone/11-autoxhr-trigger-additional.js new file mode 100644 index 000000000..0c1edd957 --- /dev/null +++ b/tests/page-templates/09-backbone/11-autoxhr-trigger-additional.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["$", "jQuery", "Handlebars", "Backbone", "app", "Widgets", "AppRouter", "hookOptions", "hadRouteChange", "hookBackboneBoomerang", "backbone_start", "backbone_imgs", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2"]); + +describe("e2e/09-backbone/11-autoxhr-trigger-additional", function() { + BOOMR_test.templates.SPA["11-autoxhr-trigger-additional"](); +}); diff --git a/tests/page-templates/09-backbone/12-autoxhr-trigger-additional-after-delay.html b/tests/page-templates/09-backbone/12-autoxhr-trigger-additional-after-delay.html new file mode 100644 index 000000000..205c56934 --- /dev/null +++ b/tests/page-templates/09-backbone/12-autoxhr-trigger-additional-after-delay.html @@ -0,0 +1,49 @@ +<%= header %> +<%= boomerangScript %> + + + +
+ + + + +<%= footer %> diff --git a/tests/page-templates/09-backbone/12-autoxhr-trigger-additional-after-delay.js b/tests/page-templates/09-backbone/12-autoxhr-trigger-additional-after-delay.js new file mode 100644 index 000000000..bd67aa61d --- /dev/null +++ b/tests/page-templates/09-backbone/12-autoxhr-trigger-additional-after-delay.js @@ -0,0 +1,26 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, [ + "$", + "jQuery", + "Handlebars", + "Backbone", + "app", + "Widgets", + "AppRouter", + "hookOptions", + "hadRouteChange", + "hookBackboneBoomerang", + "backbone_start", + "backbone_imgs", + "custom_metric_1", + "custom_metric_2", + "custom_timer_1", + "custom_timer_2" +]); + +describe("e2e/09-backbone/12-autoxhr-trigger-additional-after-delay", function() { + BOOMR_test.templates.SPA["12-autoxhr-trigger-additional-after-delay"](); +}); diff --git a/tests/page-templates/09-backbone/13-autoxhr-disabled.html b/tests/page-templates/09-backbone/13-autoxhr-disabled.html new file mode 100644 index 000000000..37674e46c --- /dev/null +++ b/tests/page-templates/09-backbone/13-autoxhr-disabled.html @@ -0,0 +1,47 @@ +<%= header %> +<%= boomerangScript %> + + + +
+ + + + +<%= footer %> diff --git a/tests/page-templates/09-backbone/13-autoxhr-disabled.js b/tests/page-templates/09-backbone/13-autoxhr-disabled.js new file mode 100644 index 000000000..73f08051d --- /dev/null +++ b/tests/page-templates/09-backbone/13-autoxhr-disabled.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["$", "jQuery", "Handlebars", "Backbone", "app", "Widgets", "AppRouter", "hookOptions", "hadRouteChange", "hookBackboneBoomerang", "backbone_start", "backbone_imgs", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2"]); + +describe("e2e/09-backbone/13-autoxhr-disabled", function() { + BOOMR_test.templates.SPA["13-autoxhr-disabled"](); +}); diff --git a/tests/page-templates/09-backbone/14-autoxhr-before-page-load.html b/tests/page-templates/09-backbone/14-autoxhr-before-page-load.html new file mode 100644 index 000000000..0fb021152 --- /dev/null +++ b/tests/page-templates/09-backbone/14-autoxhr-before-page-load.html @@ -0,0 +1,30 @@ +<%= header %> +<%= boomerangScript %> + + + +
+ + + + +<%= footer %> diff --git a/tests/page-templates/09-backbone/14-autoxhr-before-page-load.js b/tests/page-templates/09-backbone/14-autoxhr-before-page-load.js new file mode 100644 index 000000000..546f7d97e --- /dev/null +++ b/tests/page-templates/09-backbone/14-autoxhr-before-page-load.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["$", "jQuery", "Handlebars", "Backbone", "app", "Widgets", "AppRouter", "hookOptions", "hadRouteChange", "hookBackboneBoomerang", "backbone_start", "xhr", "backbone_imgs", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2"]); + +describe("e2e/09-backbone/14-autoxhr-before-page-load", function() { + BOOMR_test.templates.SPA["14-autoxhr-before-page-load"](); +}); diff --git a/tests/page-templates/09-backbone/15-delayed-boomerang.html b/tests/page-templates/09-backbone/15-delayed-boomerang.html new file mode 100644 index 000000000..3348a1261 --- /dev/null +++ b/tests/page-templates/09-backbone/15-delayed-boomerang.html @@ -0,0 +1,20 @@ +<%= header %> + + +<%= boomerangDelayedSnippet %> + + + +
+ + + + +<%= footer %> diff --git a/tests/page-templates/09-backbone/15-delayed-boomerang.js b/tests/page-templates/09-backbone/15-delayed-boomerang.js new file mode 100644 index 000000000..e235503be --- /dev/null +++ b/tests/page-templates/09-backbone/15-delayed-boomerang.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["BOOMR_script_delay", "$", "jQuery", "Handlebars", "Backbone", "app", "Widgets", "AppRouter", "hookOptions", "hadRouteChange", "hookBackboneBoomerang", "backbone_start", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2"]); + +describe("e2e/09-backbone/15-delayed-boomerang", function() { + BOOMR_test.templates.SPA["15-delayed-boomerang"](); +}); diff --git a/tests/page-templates/09-backbone/16-autoxhr-after-load-with-mutation.html b/tests/page-templates/09-backbone/16-autoxhr-after-load-with-mutation.html new file mode 100644 index 000000000..db1c0a531 --- /dev/null +++ b/tests/page-templates/09-backbone/16-autoxhr-after-load-with-mutation.html @@ -0,0 +1,58 @@ +<%= header %> +<%= boomerangScript %> + + + +
+
+ + + + +<%= footer %> diff --git a/tests/page-templates/09-backbone/16-autoxhr-after-load-with-mutation.js b/tests/page-templates/09-backbone/16-autoxhr-after-load-with-mutation.js new file mode 100644 index 000000000..969984d7c --- /dev/null +++ b/tests/page-templates/09-backbone/16-autoxhr-after-load-with-mutation.js @@ -0,0 +1,28 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, [ + "$", + "jQuery", + "Handlebars", + "Backbone", + "app", + "Widgets", + "AppRouter", + "hookOptions", + "hadRouteChange", + "hookBackboneBoomerang", + "backbone_start", + "xhr", + "beaconNum", + "backbone_imgs", + "custom_metric_1", + "custom_metric_2", + "custom_timer_1", + "custom_timer_2" +]); + +describe("e2e/09-backbone/16-autoxhr-after-load-with-mutation", function() { + BOOMR_test.templates.SPA["16-autoxhr-after-load-with-mutation"](); +}); diff --git a/tests/page-templates/09-backbone/17-wait.html b/tests/page-templates/09-backbone/17-wait.html new file mode 100644 index 000000000..d4de29ba9 --- /dev/null +++ b/tests/page-templates/09-backbone/17-wait.html @@ -0,0 +1,70 @@ +<%= header %> +<%= boomerangScript %> + + + + +
+ + + + + + +<%= footer %> diff --git a/tests/page-templates/09-backbone/17-wait.js b/tests/page-templates/09-backbone/17-wait.js new file mode 100644 index 000000000..03b3c448b --- /dev/null +++ b/tests/page-templates/09-backbone/17-wait.js @@ -0,0 +1,33 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, [ + "$", + "jQuery", + "Handlebars", + "Backbone", + "routeWaits", + "routeNumber", + "backbone_imgs", + "backbone_html5_mode", + "backbone_nav_routes", + "spaWaitCompleteTimes", + "backbone_route_wait", + "app", + "Widgets", + "AppRouter", + "hookOptions", + "hadRouteChange", + "hookBackboneBoomerang", + "backbone_start", + "custom_metric_1", + "custom_metric_2", + "custom_timer_1", + "custom_timer_2", + "i" +]); + +describe("e2e/09-backbone/17-wait", function() { + BOOMR_test.templates.SPA["17-wait"](); +}); diff --git a/tests/page-templates/09-backbone/18-autoxhr-before-page-load-alwayssendxhr.html b/tests/page-templates/09-backbone/18-autoxhr-before-page-load-alwayssendxhr.html new file mode 100644 index 000000000..d089e0be0 --- /dev/null +++ b/tests/page-templates/09-backbone/18-autoxhr-before-page-load-alwayssendxhr.html @@ -0,0 +1,25 @@ +<%= header %> +<%= boomerangScript %> + + + +
+ + + + +<%= footer %> diff --git a/tests/page-templates/09-backbone/18-autoxhr-before-page-load-alwayssendxhr.js b/tests/page-templates/09-backbone/18-autoxhr-before-page-load-alwayssendxhr.js new file mode 100644 index 000000000..1b910bc6d --- /dev/null +++ b/tests/page-templates/09-backbone/18-autoxhr-before-page-load-alwayssendxhr.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["$", "jQuery", "Handlebars", "Backbone", "backbone_imgs", "app", "Widgets", "AppRouter", "hookOptions", "hadRouteChange", "hookBackboneBoomerang", "backbone_start", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2"]); + +describe("e2e/09-backbone/18-autoxhr-before-page-load-alwayssendxhr", function() { + BOOMR_test.templates.SPA["18-autoxhr-before-page-load-alwayssendxhr"](); +}); diff --git a/tests/page-templates/09-backbone/19-autoxhr-during-nav-alwayssendxhr.html b/tests/page-templates/09-backbone/19-autoxhr-during-nav-alwayssendxhr.html new file mode 100644 index 000000000..a67089c95 --- /dev/null +++ b/tests/page-templates/09-backbone/19-autoxhr-during-nav-alwayssendxhr.html @@ -0,0 +1,28 @@ +<%= header %> +<%= boomerangScript %> + + + + +
+ + + + +<%= footer %> diff --git a/tests/page-templates/09-backbone/19-autoxhr-during-nav-alwayssendxhr.js b/tests/page-templates/09-backbone/19-autoxhr-during-nav-alwayssendxhr.js new file mode 100644 index 000000000..9ffbf4652 --- /dev/null +++ b/tests/page-templates/09-backbone/19-autoxhr-during-nav-alwayssendxhr.js @@ -0,0 +1,27 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, [ + "$", + "jQuery", + "Handlebars", + "Backbone", + "backbone_imgs", + "backbone_nav_routes", + "app", + "Widgets", + "AppRouter", + "hookOptions", + "hadRouteChange", + "hookBackboneBoomerang", + "backbone_start", + "custom_metric_1", + "custom_metric_2", + "custom_timer_1", + "custom_timer_2", + "i" +]); + +describe("e2e/09-backbone/19-autoxhr-during-nav-alwayssendxhr", function() { + BOOMR_test.templates.SPA["19-autoxhr-during-nav-alwayssendxhr"](); +}); diff --git a/tests/page-templates/09-backbone/20-no-resources.html b/tests/page-templates/09-backbone/20-no-resources.html new file mode 100644 index 000000000..ad58f60b1 --- /dev/null +++ b/tests/page-templates/09-backbone/20-no-resources.html @@ -0,0 +1,29 @@ +<%= header %> +<%= boomerangScript %> + + + + +
+ + + + + +<%= footer %> diff --git a/tests/page-templates/09-backbone/20-no-resources.js b/tests/page-templates/09-backbone/20-no-resources.js new file mode 100644 index 000000000..59045167c --- /dev/null +++ b/tests/page-templates/09-backbone/20-no-resources.js @@ -0,0 +1,29 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, [ + "$", + "jQuery", + "Handlebars", + "Backbone", + "backbone_imgs", + "backbone_html5_mode", + "backbone_nav_routes", + "app", + "Widgets", + "AppRouter", + "hookOptions", + "hadRouteChange", + "hookBackboneBoomerang", + "backbone_start", + "custom_metric_1", + "custom_metric_2", + "custom_timer_1", + "custom_timer_2", + "i" +]); + +describe("e2e/09-backbone/20-no-resources", function() { + BOOMR_test.templates.SPA["20-no-resources"](); +}); diff --git a/tests/page-templates/09-backbone/21-constant-mutations.html b/tests/page-templates/09-backbone/21-constant-mutations.html new file mode 100644 index 000000000..ef0e8d7fe --- /dev/null +++ b/tests/page-templates/09-backbone/21-constant-mutations.html @@ -0,0 +1,27 @@ +<%= header %> +<%= boomerangScript %> + + + + +
+ + + + +<%= footer %> diff --git a/tests/page-templates/09-backbone/21-constant-mutations.js b/tests/page-templates/09-backbone/21-constant-mutations.js new file mode 100644 index 000000000..c3a8e53a4 --- /dev/null +++ b/tests/page-templates/09-backbone/21-constant-mutations.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["$", "jQuery", "Handlebars", "Backbone", "app", "Widgets", "AppRouter", "hookOptions", "hadRouteChange", "hookBackboneBoomerang", "backbone_start", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2"]); + +describe("e2e/09-backbone/21-constant-mutations", function() { + BOOMR_test.templates.SPA["21-constant-mutations"](); +}); diff --git a/tests/page-templates/09-backbone/23-hard-wait-for-onload.html b/tests/page-templates/09-backbone/23-hard-wait-for-onload.html new file mode 100644 index 000000000..85893fb22 --- /dev/null +++ b/tests/page-templates/09-backbone/23-hard-wait-for-onload.html @@ -0,0 +1,23 @@ +<%= header %> +<%= boomerangScript %> + + + +
+ + + + + +<%= footer %> diff --git a/tests/page-templates/09-backbone/23-hard-wait-for-onload.js b/tests/page-templates/09-backbone/23-hard-wait-for-onload.js new file mode 100644 index 000000000..8ace3f137 --- /dev/null +++ b/tests/page-templates/09-backbone/23-hard-wait-for-onload.js @@ -0,0 +1,8 @@ +/* eslint-env mocha */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["$", "jQuery", "Handlebars", "Backbone", "backbone_imgs", "app", "Widgets", "AppRouter", "hookOptions", "hadRouteChange", "hookBackboneBoomerang", "backbone_start", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2"]); + +describe("e2e/09-backbone/23-hard-wait-for-onload", function() { + BOOMR_test.templates.SPA["23-hard-wait-for-onload"](); +}); diff --git a/tests/page-templates/09-backbone/24-route-filter.html b/tests/page-templates/09-backbone/24-route-filter.html new file mode 100644 index 000000000..b181b4a5b --- /dev/null +++ b/tests/page-templates/09-backbone/24-route-filter.html @@ -0,0 +1,39 @@ +<%= header %> + +<%= boomerangScript %> + + + + + +
+ + + + +<%= footer %> diff --git a/tests/page-templates/09-backbone/24-route-filter.js b/tests/page-templates/09-backbone/24-route-filter.js new file mode 100644 index 000000000..3eb0eafe6 --- /dev/null +++ b/tests/page-templates/09-backbone/24-route-filter.js @@ -0,0 +1,31 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, [ + "$", + "jQuery", + "Handlebars", + "Backbone", + "navs", + "backbone_imgs", + "backbone_html5_mode", + "backbone_nav_routes", + "backbone_route_filter", + "app", + "Widgets", + "AppRouter", + "hookOptions", + "hadRouteChange", + "hookBackboneBoomerang", + "backbone_start", + "custom_metric_1", + "custom_metric_2", + "custom_timer_1", + "custom_timer_2", + "i" +]); + +describe("e2e/09-backbone/24-route-filter", function() { + BOOMR_test.templates.SPA["24-route-filter"](); +}); diff --git a/tests/page-templates/09-backbone/25-delayed-boomerang-pre-config-snippet.html b/tests/page-templates/09-backbone/25-delayed-boomerang-pre-config-snippet.html new file mode 100644 index 000000000..d96b67adc --- /dev/null +++ b/tests/page-templates/09-backbone/25-delayed-boomerang-pre-config-snippet.html @@ -0,0 +1,41 @@ +<%= header %> +<%= boomerangDelayedSnippet %> + + + + +
+ + + + +<%= footer %> diff --git a/tests/page-templates/09-backbone/25-delayed-boomerang-pre-config-snippet.js b/tests/page-templates/09-backbone/25-delayed-boomerang-pre-config-snippet.js new file mode 100644 index 000000000..95b7b74d2 --- /dev/null +++ b/tests/page-templates/09-backbone/25-delayed-boomerang-pre-config-snippet.js @@ -0,0 +1,27 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, [ + "BOOMR_script_delay", + "$", + "jQuery", + "Handlebars", + "Backbone", + "timestamp", + "app", + "Widgets", + "AppRouter", + "hookOptions", + "hadRouteChange", + "hookBackboneBoomerang", + "backbone_start", + "custom_metric_1", + "custom_metric_2", + "custom_timer_1", + "custom_timer_2" +]); + +describe("e2e/09-backbone/25-delayed-boomerang-pre-config-snippet", function() { + BOOMR_test.templates.SPA["25-delayed-boomerang-pre-config-snippet"](); +}); diff --git a/tests/page-templates/09-backbone/27-route-change-back.html b/tests/page-templates/09-backbone/27-route-change-back.html new file mode 100644 index 000000000..c6f930003 --- /dev/null +++ b/tests/page-templates/09-backbone/27-route-change-back.html @@ -0,0 +1,45 @@ +<%= header %> +<%= boomerangScript %> + + + + +
+ + + + + +<%= footer %> diff --git a/tests/page-templates/09-backbone/27-route-change-back.js b/tests/page-templates/09-backbone/27-route-change-back.js new file mode 100644 index 000000000..d5471bea1 --- /dev/null +++ b/tests/page-templates/09-backbone/27-route-change-back.js @@ -0,0 +1,31 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert,angular */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, [ + "$", + "jQuery", + "Handlebars", + "Backbone", + "backbone_imgs", + "backbone_html5_mode", + "backbone_nav_routes", + "app", + "Widgets", + "AppRouter", + "hookOptions", + "hadRouteChange", + "hookBackboneBoomerang", + "backbone_start", + "beaconNum", + "custom_metric_1", + "custom_metric_2", + "custom_timer_1", + "custom_timer_2", + "i" +]); + +describe("e2e/09-backbone/27-route-change-back", function() { + // use tests from #4 + BOOMR_test.templates.SPA["04-route-change"](); +}); diff --git a/tests/page-templates/09-backbone/28-route-change-history-auto.html b/tests/page-templates/09-backbone/28-route-change-history-auto.html new file mode 100644 index 000000000..9ec423add --- /dev/null +++ b/tests/page-templates/09-backbone/28-route-change-history-auto.html @@ -0,0 +1,33 @@ +<%= header %> +<%= boomerangScript %> + + + + +
+ + + + + +<%= footer %> diff --git a/tests/page-templates/09-backbone/28-route-change-history-auto.js b/tests/page-templates/09-backbone/28-route-change-history-auto.js new file mode 100644 index 000000000..2b8213fee --- /dev/null +++ b/tests/page-templates/09-backbone/28-route-change-history-auto.js @@ -0,0 +1,31 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert,angular */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, [ + "$", + "jQuery", + "Handlebars", + "Backbone", + "backbone_imgs", + "backbone_html5_mode", + "backbone_nav_routes", + "disableBoomerangHook", + "app", + "Widgets", + "AppRouter", + "hookOptions", + "hadRouteChange", + "hookBackboneBoomerang", + "backbone_start", + "custom_metric_1", + "custom_metric_2", + "custom_timer_1", + "custom_timer_2", + "i" +]); + +describe("e2e/09-backbone/28-route-change-history-auto", function() { + // use tests from #4 + BOOMR_test.templates.SPA["04-route-change"](); +}); diff --git a/tests/page-templates/09-backbone/29-route-change-early-beacon.html b/tests/page-templates/09-backbone/29-route-change-early-beacon.html new file mode 100644 index 000000000..72f4c11d5 --- /dev/null +++ b/tests/page-templates/09-backbone/29-route-change-early-beacon.html @@ -0,0 +1,140 @@ +<%= header %> +<%= boomerangScript %> + + + + +
+ + + + + + +<%= footer %> diff --git a/tests/page-templates/09-backbone/29-route-change-early-beacon.js b/tests/page-templates/09-backbone/29-route-change-early-beacon.js new file mode 100644 index 000000000..d01c7216f --- /dev/null +++ b/tests/page-templates/09-backbone/29-route-change-early-beacon.js @@ -0,0 +1,29 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, [ + "$", + "jQuery", + "Handlebars", + "Backbone", + "backbone_imgs", + "backbone_html5_mode", + "backbone_nav_routes", + "app", + "Widgets", + "AppRouter", + "hookOptions", + "hadRouteChange", + "hookBackboneBoomerang", + "backbone_start", + "custom_metric_1", + "custom_metric_2", + "custom_timer_1", + "custom_timer_2", + "i" +]); + +describe("e2e/05-angular/29-route-change-early-beacon", function() { + BOOMR_test.templates.SPA["29-route-change-early-beacon"](); +}); diff --git a/tests/page-templates/09-backbone/support/app.js b/tests/page-templates/09-backbone/support/app.js new file mode 100644 index 000000000..f8a77af95 --- /dev/null +++ b/tests/page-templates/09-backbone/support/app.js @@ -0,0 +1,265 @@ +/* global Backbone,$,Handlebars */ +var app = window.app || {}; + +app.TEMPLATES = {}; + +// +// Widget model +// +app.Widget = Backbone.Model.extend({ +}); + +// +// Widgets collection +// +var Widgets = Backbone.Collection.extend({ + model: app.Widget, + url: "/delay?delay=250&file=/pages/09-backbone/support/widgets.json" +}); + +app.widgets = new Widgets(); + +// +// Router +// +var AppRouter = Backbone.Router.extend({ + routes: { + "widgets/:id": "widget", + "empty": "empty", + "*path": "defaultRoute" + }, + widget: function(id) { + var model = app.widgets.get(id); + var view = new app.WidgetView({ model: model }); + + view.render(); + }, + defaultRoute: function() { + var view = new app.HomeView(); + + view.render(); + }, + empty: function() { + var view = new app.EmptyView(); + + view.render(); + } +}); + +app.Router = new AppRouter(); + +// +// View - Home +// +app.HomeView = Backbone.View.extend({ + el: $("#content"), + initialize: function() { + }, + renderTemplate: function() { + var that = this; + + app.widgets.fetch({ + data: { + rnd: Math.random() + }, + reset: true, + success: function() { + // + // Custom metrics and timers + // + window.custom_metric_1 = 11; + window.custom_metric_2 = function() { + return 22; + }; + + window.custom_timer_1 = 11; + window.custom_timer_2 = function() { + return 22; + }; + + if (typeof window.performance !== "undefined" && + typeof window.performance.mark === "function") { + window.performance.mark("mark_usertiming"); + } + + var template = Handlebars.compile(app.TEMPLATES.home); + + var imgs = typeof window.backbone_imgs !== "undefined" ? window.backbone_imgs : [0]; + var hide_imgs = imgs[0] === -1; + + that.$el.html(template({ + imgs: imgs, + hide_imgs: hide_imgs, + widgets: app.widgets.toJSON(), + rnd: Math.random() + })); + } + }); + }, + render: function() { + var that = this; + + if (!app.TEMPLATES.home) { + $.get("support/home.html", function(template) { + app.TEMPLATES.home = template; + that.renderTemplate(); + }, "html"); + } + else { + // have renderTemplate run in a callback so 'route' fires first, so it + // ensures the widgets XHR is tracked + setTimeout(that.renderTemplate.bind(that), 0); + } + } +}); + +// +// View - Widget +// +app.WidgetView = Backbone.View.extend({ + el: $("#content"), + initialize: function() { + }, + renderTemplate: function() { + var that = this; + + app.widgets.fetch({ + data: { + rnd: Math.random() + }, + success: function() { + // these overwrite what was in the HTML + window.custom_metric_1 = that.model.id; + window.custom_metric_2 = function() { + return 10 * that.model.id; + }; + + window.custom_timer_1 = that.model.id; + window.custom_timer_2 = function() { + return 10 * that.model.id; + }; + + var widget = app.widgets.get(that.model.id).toJSON(); + + var template = Handlebars.compile(app.TEMPLATES.widgets); + + that.$el.html(template({ + widget: widget, + rnd: Math.random(), + carttotal: 11.11 * that.model.id + })); + } + }); + }, + render: function() { + var that = this; + + // have renderTemplate run in a callback so 'route' fires first, so it + // ensures the widgets XHR is tracked + setTimeout(function() { + if (!app.TEMPLATES.widgets) { + $.get("support/widget.html", function(template) { + app.TEMPLATES.widgets = template; + that.renderTemplate(); + }, "html"); + } + else { + that.renderTemplate(); + } + }, 0); + } +}); + +// +// View - Empty +// +app.EmptyView = Backbone.View.extend({ + el: $("#content"), + initialize: function() { + }, + render: function() { + this.$el.html("Empty"); + } +}); + +// +// Boomerang snippet +// +var hookOptions = {}; + +if (window.backbone_route_wait) { + hookOptions.routeChangeWaitFilter = window.backbone_route_wait; +} + +if (window.backbone_route_filter) { + hookOptions.routeFilter = window.backbone_route_filter; +} + +var hadRouteChange = false; + +app.Router.on("route", function() { + hadRouteChange = true; +}); + +function hookBackboneBoomerang() { + if (window.BOOMR && BOOMR.version) { + if (BOOMR.plugins && BOOMR.plugins.Backbone) { + BOOMR.plugins.Backbone.hook(app.Router, hadRouteChange, hookOptions); + } + + return true; + } +} + +if (!window.disableBoomerangHook) { + if (!hookBackboneBoomerang()) { + if (document.addEventListener) { + document.addEventListener("onBoomerangLoaded", hookBackboneBoomerang); + } + else if (document.attachEvent) { + document.attachEvent("onpropertychange", function(e) { + e = e || window.event; + + if (e && e.propertyName === "onBoomerangLoaded") { + hookBackboneBoomerang(); + } + }); + } + } +} + +// +// Start the app +// +window.backbone_start = function() { + Backbone.history.start({ + root: "/pages/09-backbone/", + pushState: window.backbone_html5_mode ? true : false + }); +}; + +if (!window.backbone_delay_startup) { + window.backbone_start(); +} + +if (typeof window.backbone_nav_routes !== "undefined" && + Object.prototype.toString.call(window.backbone_nav_routes) === "[object Array]") { + BOOMR.subscribe("beacon", function(beacon) { + // only continue for non-early SPA beacons + if (!BOOMR.utils.inArray(beacon["http.initiator"], BOOMR.constants.BEACON_TYPE_SPAS) || + typeof beacon.early !== "undefined") { + return; + } + + if (window.backbone_nav_routes.length > 0) { + var nextRoute = window.backbone_nav_routes.shift(); + + setTimeout(function() { + app.Router.navigate(nextRoute, { + trigger: true, + replace: false // use pushState instead of replaceState + }); + }, 100); + } + }); +} diff --git a/tests/page-templates/09-backbone/support/home.html b/tests/page-templates/09-backbone/support/home.html new file mode 100644 index 000000000..1efef949b --- /dev/null +++ b/tests/page-templates/09-backbone/support/home.html @@ -0,0 +1,16 @@ +
+

Home

+ {{#unless hide_imgs}} + {{#each imgs}} + + {{/each}} + {{/unless}} + +
    + {{#each widgets}} +
  • {{name}}
  • + {{/each}} +
+
diff --git a/tests/page-templates/09-backbone/support/img.jpg b/tests/page-templates/09-backbone/support/img.jpg new file mode 100644 index 000000000..de3a1f7e9 Binary files /dev/null and b/tests/page-templates/09-backbone/support/img.jpg differ diff --git a/tests/page-templates/09-backbone/support/widget.html b/tests/page-templates/09-backbone/support/widget.html new file mode 100644 index 000000000..6204cc01a --- /dev/null +++ b/tests/page-templates/09-backbone/support/widget.html @@ -0,0 +1,5 @@ +

{{widget.name}}

+ + diff --git a/tests/page-templates/09-backbone/support/widgets.json b/tests/page-templates/09-backbone/support/widgets.json new file mode 100644 index 000000000..736c8f1bf --- /dev/null +++ b/tests/page-templates/09-backbone/support/widgets.json @@ -0,0 +1,14 @@ +[ + { + "id": 1, + "name": "Widget 1" + }, + { + "id": 2, + "name": "Widget 2" + }, + { + "id": 3, + "name": "Widget 3" + } +] diff --git a/tests/page-templates/09-backbone/tests.json5 b/tests/page-templates/09-backbone/tests.json5 new file mode 100644 index 000000000..38763e1e7 --- /dev/null +++ b/tests/page-templates/09-backbone/tests.json5 @@ -0,0 +1,8 @@ +{ + all: { + requires: ["spa", "history", "auto-xhr"] + }, + "29-route-change-early-beacon": { + requires: ["early"] + } +} diff --git a/tests/page-templates/11-restiming/00-clear-onbeacon.html b/tests/page-templates/11-restiming/00-clear-onbeacon.html new file mode 100644 index 000000000..10432cc89 --- /dev/null +++ b/tests/page-templates/11-restiming/00-clear-onbeacon.html @@ -0,0 +1,13 @@ +<%= header %> +<%= boomerangSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/11-restiming/00-clear-onbeacon.js b/tests/page-templates/11-restiming/00-clear-onbeacon.js new file mode 100644 index 000000000..277eef263 --- /dev/null +++ b/tests/page-templates/11-restiming/00-clear-onbeacon.js @@ -0,0 +1,31 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/11-restiming/00-clear-onbeacon", function() { + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should clear ResourceTiming array after beacon (if ResourceTiming is enabled)", function() { + var a; + + if (t.isResourceTimingSupported()) { + var entries = window.performance.getEntriesByType("resource"); + + if (entries.length === 1) { + // if we have 1 entry then it should be the beacon + a = document.createElement("a"); + a.href = entries[0].name; + assert.equal(a.pathname, BOOMR.getBeaconURL()); + } + else { + assert.equal(entries.length, 0); + } + } + else { + this.skip(); + } + }); +}); diff --git a/tests/page-templates/11-restiming/01-clear-onbeacon-disabled.html b/tests/page-templates/11-restiming/01-clear-onbeacon-disabled.html new file mode 100644 index 000000000..9ab955ca0 --- /dev/null +++ b/tests/page-templates/11-restiming/01-clear-onbeacon-disabled.html @@ -0,0 +1,14 @@ +<%= header %> +<%= boomerangSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/11-restiming/01-clear-onbeacon-disabled.js b/tests/page-templates/11-restiming/01-clear-onbeacon-disabled.js new file mode 100644 index 000000000..e951b3402 --- /dev/null +++ b/tests/page-templates/11-restiming/01-clear-onbeacon-disabled.js @@ -0,0 +1,21 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/11-restiming/01-clear-onbeacon-disabled", function() { + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should not clear ResourceTiming array after beacon (if ResourceTiming is enabled)", function() { + if (t.isResourceTimingSupported()) { + var entries = window.performance.getEntriesByType("resource"); + + assert.isTrue(entries.length > 0); + } + else { + this.skip(); + } + }); +}); diff --git a/tests/page-templates/11-restiming/02-resource-dimensions.html b/tests/page-templates/11-restiming/02-resource-dimensions.html new file mode 100644 index 000000000..04f26e0b4 --- /dev/null +++ b/tests/page-templates/11-restiming/02-resource-dimensions.html @@ -0,0 +1,25 @@ +<%= header %> +<%= boomerangSnippet %> + + + + + + + + + + + + + + + +<%= footer %> diff --git a/tests/page-templates/11-restiming/02-resource-dimensions.js b/tests/page-templates/11-restiming/02-resource-dimensions.js new file mode 100644 index 000000000..9a607e40c --- /dev/null +++ b/tests/page-templates/11-restiming/02-resource-dimensions.js @@ -0,0 +1,64 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/11-restiming/02-resource-dimensions", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should pass basic beacon validation", function(done){ + t.validateBeaconWasSent(done); + }); + + it("Should have dimensions for the IMG on the page (if ResourceTiming is supported)", function() { + if (t.isResourceTimingSupported()) { + var b = tf.beacons[0]; + + // 200 height = 5k + // 400 width = b4 + // 500 y = dw + // 100 x = 2s + // 742 nh = km + // 2000 nw = 1jk + assert.match(b.restiming, /\*05k,b4,dw,2s,km,1jk\b/); + } + else { + this.skip(); + } + }); + + it("Should have dimensions for the IFRAME on the page (if ResourceTiming is supported)", function() { + if (t.isResourceTimingSupported()) { + var b = tf.beacons[0]; + + // 50 height = 1e + // 50 width = 1e + // 800 y = m8 + // 200 x = 5k + assert.include(b.restiming, "*01e,1e,m8,5k"); + } + else { + this.skip(); + } + }); + + it("Should not have timepoints for resources on the page (even if ResourceTiming is supported)", function() { + if (t.isResourceTimingSupported()) { + var b = tf.beacons[0]; + + assert.isUndefined(b.timepoints); + + /* + var decompressed = BOOMR.plugins.ResourceTiming.decompressTimePoints(b.timepoints); + var tp = Object.keys(decompressed).map(Number).sort(function(a1, a2) { return a1 - a2; }); + + assert.strictEqual(tp.length, 5); + + assert.strictEqual(decompressed[tp[0]][0], 80000); + assert.strictEqual(decompressed[tp[0]][1], 21400); + */ + } + else { + this.skip(); + } + }); +}); diff --git a/tests/page-templates/11-restiming/03-url-length.html b/tests/page-templates/11-restiming/03-url-length.html new file mode 100644 index 000000000..15f3710b5 --- /dev/null +++ b/tests/page-templates/11-restiming/03-url-length.html @@ -0,0 +1,31 @@ +<%= header %> +<%= boomerangSnippet %> + + + +<%= footer %> diff --git a/tests/page-templates/11-restiming/03-url-length.js b/tests/page-templates/11-restiming/03-url-length.js new file mode 100644 index 000000000..f2041b1e1 --- /dev/null +++ b/tests/page-templates/11-restiming/03-url-length.js @@ -0,0 +1,56 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["ResourceTimingDecompression", "url", "i", "img", "img2"]); + +describe("e2e/11-restiming/03-url-length", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have a restiming parameter on the beacon (if ResourceTiming is supported)", function() { + if (t.isResourceTimingSupported()) { + var b = tf.lastBeacon(); + + assert.isDefined(b.restiming); + } + else { + this.skip(); + } + }); + + it("Should have trimmed the long URL (if ResourceTiming is supported)", function() { + if (t.isResourceTimingSupported()) { + var b = tf.lastBeacon(); + + var resources = ResourceTimingDecompression.decompressResources(JSON.parse(b.restiming)); + + // find our img + assert.isDefined(BOOMR.utils.arrayFind(resources, function(r) { + return r.name.indexOf("blackhole?...") !== -1; + }), "Find blackhole?..."); + } + else { + this.skip(); + } + }); + + it("Should have trimmed the pixel URL (if ResourceTiming is supported)", function() { + if (t.isResourceTimingSupported()) { + var b = tf.lastBeacon(); + var resources = ResourceTimingDecompression.decompressResources(JSON.parse(b.restiming)); + + // find our img + assert.isDefined(BOOMR.utils.arrayFind(resources, function(r) { + return r.name.indexOf("/foo/...") !== -1; + }), "Find /foo/..."); + } + else { + this.skip(); + } + }); +}); diff --git a/tests/page-templates/11-restiming/04-resource-sizes.html b/tests/page-templates/11-restiming/04-resource-sizes.html new file mode 100644 index 000000000..c744ee219 --- /dev/null +++ b/tests/page-templates/11-restiming/04-resource-sizes.html @@ -0,0 +1,42 @@ +<%= header %> +<%= boomerangSnippet %> + + + + + + + + + +<%= footer %> diff --git a/tests/page-templates/11-restiming/04-resource-sizes.js b/tests/page-templates/11-restiming/04-resource-sizes.js new file mode 100644 index 000000000..f0188f122 --- /dev/null +++ b/tests/page-templates/11-restiming/04-resource-sizes.js @@ -0,0 +1,42 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["ResourceTimingDecompression", "t", "xhr1", "xhr2", "f1", "f2", "consume"]); + +describe("e2e/11-restiming/04-resource-sizes", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should pass basic beacon validation", function(done){ + t.validateBeaconWasSent(done); + }); + + it("Should have sizes for the IMGs on the page (if ResourceTiming2 is supported)", function() { + if (t.isResourceTimingSupported()) { + var b = tf.beacons[0]; + + var resources = ResourceTimingDecompression.decompressResources(JSON.parse(b.restiming)); + + // find our 3 (w/o fetch) or 5 (w/ fetch) img entries + var cnt = 0; + + for (var i = resources.length - 1; i >= 0; i--) { + var img = resources[i]; + + if (img.name.indexOf("assets/img.jpg") !== -1) { + cnt += 1; + + if (img.encodedBodySize) { + assert.equal(296341, img.encodedBodySize); + } + } + } + + assert.equal(t.isFetchApiSupported() ? 5 : 3, cnt); + } + else { + this.skip(); + } + }); +}); diff --git a/tests/page-templates/11-restiming/05-no-beacon-url.html b/tests/page-templates/11-restiming/05-no-beacon-url.html new file mode 100644 index 000000000..1cef7541f --- /dev/null +++ b/tests/page-templates/11-restiming/05-no-beacon-url.html @@ -0,0 +1,42 @@ +<%= header %> + +<%= boomerangScript %> + +<%= footer %> diff --git a/tests/page-templates/11-restiming/05-no-beacon-url.js b/tests/page-templates/11-restiming/05-no-beacon-url.js new file mode 100644 index 000000000..7f6e12609 --- /dev/null +++ b/tests/page-templates/11-restiming/05-no-beacon-url.js @@ -0,0 +1,26 @@ +/* eslint-env mocha */ +/* global assert */ + +describe("e2e/11-restiming/05-no-beacon-url", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should get 3 beacons: 1 onload, 2 xhr (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 3); + }); + }); + + it("None of the beacons should have the beacon URL in restiming", function() { + if (!window.BOOMR.plugins.AutoXHR) { + return this.skip(); + } + + for (var i = 0; i < 3; i++) { + assert.notInclude(tf.beacons[i].restiming, "blackhole"); + } + }); +}); diff --git a/tests/page-templates/11-restiming/06-iframes.html b/tests/page-templates/11-restiming/06-iframes.html new file mode 100644 index 000000000..5186d58c3 --- /dev/null +++ b/tests/page-templates/11-restiming/06-iframes.html @@ -0,0 +1,16 @@ +<%= header %> +<%= boomerangScript %> + + + + + + +<%= footer %> diff --git a/tests/page-templates/11-restiming/06-iframes.js b/tests/page-templates/11-restiming/06-iframes.js new file mode 100644 index 000000000..460af56e9 --- /dev/null +++ b/tests/page-templates/11-restiming/06-iframes.js @@ -0,0 +1,74 @@ +/* eslint-env mocha */ +/* eslint-disable no-loop-func */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["ResourceTimingDecompression"]); + +describe("e2e/11-restiming/06-iframes", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have a restiming parameter on the beacon (if ResourceTiming is supported)", function() { + if (t.isResourceTimingSupported()) { + var b = tf.lastBeacon(); + + assert.isDefined(b.restiming); + } + else { + this.skip(); + } + }); + + it("Should have all of the resouces on the page", function() { + if (t.isResourceTimingSupported()) { + var b = tf.lastBeacon(); + + ResourceTimingDecompression.HOSTNAMES_REVERSED = false; + var resources = ResourceTimingDecompression.decompressResources(JSON.parse(b.restiming)); + var pageResources = window.performance.getEntriesByType("resource"); + + for (var i = 0; i < pageResources.length; i++) { + var url = pageResources[i].name; + + // ideally, we should skip anything in RT that is newer than our beacon + // skip beacon, boomerang, & config URLs + if (url.indexOf(BOOMR.getBeaconURL()) !== -1 || url === BOOMR.url || url === BOOMR.config_url) { + continue; + } + + // skip favicon which is requested after beacon + if (url.indexOf("/favicon.ico") !== -1) { + continue; + } + + assert.isDefined(BOOMR.utils.arrayFind(resources, function(r) { + return r.name === url; + }), "Finding " + url); + } + } + else { + this.skip(); + } + }); + + it("Should have the IMG in the IFRAME", function() { + if (t.isResourceTimingSupported()) { + var b = tf.lastBeacon(); + + ResourceTimingDecompression.HOSTNAMES_REVERSED = false; + var resources = ResourceTimingDecompression.decompressResources(JSON.parse(b.restiming)); + + assert.isDefined(BOOMR.utils.arrayFind(resources, function(r) { + return r.name.indexOf("/assets/img.jpg?iframe") !== -1; + }), "Finding /assets/img.jpg?iframe in the IFRAME"); + } + else { + this.skip(); + } + }); +}); diff --git a/tests/page-templates/11-restiming/06-svg-image.html b/tests/page-templates/11-restiming/06-svg-image.html new file mode 100644 index 000000000..c219c472b --- /dev/null +++ b/tests/page-templates/11-restiming/06-svg-image.html @@ -0,0 +1,19 @@ +<%= header %> +<%= boomerangSnippet %> + + + + + + + + +<%= footer %> diff --git a/tests/page-templates/11-restiming/06-svg-image.js b/tests/page-templates/11-restiming/06-svg-image.js new file mode 100644 index 000000000..f86dbee7b --- /dev/null +++ b/tests/page-templates/11-restiming/06-svg-image.js @@ -0,0 +1,118 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["ResourceTimingDecompression"]); + +describe("e2e/11-restiming/06-svg-image", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + function findSvgImage(resources) { + for (var i = 0; i < resources.length; i++) { + if (resources[i].name.indexOf("/assets/img.jpg") !== -1) { + return resources[i]; + } + } + + return undefined; + } + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should found the SVG:image element", function() { + if (t.isResourceTimingSupported()) { + var b = tf.beacons[0]; + + var resources = ResourceTimingDecompression.decompressResources(JSON.parse(b.restiming)); + var img = findSvgImage(resources); + + assert.isDefined(img, "Image is not null"); + } + else { + this.skip(); + } + }); + + it("Should have set the SVG:image initiatorType to IMAGE (or IMG, OTHER)", function() { + if (t.isResourceTimingSupported()) { + var b = tf.beacons[0]; + + var resources = ResourceTimingDecompression.decompressResources(JSON.parse(b.restiming)); + var img = findSvgImage(resources); + + if (t.isFirefox()) { + // Firefox initiator type may change depending on cache or response content-type? + // Looks to be `other` (soft reload), `img` (first hit) or `image` (hard reload) + assert.isTrue(BOOMR.utils.inArray(img.initiatorType, ["image", "img", "other"])); + } + else { + // `img` for IE & Edge + // `image` or `img` for Chrome + // `image` for Safari + assert.isTrue(BOOMR.utils.inArray(img.initiatorType, ["image", "img"])); + } + } + else { + this.skip(); + } + }); + + it("Should have captured the SVG:image element height", function() { + if (t.isResourceTimingSupported()) { + var b = tf.beacons[0]; + + var resources = ResourceTimingDecompression.decompressResources(JSON.parse(b.restiming)); + var img = findSvgImage(resources); + + assert.equal(img.height, 200); + } + else { + this.skip(); + } + }); + + it("Should have captured the SVG:image element width", function() { + if (t.isResourceTimingSupported()) { + var b = tf.beacons[0]; + + var resources = ResourceTimingDecompression.decompressResources(JSON.parse(b.restiming)); + var img = findSvgImage(resources); + + assert.equal(img.width, 400); + } + else { + this.skip(); + } + }); + + it("Should have captured the SVG:image element top", function() { + if (t.isResourceTimingSupported()) { + var b = tf.beacons[0]; + + var resources = ResourceTimingDecompression.decompressResources(JSON.parse(b.restiming)); + var img = findSvgImage(resources); + + assert.operator(img.y, ">=", 20); + } + else { + this.skip(); + } + }); + + it("Should have captured the SVG:image element left", function() { + if (t.isResourceTimingSupported()) { + var b = tf.beacons[0]; + + var resources = ResourceTimingDecompression.decompressResources(JSON.parse(b.restiming)); + var img = findSvgImage(resources); + + assert.operator(img.x, ">=", 10); + } + else { + this.skip(); + } + }); +}); diff --git a/tests/page-templates/11-restiming/06-type-filter.html b/tests/page-templates/11-restiming/06-type-filter.html new file mode 100644 index 000000000..e47fe2a34 --- /dev/null +++ b/tests/page-templates/11-restiming/06-type-filter.html @@ -0,0 +1,16 @@ +<%= header %> +<%= boomerangSnippet %> + + + + + +<%= footer %> diff --git a/tests/page-templates/11-restiming/06-type-filter.js b/tests/page-templates/11-restiming/06-type-filter.js new file mode 100644 index 000000000..e141a97a5 --- /dev/null +++ b/tests/page-templates/11-restiming/06-type-filter.js @@ -0,0 +1,30 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["ResourceTimingDecompression"]); + +describe("e2e/11-restiming/06-type-filter", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should only have the one IMG in the filter (if ResourceTiming is supported)", function() { + if (t.isResourceTimingSupported()) { + var b = tf.lastBeacon(); + + var resources = ResourceTimingDecompression.decompressResources(JSON.parse(b.restiming)); + + // find our img + assert.equal(resources.length, 1); + assert.equal(resources[0].initiatorType, "img"); + assert.include(resources[0].name, "img.jpg"); + } + else { + this.skip(); + } + }); +}); diff --git a/tests/page-templates/11-restiming/07-page-ready-held.html b/tests/page-templates/11-restiming/07-page-ready-held.html new file mode 100644 index 000000000..dfde7d278 --- /dev/null +++ b/tests/page-templates/11-restiming/07-page-ready-held.html @@ -0,0 +1,63 @@ +<%= header %> +<%= boomerangScript %> + + + + +<%= footer %> diff --git a/tests/page-templates/11-restiming/07-page-ready-held.js b/tests/page-templates/11-restiming/07-page-ready-held.js new file mode 100644 index 000000000..b3b751072 --- /dev/null +++ b/tests/page-templates/11-restiming/07-page-ready-held.js @@ -0,0 +1,36 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["ResourceTimingDecompression"]); + +describe("e2e/11-restiming/07-page-ready-held", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should include img.jpg in the ResourceTiming data", function() { + if (t.isResourceTimingSupported()) { + var b = tf.beacons[0]; + + var resources = ResourceTimingDecompression.decompressResources(JSON.parse(b.restiming)); + + var found = false; + + for (var i = 0; i < resources.length; i++) { + if (resources[i].name.indexOf("/assets/img.jpg") !== -1) { + found = true; + break; + } + } + + assert.isTrue(found, "Found img.jpg"); + } + else { + this.skip(); + } + }); +}); diff --git a/tests/page-templates/11-restiming/07-script-attrs.html b/tests/page-templates/11-restiming/07-script-attrs.html new file mode 100644 index 000000000..c43cbefff --- /dev/null +++ b/tests/page-templates/11-restiming/07-script-attrs.html @@ -0,0 +1,34 @@ +<%= header %> +<%= boomerangSnippet %> + + + + + + + + + + + + +<%= footer %> diff --git a/tests/page-templates/11-restiming/07-script-attrs.js b/tests/page-templates/11-restiming/07-script-attrs.js new file mode 100644 index 000000000..1b9a007d3 --- /dev/null +++ b/tests/page-templates/11-restiming/07-script-attrs.js @@ -0,0 +1,107 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["ResourceTimingDecompression", "s"]); + +describe("e2e/11-restiming/07-script-attrs", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + function getInterestingScripts(resources) { + var interesting = []; + + for (var i = 0; i < resources.length; i++) { + if (resources[i].name.indexOf("/mocha.js") !== -1 || resources[i].name.match(/\/07-script-attrs(\.\w+)?\.js$/)) { + interesting.push(resources[i]); + } + } + + return interesting; + } + + it("Should pass basic beacon validation", function(done){ + t.validateBeaconWasSent(done); + }); + + it("Should find the script elements", function() { + if (t.isResourceTimingSupported()) { + var b = tf.beacons[0]; + + var resources = ResourceTimingDecompression.decompressResources(JSON.parse(b.restiming)); + + var interesting = getInterestingScripts(resources); + + assert.strictEqual(interesting.length, 7); + } + else { + this.skip(); + } + }); + + it("Should find attributes for script elements", function() { + if (t.isResourceTimingSupported()) { + var RT = BOOMR.plugins.ResourceTiming; + + var SCRIPT_ATTR_EXPR = new RegExp("^.*\\" + RT.SPECIAL_DATA_PREFIX + RT.SPECIAL_DATA_SCRIPT_ATTR_TYPE); + + function assertBodyAsync(data) { + assert.match(data, SCRIPT_ATTR_EXPR); + data = parseInt(data.replace(SCRIPT_ATTR_EXPR, ""), 10); + assert.strictEqual(data & RT.ASYNC_ATTR, RT.ASYNC_ATTR); + assert.strictEqual(data & RT.DEFER_ATTR, 0); + assert.strictEqual(data & RT.LOCAT_ATTR, RT.LOCAT_ATTR); + } + + var b = tf.beacons[0]; + + var trie = JSON.parse(b.restiming); + var interesting = trie[location.protocol + "//" + location.host + "/"]["pages/11-restiming/"]; + + var js_head_static = trie[location.protocol + "//" + location.host + "/"]["vendor/"]["mocha/"]["mocha.js"]; + var js_body_static = interesting["07-script-attrs.js"]; + var js_body_async = interesting["support/"]["07-script-attrs.bodyasync.js"]; + var js_body_defer = interesting["support/"]["07-script-attrs.defer.js"]; + var js_head_async = interesting["support/"]["07-script-attrs.async.js"]; + var js_head_asfer = interesting["support/"]["07-script-attrs.asfer.js"]; + var js_link_async = interesting["support/"]["07-script-attrs.linkasync.js"]; + + assert.match(js_head_static, SCRIPT_ATTR_EXPR); + js_head_static = parseInt(js_head_static.replace(SCRIPT_ATTR_EXPR, ""), 10); + assert.strictEqual(js_head_static & RT.ASYNC_ATTR, 0); + assert.strictEqual(js_head_static & RT.DEFER_ATTR, 0); + assert.strictEqual(js_head_static & RT.LOCAT_ATTR, 0); + + assert.match(js_head_async, SCRIPT_ATTR_EXPR); + js_head_async = parseInt(js_head_async.replace(SCRIPT_ATTR_EXPR, ""), 10); + assert.strictEqual(js_head_async & RT.ASYNC_ATTR, RT.ASYNC_ATTR); + assert.strictEqual(js_head_async & RT.DEFER_ATTR, 0); + assert.strictEqual(js_head_async & RT.LOCAT_ATTR, 0); + + assert.match(js_head_asfer, SCRIPT_ATTR_EXPR); + js_head_asfer = parseInt(js_head_asfer.replace(SCRIPT_ATTR_EXPR, ""), 10); + assert.strictEqual(js_head_asfer & RT.ASYNC_ATTR, RT.ASYNC_ATTR); + assert.strictEqual(js_head_asfer & RT.DEFER_ATTR, RT.DEFER_ATTR); + assert.strictEqual(js_head_asfer & RT.LOCAT_ATTR, 0); + + assert.match(js_body_static, SCRIPT_ATTR_EXPR); + js_body_static = parseInt(js_body_static.replace(SCRIPT_ATTR_EXPR, ""), 10); + assert.strictEqual(js_body_static & RT.ASYNC_ATTR, 0); + assert.strictEqual(js_body_static & RT.DEFER_ATTR, 0); + assert.strictEqual(js_body_static & RT.LOCAT_ATTR, RT.LOCAT_ATTR); + + assertBodyAsync(js_body_async); + + assert.match(js_body_defer, SCRIPT_ATTR_EXPR); + js_body_defer = parseInt(js_body_defer.replace(SCRIPT_ATTR_EXPR, ""), 10); + assert.strictEqual(js_body_defer & RT.ASYNC_ATTR, 0); + assert.strictEqual(js_body_defer & RT.DEFER_ATTR, RT.DEFER_ATTR); + assert.strictEqual(js_body_defer & RT.LOCAT_ATTR, RT.LOCAT_ATTR); + + assertBodyAsync(js_link_async); + } + else { + this.skip(); + } + }); +}); diff --git a/tests/page-templates/11-restiming/08-cross-origin.html b/tests/page-templates/11-restiming/08-cross-origin.html new file mode 100644 index 000000000..bda07c93e --- /dev/null +++ b/tests/page-templates/11-restiming/08-cross-origin.html @@ -0,0 +1,28 @@ +<%= header %> +<%= boomerangScript %> + + + + + + + + + + + + +<%= footer %> diff --git a/tests/page-templates/11-restiming/08-cross-origin.js b/tests/page-templates/11-restiming/08-cross-origin.js new file mode 100644 index 000000000..d36ca9197 --- /dev/null +++ b/tests/page-templates/11-restiming/08-cross-origin.js @@ -0,0 +1,127 @@ +/* eslint-env mocha */ +/* eslint-disable no-loop-func */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["ResourceTimingDecompression", "host", "port", "crossOriginHost"]); + +describe("e2e/11-restiming/08-cross-origin", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have a restiming parameter on the beacon (if ResourceTiming is supported)", function() { + if (t.isResourceTimingSupported()) { + var b = tf.lastBeacon(); + + assert.isDefined(b.restiming); + } + else { + this.skip(); + } + }); + + it("Should have all of the resouces on the page", function() { + if (t.isResourceTimingSupported()) { + var b = tf.lastBeacon(); + + ResourceTimingDecompression.HOSTNAMES_REVERSED = false; + var resources = ResourceTimingDecompression.decompressResources(JSON.parse(b.restiming)); + var pageResources = window.performance.getEntriesByType("resource"); + + for (var i = 0; i < pageResources.length; i++) { + var url = pageResources[i].name; + + // skip beacon, boomerang, & config URLs + if (url.indexOf(BOOMR.getBeaconURL()) !== -1 || url === BOOMR.url || url === BOOMR.config_url) { + continue; + } + + // skip favicon + if (url.indexOf("favicon.ico") !== -1) { + continue; + } + + // skip about:blank (IE 11) + if (url.indexOf("about:blank") !== -1) { + continue; + } + + assert.isDefined(BOOMR.utils.arrayFind(resources, function(r) { + return r.name === url; + }), "Finding " + url); + } + } + else { + this.skip(); + } + }); + + it("Should have the same-origin IFRAME", function() { + if (t.isResourceTimingSupported()) { + var b = tf.lastBeacon(); + + ResourceTimingDecompression.HOSTNAMES_REVERSED = false; + var resources = ResourceTimingDecompression.decompressResources(JSON.parse(b.restiming)); + + assert.isDefined(BOOMR.utils.arrayFind(resources, function(r) { + return r.name.indexOf("support/iframe.html?same-origin") !== -1; + }), "Finding support/iframe.html?same-origin"); + } + else { + this.skip(); + } + }); + + it("Should have the IMG from the same-origin IFRAME", function() { + if (t.isResourceTimingSupported()) { + var b = tf.lastBeacon(); + + ResourceTimingDecompression.HOSTNAMES_REVERSED = false; + var resources = ResourceTimingDecompression.decompressResources(JSON.parse(b.restiming)); + + assert.isDefined(BOOMR.utils.arrayFind(resources, function(r) { + return r.name.indexOf("/assets/img.jpg?iframe") !== -1; + }), "Finding /assets/img.jpg?iframe in the IFRAME"); + } + else { + this.skip(); + } + }); + + it("Should have the cross-origin IFRAME", function() { + if (t.isResourceTimingSupported()) { + var b = tf.lastBeacon(); + + ResourceTimingDecompression.HOSTNAMES_REVERSED = false; + var resources = ResourceTimingDecompression.decompressResources(JSON.parse(b.restiming)); + + assert.isDefined(BOOMR.utils.arrayFind(resources, function(r) { + return r.name.indexOf("support/iframe.html?cross-origin") !== -1; + }), "Finding support/iframe.html?cross-origin"); + } + else { + this.skip(); + } + }); + + it("Should not have the IMG from the cross-origin IFRAME", function() { + if (t.isResourceTimingSupported()) { + var b = tf.lastBeacon(); + + ResourceTimingDecompression.HOSTNAMES_REVERSED = false; + var resources = ResourceTimingDecompression.decompressResources(JSON.parse(b.restiming)); + + assert.isUndefined(BOOMR.utils.arrayFind(resources, function(r) { + return r.name.indexOf("/assets/img.jpg?iframe") !== -1 && + r.name.indexOf(window.crossOriginHost) !== -1; + }), "Not finding /assets/img.jpg?iframe from cross-origin iframe"); + } + else { + this.skip(); + } + }); +}); diff --git a/tests/page-templates/11-restiming/09-link-attrs.html b/tests/page-templates/11-restiming/09-link-attrs.html new file mode 100644 index 000000000..26169d6db --- /dev/null +++ b/tests/page-templates/11-restiming/09-link-attrs.html @@ -0,0 +1,22 @@ +<%= header %> +<%= boomerangSnippet %> + + + + + + + + + + + + +<%= footer %> diff --git a/tests/page-templates/11-restiming/09-link-attrs.js b/tests/page-templates/11-restiming/09-link-attrs.js new file mode 100644 index 000000000..9231dc8e4 --- /dev/null +++ b/tests/page-templates/11-restiming/09-link-attrs.js @@ -0,0 +1,119 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["ResourceTimingDecompression"]); + +describe("e2e/11-restiming/09-link-attrs", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + function assertLinkRel(data, expectedRel) { + var RT = BOOMR.plugins.ResourceTiming; + var LINK_ATTR_EXPR = new RegExp("^.*\\" + RT.SPECIAL_DATA_PREFIX + RT.SPECIAL_DATA_LINK_ATTR_TYPE + "(\\d)"); + + assert.match(data, LINK_ATTR_EXPR); + assert.strictEqual(data.match(LINK_ATTR_EXPR)[1], String(expectedRel)); + } + + function getInteresting() { + var b = tf.beacons[0]; + var trie = JSON.parse(b.restiming); + + return trie[location.protocol + "//" + location.host + "/"]["pages/11-restiming/"]["support/"]; + } + + it("Should pass basic beacon validation", function(done){ + t.validateBeaconWasSent(done); + }); + + it("Should find the link elements", function() { + if (!t.isResourceTimingSupported()) { + this.skip(); + + return; + } + + var b = tf.beacons[0]; + + ResourceTimingDecompression.HOSTNAMES_REVERSED = false; + var resources = ResourceTimingDecompression.decompressResources(JSON.parse(b.restiming)); + var foundURLs = resources.reduce(function(arr, r) { + arr.push(r.name); + + return arr; + }, []); + + var a = document.createElement("a"); + var expectedURLs = [ + "./support/09.js", + "./support/09.jpg", + "./support/09.css" + ].reduce(function(arr, url) { + a.href = url; + arr.push(a.href); + + return arr; + }, []); + + assert.includeMembers(foundURLs, expectedURLs); + }); + + it("Should find stylesheet `rel` for link elements", function() { + if (!t.isResourceTimingSupported()) { + this.skip(); + + return; + } + + var interesting = getInteresting(); + + assertLinkRel(interesting["09.css"], BOOMR.plugins.ResourceTiming.REL_TYPES.stylesheet); + }); + + it("Should find script `rel` for link elements", function() { + if (!t.isResourceTimingSupported()) { + return this.skip(); + } + + var interesting = getInteresting(); + + var a = document.createElement("a"); + + a.href = "./support/09.js"; + var resource = t.findFirstResource(a.href); + + if (resource.initiatorType === "link") { + // Chrome sets initiatorType to link + assertLinkRel(interesting["09.js"], BOOMR.plugins.ResourceTiming.REL_TYPES.preload); + } + else { + // FF, Edge and Safari set initiatorType to script + this.skip(); + } + }); + + it("Should find image `rel` for link elements", function() { + if (!t.isResourceTimingSupported()) { + this.skip(); + + return; + } + + var interesting = getInteresting(); + + var a = document.createElement("a"); + + a.href = "./support/09.jpg"; + var resource = t.findFirstResource(a.href); + + if (resource.initiatorType === "link") { + // Chrome sets initiatorType to link + assertLinkRel(interesting["09.jpg"], BOOMR.plugins.ResourceTiming.REL_TYPES.preload); + } + else { + // FF, Edge and Safari set initiatorType to img + this.skip(); + } + }); +}); diff --git a/tests/page-templates/11-restiming/09-server-timing.html b/tests/page-templates/11-restiming/09-server-timing.html new file mode 100644 index 000000000..efd5ee2e0 --- /dev/null +++ b/tests/page-templates/11-restiming/09-server-timing.html @@ -0,0 +1,14 @@ +<%= header %> +<%= boomerangSnippet %> + + + + +<%= footer %> diff --git a/tests/page-templates/11-restiming/09-server-timing.html.headers b/tests/page-templates/11-restiming/09-server-timing.html.headers new file mode 100644 index 000000000..eadae920c --- /dev/null +++ b/tests/page-templates/11-restiming/09-server-timing.html.headers @@ -0,0 +1 @@ +Server-Timing: metric1;dur=1;desc="for 09-server-timing.html", metric2;dur=2;desc="for 09-server-timing.html" diff --git a/tests/page-templates/11-restiming/09-server-timing.js b/tests/page-templates/11-restiming/09-server-timing.js new file mode 100644 index 000000000..a04658da6 --- /dev/null +++ b/tests/page-templates/11-restiming/09-server-timing.js @@ -0,0 +1,43 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/11-restiming/09-server-timing", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should pass basic beacon validation", function(done){ + t.validateBeaconWasSent(done); + }); + + it("Should have optimized server timing entries as `servertiming`", function() { + if (!t.isServerTimingSupported()) { + this.skip(); + + return; + } + + // because of other potential server timing entries in the page (express-middleware-server-timing), + // we can't do an exact equality check + var b = tf.beacons[0]; + + assert.match(b.servertiming, + /~\(~'metric1~'for\*20spacer.gif~'for\*2009-server-timing.html~'for\*20st-iframe.html\)~\(~'metric3~'for\*20spacer.gif\)~\(~'metric2~'for\*2009-server-timing.html\)~\(~'metric4~'for\*20st-iframe.html\)/); + }); + + it("Should have server timing data on `restiming`", function() { + if (!t.isServerTimingSupported()) { + this.skip(); + + return; + } + + var b = tf.beacons[0]; + var hasMiddlewareEntry = typeof performance.getEntriesByType("navigation")[0].serverTiming.find(function(e) { + return (e.name || e.metric) === "mw"; + }) !== "undefined"; + + assert.match(b.restiming, !hasMiddlewareEntry ? /\*31:.1,2:2/ : /\*31:1.1,2:3/); + assert.match(b.restiming, !hasMiddlewareEntry ? /\*31:.2,4:3/ : /\*31:1.2,4:4/); + assert.lengthOf(b.restiming.match(!hasMiddlewareEntry ? /\*31,3:1/g : /\*31:1,3:2/g), 2); // should match twice + }); +}); diff --git a/tests/page-templates/11-restiming/10-img-srcset.html b/tests/page-templates/11-restiming/10-img-srcset.html new file mode 100644 index 000000000..d810aa105 --- /dev/null +++ b/tests/page-templates/11-restiming/10-img-srcset.html @@ -0,0 +1,38 @@ +<%= header %> +<%= boomerangSnippet %> + + + + + + +<%= footer %> diff --git a/tests/page-templates/11-restiming/10-img-srcset.js b/tests/page-templates/11-restiming/10-img-srcset.js new file mode 100644 index 000000000..81c879e82 --- /dev/null +++ b/tests/page-templates/11-restiming/10-img-srcset.js @@ -0,0 +1,89 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["ResourceTimingDecompression", "widths"]); + +describe("e2e/11-restiming/10-img-srcset", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + var issupported = !!document.createElement("PICTURE").constructor.toString().match(/HTMLPictureElement/); + + function getExpectedImage() { + var img = document.getElementsByTagName("img")[0]; + var src = img.currentSrc.replace(/^.*\/(support\/)/, "$1"); + + var srcset = img.srcset.split(/\s*,\s*/m).map(function(x) { + return x.split(/ +/)[0]; + }); + + for (var s = 0; s < srcset.length - 1; s++) { + if (src === srcset[s]) { + return [src, widths[s], img.clientWidth || img.offsetWidth]; + } + } + + return [src, widths[s], img.clientWidth || img.offsetWidth]; + } + + function findSrcSetImage(resources) { + var imgtuple = getExpectedImage(); + + for (var i = 0; i < resources.length; i++) { + if (resources[i].name.indexOf(imgtuple[0]) !== -1) { + return resources[i]; + } + } + + return undefined; + } + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should find the IMG element for screen width " + screen.width + "px and dpr " + window.devicePixelRatio, function() { + if (t.isResourceTimingSupported() && screen.width && issupported) { + var b = tf.beacons[0]; + + ResourceTimingDecompression.HOSTNAMES_REVERSED = false; + var resources = ResourceTimingDecompression.decompressResources(JSON.parse(b.restiming)); + var img = findSrcSetImage(resources); + + assert.isDefined(img, "Image is defined"); + } + else { + this.skip(); + } + }); + + it("Should have captured the IMG element width for screen width " + screen.width + "px and dpr " + window.devicePixelRatio, function() { + if (t.isResourceTimingSupported() && screen.width && issupported) { + var b = tf.beacons[0]; + + ResourceTimingDecompression.HOSTNAMES_REVERSED = false; + var resources = ResourceTimingDecompression.decompressResources(JSON.parse(b.restiming)); + var img = findSrcSetImage(resources); + + assert.equal(img.width, getExpectedImage()[2]); + } + else { + this.skip(); + } + }); + + it("Should have captured the IMG element naturalWidth for screen width " + screen.width + "px and dpr " + window.devicePixelRatio, function() { + if (t.isResourceTimingSupported() && screen.width && issupported) { + var b = tf.beacons[0]; + + ResourceTimingDecompression.HOSTNAMES_REVERSED = false; + var resources = ResourceTimingDecompression.decompressResources(JSON.parse(b.restiming)); + var img = findSrcSetImage(resources); + + assert.closeTo(img.naturalWidth, getExpectedImage()[2], 1); // without getSrcsetDimensions, we'll have the clientWidth + } + else { + this.skip(); + } + }); +}); diff --git a/tests/page-templates/11-restiming/11-picture.html b/tests/page-templates/11-restiming/11-picture.html new file mode 100644 index 000000000..aae0f2202 --- /dev/null +++ b/tests/page-templates/11-restiming/11-picture.html @@ -0,0 +1,18 @@ +<%= header %> +<%= boomerangSnippet %> + + + + + + + + +<%= footer %> diff --git a/tests/page-templates/11-restiming/11-picture.js b/tests/page-templates/11-restiming/11-picture.js new file mode 100644 index 000000000..6b5fb4207 --- /dev/null +++ b/tests/page-templates/11-restiming/11-picture.js @@ -0,0 +1,65 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["ResourceTimingDecompression"]); + +describe("e2e/11-restiming/11-picture", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + var issupported = !!document.createElement("PICTURE").constructor.toString().match(/HTMLPictureElement/); + + function findSrcSetImage(resources, width) { + for (var i = 0; i < resources.length; i++) { + if (resources[i].name.indexOf("/support/09-" + width + ".jpg") !== -1 && resources[i].hasOwnProperty("width")) { + return resources[i]; + } + } + + return undefined; + } + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should find the PICTURE elements", function() { + if (t.isResourceTimingSupported() && issupported) { + var b = tf.beacons[0]; + + var resources = ResourceTimingDecompression.decompressResources(JSON.parse(b.restiming)); + var img = findSrcSetImage(resources, 320); + + assert.isDefined(img, "Image 320 is undefined"); + + img = findSrcSetImage(resources, 480); + assert.isDefined(img, "Image 480 is undefined"); + + img = findSrcSetImage(resources, 800); + assert.isDefined(img, "Image 800 is undefined"); + } + else { + this.skip(); + } + }); + + it("Should have captured the PICTURE element width", function() { + if (t.isResourceTimingSupported() && issupported) { + var b = tf.beacons[0]; + + var resources = ResourceTimingDecompression.decompressResources(JSON.parse(b.restiming)); + var img = findSrcSetImage(resources, 320); + + assert.equal(img.width, 320); + + img = findSrcSetImage(resources, 480); + assert.equal(img.width, 480); + + img = findSrcSetImage(resources, 800); + assert.equal(img.width, 800); + } + else { + this.skip(); + } + }); +}); diff --git a/tests/page-templates/11-restiming/12-clearresourcetiming.html b/tests/page-templates/11-restiming/12-clearresourcetiming.html new file mode 100644 index 000000000..a691973bd --- /dev/null +++ b/tests/page-templates/11-restiming/12-clearresourcetiming.html @@ -0,0 +1,43 @@ +<%= header %> +<%= boomerangSnippet %> + + + +<%= footer %> diff --git a/tests/page-templates/11-restiming/12-clearresourcetiming.js b/tests/page-templates/11-restiming/12-clearresourcetiming.js new file mode 100644 index 000000000..7769e4b54 --- /dev/null +++ b/tests/page-templates/11-restiming/12-clearresourcetiming.js @@ -0,0 +1,29 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["ResourceTimingDecompression"]); + +describe("e2e/11-restiming/12-clearresourcetiming", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should find both scripts", function() { + if (!t.isResourceTimingSupported()) { + return this.skip(); + } + + var b = tf.lastBeacon(); + var entries = ResourceTimingDecompression.decompressResources(JSON.parse(b.restiming)); + + BOOMR.utils.forEach(["one.js", "two.js"], function(file) { + assert.isDefined(BOOMR.utils.arrayFind(entries, function(e){ + return e.name.indexOf(file) > -1; + }), "can't find: " + file); + }); + }); +}); diff --git a/tests/page-templates/11-restiming/13-addresources.html b/tests/page-templates/11-restiming/13-addresources.html new file mode 100644 index 000000000..3e5f80703 --- /dev/null +++ b/tests/page-templates/11-restiming/13-addresources.html @@ -0,0 +1,71 @@ +<%= header %> +<%= boomerangSnippet %> + + + +<%= footer %> diff --git a/tests/page-templates/11-restiming/13-addresources.js b/tests/page-templates/11-restiming/13-addresources.js new file mode 100644 index 000000000..27e7a78a0 --- /dev/null +++ b/tests/page-templates/11-restiming/13-addresources.js @@ -0,0 +1,68 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["ResourceTimingDecompression"]); + +describe("e2e/11-restiming/13-addresources", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should decompress correctly", function() { + if (!t.isResourceTimingSupported()) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + ResourceTimingDecompression.HOSTNAMES_REVERSED = false; + var entries = ResourceTimingDecompression.decompressResources(JSON.parse(b.restiming)); + + var spacer = location.protocol + "//" + location.host + "/pages/11-restiming/support/spacer.gif"; + + var interesting = {}; + + interesting[spacer] = []; + interesting["http://example.com/script1.js"] = []; + interesting["http://example.com/script2.js"] = []; + + entries.forEach(function(entry) { + if (interesting[entry.name]) { + interesting[entry.name].push(entry); + } + }); + + // script1.js + assert.lengthOf(interesting["http://example.com/script1.js"], 1, "there should be 1 RT entry for script1.js"); + var entry = interesting["http://example.com/script1.js"][0]; + + assert.equal(entry.startTime, 300); + assert.equal(entry.initiatorType, "script"); + assert.isObject(entry._data); + assert.deepEqual(entry._data, {"ns5": "namespaced_payload_5"}); + + // script2.js + assert.lengthOf(interesting["http://example.com/script2.js"], 1, "there should be 1 RT entry for script2.js"); + var entry = interesting["http://example.com/script2.js"][0]; + + assert.equal(entry.startTime, 400); + assert.equal(entry.initiatorType, "script"); + assert.isObject(entry._data); + assert.deepEqual(entry._data, {"ns6": "namespaced_payload_6", "ns7": "namespaced_payload_7"}); + + // spacer.gif + assert.lengthOf(interesting[spacer], 1, "there should be 1 RT entry for spacer.gif"); + var entry = interesting[spacer][0]; + + assert.isObject(entry._data); + assert.deepEqual(entry._data, { + "ns1": ["namespaced_payload_1_a", "namespaced_payload_1_b"], + "ns2": "namespaced_payload_2", + "ns3": "namespaced_payload_3" + }); + }); +}); diff --git a/tests/page-templates/11-restiming/14-splitatpath.html b/tests/page-templates/11-restiming/14-splitatpath.html new file mode 100644 index 000000000..104c5d527 --- /dev/null +++ b/tests/page-templates/11-restiming/14-splitatpath.html @@ -0,0 +1,16 @@ +<%= header %> +<%= boomerangSnippet %> + + + + + +<%= footer %> diff --git a/tests/page-templates/11-restiming/14-splitatpath.js b/tests/page-templates/11-restiming/14-splitatpath.js new file mode 100644 index 000000000..d783b0579 --- /dev/null +++ b/tests/page-templates/11-restiming/14-splitatpath.js @@ -0,0 +1,42 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["ResourceTimingDecompression"]); + +describe("e2e/11-restiming/14-splitatpath", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have a restiming parameter on the beacon (if ResourceTiming is supported)", function() { + if (t.isResourceTimingSupported()) { + var b = tf.lastBeacon(); + + assert.isDefined(b.restiming); + } + else { + this.skip(); + } + }); + + it("Should have split resources by path in the Trie (if ResourceTiming is supported)", function() { + if (t.isResourceTimingSupported()) { + var b = tf.lastBeacon(); + + // Should not have "assets/img.jpg?N" full paths in the Trie, they would + // be split at "assets/" and have both "img.jpg?1" and "img.jpg?2" instead of + // having "assets/img.jpg?" and split at "1" and "2" in the perfectly + // optimized case. + assert.notInclude(b.restiming, "assets/img.jpg"); + assert.include(b.restiming, "img.jpg?1"); + assert.include(b.restiming, "img.jpg?2"); + } + else { + this.skip(); + } + }); +}); diff --git a/tests/page-templates/11-restiming/15-initiator-types.html b/tests/page-templates/11-restiming/15-initiator-types.html new file mode 100644 index 000000000..1cda71b36 --- /dev/null +++ b/tests/page-templates/11-restiming/15-initiator-types.html @@ -0,0 +1,57 @@ +<%= header %> +<%= boomerangSnippet %> + + + + + + + + + + + + +<%= footer %> diff --git a/tests/page-templates/11-restiming/15-initiator-types.js b/tests/page-templates/11-restiming/15-initiator-types.js new file mode 100644 index 000000000..d784bae19 --- /dev/null +++ b/tests/page-templates/11-restiming/15-initiator-types.js @@ -0,0 +1,74 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["ResourceTimingDecompression"]); + +describe("e2e/11-restiming/15-initiator-types", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have a restiming parameter on the beacon (if ResourceTiming is supported)", function() { + if (t.isResourceTimingSupported()) { + var b = tf.lastBeacon(); + + assert.isDefined(b.restiming); + } + else { + this.skip(); + } + }); + + it("Should decompress all known resource's initiatorTypes correctly", function() { + if (!t.isResourceTimingSupported()) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + ResourceTimingDecompression.HOSTNAMES_REVERSED = false; + var entries = ResourceTimingDecompression.decompressResources(JSON.parse(b.restiming)); + + // there are initiatorType differences between browsers + var types = [ + ["boomerang-test-framework", ["script"]], + ["id=audio-src", ["audio"]], + ["id=video-src", ["video"]], + ["id=video-track", ["track"]], + ["id=video-track", ["track"]], + ["15-initiator-types.html", ["navigation", "html"]], // navigation in Chrome, sometimes html in FF + ["15-initiator-types.js", ["script"]], + ["id=img", ["img"]], + ["id=input", ["input", "img"]], // input in Chrome, img in FF + ["id=video-poster", ["video", "img"]], // video in Chrome, img in FF + ["id=object", ["object"]], + ["mocha.css", ["link"]], + ["mocha.js", ["script"]], + ["assertive-chai.js", ["script"]], + ["lodash.js", ["script"]], + ["resourcetiming-decompression.js", ["script"]], + ["common.js", ["script"]], + ["id=early-hints", ["early-hints"]], + ["id=ping", ["ping"]], + ["id=font", ["font"]] + ]; + + BOOMR.utils.forEach(types, function(type) { + var entry = BOOMR.utils.arrayFind(entries, function(e) { + return e.name.indexOf(type[0]) > -1; + }); + + // skip ones that don't exist + if (!entry) { + return; + } + + assert.isTrue(BOOMR.utils.inArray(entry.initiatorType, type[1].concat(["other"])), + type[0] + " in " + type[1].join() + ", was " + entry.initiatorType); + }); + }); +}); diff --git a/tests/page-templates/11-restiming/16-img-srcset-getdim.html b/tests/page-templates/11-restiming/16-img-srcset-getdim.html new file mode 100644 index 000000000..c3249d6b4 --- /dev/null +++ b/tests/page-templates/11-restiming/16-img-srcset-getdim.html @@ -0,0 +1,38 @@ +<%= header %> +<%= boomerangSnippet %> + + + + + + +<%= footer %> diff --git a/tests/page-templates/11-restiming/16-img-srcset-getdim.js b/tests/page-templates/11-restiming/16-img-srcset-getdim.js new file mode 100644 index 000000000..16d8b83e0 --- /dev/null +++ b/tests/page-templates/11-restiming/16-img-srcset-getdim.js @@ -0,0 +1,89 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["ResourceTimingDecompression", "widths"]); + +describe("e2e/11-restiming/16-img-srcset-getdim", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + var issupported = !!document.createElement("PICTURE").constructor.toString().match(/HTMLPictureElement/); + + function getExpectedImage() { + var img = document.getElementsByTagName("img")[0]; + var src = img.currentSrc.replace(/^.*\/(support\/)/, "$1"); + + var srcset = img.srcset.split(/\s*,\s*/m).map(function(x) { + return x.split(/ +/)[0]; + }); + + for (var s = 0; s < srcset.length - 1; s++) { + if (src === srcset[s]) { + return [src, widths[s], img.clientWidth || img.offsetWidth]; + } + } + + return [src, widths[s], img.clientWidth || img.offsetWidth]; + } + + function findSrcSetImage(resources) { + var imgtuple = getExpectedImage(); + + for (var i = 0; i < resources.length; i++) { + if (resources[i].name.indexOf(imgtuple[0]) !== -1) { + return resources[i]; + } + } + + return undefined; + } + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should find the IMG element for screen width " + screen.width + "px and dpr " + window.devicePixelRatio, function() { + if (t.isResourceTimingSupported() && screen.width && issupported) { + var b = tf.beacons[0]; + + ResourceTimingDecompression.HOSTNAMES_REVERSED = false; + var resources = ResourceTimingDecompression.decompressResources(JSON.parse(b.restiming)); + var img = findSrcSetImage(resources); + + assert.isDefined(img, "Image is defined"); + } + else { + this.skip(); + } + }); + + it("Should have captured the IMG element width for screen width " + screen.width + "px and dpr " + window.devicePixelRatio, function() { + if (t.isResourceTimingSupported() && screen.width && issupported) { + var b = tf.beacons[0]; + + ResourceTimingDecompression.HOSTNAMES_REVERSED = false; + var resources = ResourceTimingDecompression.decompressResources(JSON.parse(b.restiming)); + var img = findSrcSetImage(resources); + + assert.equal(img.width, getExpectedImage()[2]); + } + else { + this.skip(); + } + }); + + it("Should have captured the IMG element naturalWidth for screen width " + screen.width + "px and dpr " + window.devicePixelRatio, function() { + if (t.isResourceTimingSupported() && screen.width && issupported) { + var b = tf.beacons[0]; + + ResourceTimingDecompression.HOSTNAMES_REVERSED = false; + var resources = ResourceTimingDecompression.decompressResources(JSON.parse(b.restiming)); + var img = findSrcSetImage(resources); + + assert.equal(img.naturalWidth, getExpectedImage()[1]); + } + else { + this.skip(); + } + }); +}); diff --git a/tests/page-templates/11-restiming/17-service-worker-rs.html b/tests/page-templates/11-restiming/17-service-worker-rs.html new file mode 100644 index 000000000..e2a4340e8 --- /dev/null +++ b/tests/page-templates/11-restiming/17-service-worker-rs.html @@ -0,0 +1,14 @@ +<%= header %> +<%= boomerangScript %> + + + +<%= footer %> diff --git a/tests/page-templates/11-restiming/17-service-worker-rs.js b/tests/page-templates/11-restiming/17-service-worker-rs.js new file mode 100644 index 000000000..9489aaae7 --- /dev/null +++ b/tests/page-templates/11-restiming/17-service-worker-rs.js @@ -0,0 +1,99 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/11-restiming/17-service-worker-rs", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + var beaconData; + + it("Should have sent an unload beacon", function(done) { + var unloadBeaconHandler = function(data) { + beaconData = data; + assert.isString(beaconData["rt.quit"]); + done(); + }; + + var testFrame = document.getElementById("boomer_test_frame"); + + testFrame.contentWindow.BOOMR.subscribe("beacon", unloadBeaconHandler, null, this); + testFrame.src = "about:blank"; + }); + + it("Should have run all unload plugins for the unload beacon", function() { + assert.equal(beaconData.onunloadtest, 1); + }); + + it("Should have sent a load beacon for the iFrame on reload", function(done) { + var testFrame = document.getElementById("boomer_test_frame"); + + var onBeaconHandler = function(data) { + done(); + }; + + var onLoadHandler = function(data) { + if (testFrame.contentWindow.BOOMR.hasSentPageLoadBeacon()) { + // We already have beacon array ready for next step. continue + done(); + } + else { + // Page load hasnt happened. So let wait for that before proceeeding to + // next step. + testFrame.contentWindow.BOOMR.subscribe("beacon", onBeaconHandler, null, this); + } + }; + + // reloading the iFrame via "src" attribute kills the current Boomerang context + // and so we cant register for on_beacon event directly. Instead we need to + // register for load event on the iFrame's context object from where we can + // register for the on_beacon Boomerang event. This is need to ensure we wait for + // the beacon to be sent from the iFrame before proceeding to the next part where + // check the Beacons res timing. + + testFrame.onload = onLoadHandler; + testFrame.src = "support/17-iframe-sw.html"; + }); + + it("Should pass basic beacon validation", function(){ + var testFrame = document.getElementById("boomer_test_frame"); + + assert.equal(testFrame.contentWindow.BOOMR.plugins.TestFramework.beacons.length, 1); + }); + + it("Should have Service worker start time for the IMG on the page (if ResourceTiming is supported)", function() { + var testFrame = document.getElementById("boomer_test_frame"); + + if (testFrame && testFrame.contentWindow && + testFrame.contentWindow.BOOMR_test.isResourceTimingSupported() && + testFrame.contentWindow.BOOMR_test.isServiceWorkerSupported()) { + var b = testFrame.contentWindow.BOOMR.plugins.TestFramework.beacons[0]; + + var resTimingEntries = testFrame.contentWindow.performance.getEntriesByType("resource"); + + var imgEntry = resTimingEntries.filter(function(e) { + return e.initiatorType === "img"; + })[0]; + + if (imgEntry.workerStart && typeof imgEntry.workerStart === "number" && imgEntry.workerStart !== 0) { + // We have a non-zero service worker server resource timing. Lets proceed. + var workerStartRoundup = Math.ceil(imgEntry.workerStart ? imgEntry.workerStart : 0); + var startTime = Math.round(imgEntry.startTime ? imgEntry.startTime : 0); + var workerStartOffsetStartTime = (workerStartRoundup === 0 ? 0 : (workerStartRoundup - startTime)); + + var base36ConvertedWs = workerStartOffsetStartTime.toString(36); + var compressedWorkerStartVal = "*6" + (base36ConvertedWs === "0" ? "" : base36ConvertedWs); + + assert.include(b.restiming, compressedWorkerStartVal); + } + else { + // looks like Service worker isnt really supported even though the api is. We dont have + // service worker timestamps. So just skip this test. + this.skip(); + } + } + else { + // Service worker not supported by the browser. + this.skip(); + } + }); +}); diff --git a/tests/page-templates/11-restiming/18-nexthopprotocol.html b/tests/page-templates/11-restiming/18-nexthopprotocol.html new file mode 100644 index 000000000..93c62bdff --- /dev/null +++ b/tests/page-templates/11-restiming/18-nexthopprotocol.html @@ -0,0 +1,13 @@ +<%= header %> +<%= boomerangScript %> + + + +<%= footer %> diff --git a/tests/page-templates/11-restiming/18-nexthopprotocol.js b/tests/page-templates/11-restiming/18-nexthopprotocol.js new file mode 100644 index 000000000..018bc8c99 --- /dev/null +++ b/tests/page-templates/11-restiming/18-nexthopprotocol.js @@ -0,0 +1,44 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["ResourceTimingDecompression"]); + +describe("e2e/11-restiming/18-nexthopprotocol", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + var beaconData; + + it("Should have captured nextHopProtocol on each resource", function() { + if (t.isResourceTimingSupported()) { + var b = tf.beacons[0]; + + ResourceTimingDecompression.HOSTNAMES_REVERSED = false; + var resources = ResourceTimingDecompression.decompressResources(JSON.parse(b.restiming)); + + for (var i = 0; i < resources.length; i++) { + // find this JavaScript from ResourceTiming + var resourceTimingResource = t.findFirstResource(resources[i].name); + + if (resourceTimingResource === null) { + resourceTimingResource = performance.getEntriesByType("navigation")[0]; + } + + if (!resources[i].nextHopProtocol) { + return; + } + + assert.isNotNull(resourceTimingResource); + + assert.equal( + resourceTimingResource.nextHopProtocol, + resources[i].nextHopProtocol, + resources[i].name + " should have protocol " + resources[i].nextHopProtocol); + } + } + else { + this.skip(); + } + }); +}); diff --git a/tests/page-templates/11-restiming/support/07-elem-attrs.rel.css b/tests/page-templates/11-restiming/support/07-elem-attrs.rel.css new file mode 100644 index 000000000..e69de29bb diff --git a/tests/page-templates/11-restiming/support/07-script-attrs.asfer.js b/tests/page-templates/11-restiming/support/07-script-attrs.asfer.js new file mode 100644 index 000000000..e69de29bb diff --git a/tests/page-templates/11-restiming/support/07-script-attrs.async.js b/tests/page-templates/11-restiming/support/07-script-attrs.async.js new file mode 100644 index 000000000..e69de29bb diff --git a/tests/page-templates/11-restiming/support/07-script-attrs.bodyasync.js b/tests/page-templates/11-restiming/support/07-script-attrs.bodyasync.js new file mode 100644 index 000000000..e69de29bb diff --git a/tests/page-templates/11-restiming/support/07-script-attrs.defer.js b/tests/page-templates/11-restiming/support/07-script-attrs.defer.js new file mode 100644 index 000000000..e69de29bb diff --git a/tests/page-templates/11-restiming/support/07-script-attrs.linkasync.js b/tests/page-templates/11-restiming/support/07-script-attrs.linkasync.js new file mode 100644 index 000000000..e69de29bb diff --git a/tests/page-templates/11-restiming/support/09-320.jpg b/tests/page-templates/11-restiming/support/09-320.jpg new file mode 100644 index 000000000..e07c16825 Binary files /dev/null and b/tests/page-templates/11-restiming/support/09-320.jpg differ diff --git a/tests/page-templates/11-restiming/support/09-480.jpg b/tests/page-templates/11-restiming/support/09-480.jpg new file mode 100644 index 000000000..c0e5a8eb7 Binary files /dev/null and b/tests/page-templates/11-restiming/support/09-480.jpg differ diff --git a/tests/page-templates/11-restiming/support/09-800.jpg b/tests/page-templates/11-restiming/support/09-800.jpg new file mode 100644 index 000000000..0b68524b2 Binary files /dev/null and b/tests/page-templates/11-restiming/support/09-800.jpg differ diff --git a/tests/page-templates/11-restiming/support/09.css b/tests/page-templates/11-restiming/support/09.css new file mode 100644 index 000000000..e69de29bb diff --git a/tests/page-templates/11-restiming/support/09.jpg b/tests/page-templates/11-restiming/support/09.jpg new file mode 100644 index 000000000..de3a1f7e9 Binary files /dev/null and b/tests/page-templates/11-restiming/support/09.jpg differ diff --git a/tests/page-templates/11-restiming/support/09.js b/tests/page-templates/11-restiming/support/09.js new file mode 100644 index 000000000..e69de29bb diff --git a/tests/page-templates/11-restiming/support/11-picture.content.html b/tests/page-templates/11-restiming/support/11-picture.content.html new file mode 100644 index 000000000..d84d30c9c --- /dev/null +++ b/tests/page-templates/11-restiming/support/11-picture.content.html @@ -0,0 +1,38 @@ + + + +Testing picture source + + + + + + + diff --git a/tests/page-templates/11-restiming/support/17-iframe-sw.html b/tests/page-templates/11-restiming/support/17-iframe-sw.html new file mode 100644 index 000000000..4e7b6921f --- /dev/null +++ b/tests/page-templates/11-restiming/support/17-iframe-sw.html @@ -0,0 +1,70 @@ + + + + Generic Page w/ Service Worker scripts + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + diff --git a/tests/page-templates/11-restiming/support/17-service-worker.js b/tests/page-templates/11-restiming/support/17-service-worker.js new file mode 100644 index 000000000..b5b81c43f --- /dev/null +++ b/tests/page-templates/11-restiming/support/17-service-worker.js @@ -0,0 +1,66 @@ +var CACHE_NAME = "17-sw-cache"; +var urlsToCache = ["/pages/11-restiming/support/sm-image.jpg"]; + +self.addEventListener("install", function(event){ + event.waitUntil( + caches.open(CACHE_NAME) + .then(function(cache) { + console.log("Opened cache"); + + return cache.addAll(urlsToCache); + }) + ); +}); + +function handleSwImage(event) { + if (event && event.request.method === "GET" && event.request.url.includes("sm-image")) { + return caches.match(event.request).then(function(response) { + // cache hit, return response + if (response) { + console.log("Cache hit for request"); + + return response; + } + + console.log("Cache Miss"); + // Cache miss, request from the network and cache the results as well + + var result = fetch(event.request).then(function(r) { + // check if we got a valid response. + if (!r || !(r.status === 200 || r.status === 204) || r.type !== "basic") { + console.log("Invalid response"); + + return r; + } + + // clone the response so that we have one copy to be consumed by the cache + // while the other copy can be returned by to be consumed by the browser. + var resultCopy = r.clone(); + + console.log("Putting requested object in cache: " + event.request); + + // put a copy of the response in our cache for future requests. + caches.open(CACHE_NAME).then(function(cache) { + cache.put(event.request, resultCopy); + }); + + // Now return the other response stream to be consumed by the browser. + return r; + }); + + return result; + }); + } + else { + console.log("Not intercepting: " + event.request.url); + + return fetch(event.request); + } +} + +self.addEventListener("fetch", function(event){ + console.log("Got fetch: " + event); + event.respondWith( + handleSwImage(event) + ); +}); diff --git a/tests/page-templates/11-restiming/support/iframe.html b/tests/page-templates/11-restiming/support/iframe.html new file mode 100644 index 000000000..83a065473 --- /dev/null +++ b/tests/page-templates/11-restiming/support/iframe.html @@ -0,0 +1,21 @@ + + + + IFRAME with an Image + + + + + + + + diff --git a/tests/page-templates/11-restiming/support/one.js b/tests/page-templates/11-restiming/support/one.js new file mode 100644 index 000000000..e69de29bb diff --git a/tests/page-templates/11-restiming/support/sm-image.jpg b/tests/page-templates/11-restiming/support/sm-image.jpg new file mode 100644 index 000000000..39cf78e3f Binary files /dev/null and b/tests/page-templates/11-restiming/support/sm-image.jpg differ diff --git a/tests/page-templates/11-restiming/support/spacer.gif b/tests/page-templates/11-restiming/support/spacer.gif new file mode 100644 index 000000000..35d42e808 Binary files /dev/null and b/tests/page-templates/11-restiming/support/spacer.gif differ diff --git a/tests/page-templates/11-restiming/support/spacer.gif.headers b/tests/page-templates/11-restiming/support/spacer.gif.headers new file mode 100644 index 000000000..64ab3c1f4 --- /dev/null +++ b/tests/page-templates/11-restiming/support/spacer.gif.headers @@ -0,0 +1 @@ +Server-Timing: metric1;dur=1;desc="for spacer.gif", metric3;dur=3;desc="for spacer.gif" diff --git a/tests/page-templates/11-restiming/support/st-iframe.html b/tests/page-templates/11-restiming/support/st-iframe.html new file mode 100644 index 000000000..a3b495630 --- /dev/null +++ b/tests/page-templates/11-restiming/support/st-iframe.html @@ -0,0 +1 @@ + diff --git a/tests/page-templates/11-restiming/support/st-iframe.html.headers b/tests/page-templates/11-restiming/support/st-iframe.html.headers new file mode 100644 index 000000000..50f0da0cd --- /dev/null +++ b/tests/page-templates/11-restiming/support/st-iframe.html.headers @@ -0,0 +1 @@ +Server-Timing: metric1;dur=1;desc="for st-iframe.html", metric4;dur=4;desc="for st-iframe.html" diff --git a/tests/page-templates/11-restiming/support/two.js b/tests/page-templates/11-restiming/support/two.js new file mode 100644 index 000000000..e69de29bb diff --git a/tests/page-templates/11-restiming/tests.json5 b/tests/page-templates/11-restiming/tests.json5 new file mode 100644 index 000000000..ed615753d --- /dev/null +++ b/tests/page-templates/11-restiming/tests.json5 @@ -0,0 +1,5 @@ +{ + all: { + requires: ["restiming"] + } +} diff --git a/tests/page-templates/12-react/00-simple.html b/tests/page-templates/12-react/00-simple.html new file mode 100644 index 000000000..663b9fe7e --- /dev/null +++ b/tests/page-templates/12-react/00-simple.html @@ -0,0 +1,26 @@ +<%= header %> +<%= boomerangScript %> + + + + + +
+ + + + + + + +<%= footer %> diff --git a/tests/page-templates/12-react/00-simple.js b/tests/page-templates/12-react/00-simple.js new file mode 100644 index 000000000..8e894eac5 --- /dev/null +++ b/tests/page-templates/12-react/00-simple.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["imgs", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2"]); + +describe("e2e/12-react/00-simple", function() { + BOOMR_test.templates.SPA["00-simple"](); +}); diff --git a/tests/page-templates/12-react/04-route-change.html b/tests/page-templates/12-react/04-route-change.html new file mode 100644 index 000000000..fcc08c61d --- /dev/null +++ b/tests/page-templates/12-react/04-route-change.html @@ -0,0 +1,35 @@ +<%= header %> +<%= boomerangScript %> + + + + + + +
+ + + + + + + +<%= footer %> diff --git a/tests/page-templates/12-react/04-route-change.js b/tests/page-templates/12-react/04-route-change.js new file mode 100644 index 000000000..68191e0c4 --- /dev/null +++ b/tests/page-templates/12-react/04-route-change.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["imgs", "html5_mode", "nav_routes", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2", "i"]); + +describe("e2e/12-react/04-route-change", function() { + BOOMR_test.templates.SPA["04-route-change"](); +}); diff --git a/tests/page-templates/12-react/05-route-change-hashtags.html b/tests/page-templates/12-react/05-route-change-hashtags.html new file mode 100644 index 000000000..7c35315f8 --- /dev/null +++ b/tests/page-templates/12-react/05-route-change-hashtags.html @@ -0,0 +1,32 @@ +<%= header %> +<%= boomerangScript %> + + + + + + +
+ + + + + + + +<%= footer %> diff --git a/tests/page-templates/12-react/05-route-change-hashtags.js b/tests/page-templates/12-react/05-route-change-hashtags.js new file mode 100644 index 000000000..135f66644 --- /dev/null +++ b/tests/page-templates/12-react/05-route-change-hashtags.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["imgs", "html5_mode", "nav_routes", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2", "i"]); + +describe("e2e/12-react/05-route-change-hashtags", function() { + BOOMR_test.templates.SPA["05-route-change-hashtags"](); +}); diff --git a/tests/page-templates/12-react/06-hard-nav-resources.html b/tests/page-templates/12-react/06-hard-nav-resources.html new file mode 100644 index 000000000..0c7dc982d --- /dev/null +++ b/tests/page-templates/12-react/06-hard-nav-resources.html @@ -0,0 +1,26 @@ +<%= header %> +<%= boomerangScript %> + + + + +
+ + + + + + +<%= footer %> diff --git a/tests/page-templates/12-react/06-hard-nav-resources.js b/tests/page-templates/12-react/06-hard-nav-resources.js new file mode 100644 index 000000000..44783cb36 --- /dev/null +++ b/tests/page-templates/12-react/06-hard-nav-resources.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["imgs", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2"]); + +describe("e2e/12-react/06-hard-nav-resources", function() { + BOOMR_test.templates.SPA["06-hard-nav-resources"](); +}); diff --git a/tests/page-templates/12-react/07-soft-nav-resources.html b/tests/page-templates/12-react/07-soft-nav-resources.html new file mode 100644 index 000000000..7784d40ca --- /dev/null +++ b/tests/page-templates/12-react/07-soft-nav-resources.html @@ -0,0 +1,37 @@ +<%= header %> +<%= boomerangScript %> + + + + + + +
+ + + + + + +<%= footer %> diff --git a/tests/page-templates/12-react/07-soft-nav-resources.js b/tests/page-templates/12-react/07-soft-nav-resources.js new file mode 100644 index 000000000..de37e2a9a --- /dev/null +++ b/tests/page-templates/12-react/07-soft-nav-resources.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["html5_mode", "imgs", "nav_routes", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2", "i"]); + +describe("e2e/12-react/07-soft-nav-resources", function() { + BOOMR_test.templates.SPA["07-soft-nav-resources"](); +}); diff --git a/tests/page-templates/12-react/08-no-images.html b/tests/page-templates/12-react/08-no-images.html new file mode 100644 index 000000000..4fbbcf6b1 --- /dev/null +++ b/tests/page-templates/12-react/08-no-images.html @@ -0,0 +1,24 @@ +<%= header %> +<%= boomerangScript %> + + + + +
+ + + + + +<%= footer %> diff --git a/tests/page-templates/12-react/08-no-images.js b/tests/page-templates/12-react/08-no-images.js new file mode 100644 index 000000000..006b627ec --- /dev/null +++ b/tests/page-templates/12-react/08-no-images.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["imgs", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2"]); + +describe("e2e/12-react/08-no-images", function() { + BOOMR_test.templates.SPA["08-no-images"](); +}); diff --git a/tests/page-templates/12-react/09-autoxhr-after-load.html b/tests/page-templates/12-react/09-autoxhr-after-load.html new file mode 100644 index 000000000..4aba416f0 --- /dev/null +++ b/tests/page-templates/12-react/09-autoxhr-after-load.html @@ -0,0 +1,54 @@ +<%= header %> +<%= boomerangScript %> + + + + +
+ + + + + + +<%= footer %> diff --git a/tests/page-templates/12-react/09-autoxhr-after-load.js b/tests/page-templates/12-react/09-autoxhr-after-load.js new file mode 100644 index 000000000..e8feccd43 --- /dev/null +++ b/tests/page-templates/12-react/09-autoxhr-after-load.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["imgs", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2", "xhr", "beaconNum"]); + +describe("e2e/12-react/09-autoxhr-after-load", function() { + BOOMR_test.templates.SPA["09-autoxhr-after-load"](); +}); diff --git a/tests/page-templates/12-react/10-autoxhr-overlapping.html b/tests/page-templates/12-react/10-autoxhr-overlapping.html new file mode 100644 index 000000000..4aea59e2e --- /dev/null +++ b/tests/page-templates/12-react/10-autoxhr-overlapping.html @@ -0,0 +1,49 @@ +<%= header %> +<%= boomerangScript %> + + + + +
+ + + + + + + + +<%= footer %> diff --git a/tests/page-templates/12-react/10-autoxhr-overlapping.js b/tests/page-templates/12-react/10-autoxhr-overlapping.js new file mode 100644 index 000000000..234b34af5 --- /dev/null +++ b/tests/page-templates/12-react/10-autoxhr-overlapping.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["imgs", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2"]); + +describe("e2e/12-react/10-autoxhr-overlapping", function() { + BOOMR_test.templates.SPA["10-autoxhr-overlapping"](); +}); diff --git a/tests/page-templates/12-react/101-hard-nav-onload-no-xhr.html b/tests/page-templates/12-react/101-hard-nav-onload-no-xhr.html new file mode 100644 index 000000000..617738dc0 --- /dev/null +++ b/tests/page-templates/12-react/101-hard-nav-onload-no-xhr.html @@ -0,0 +1,16 @@ +<%= header %> +<%= boomerangScript %> + + + + +<%= footer %> diff --git a/tests/page-templates/12-react/101-hard-nav-onload-no-xhr.js b/tests/page-templates/12-react/101-hard-nav-onload-no-xhr.js new file mode 100644 index 000000000..8d7ce0b46 --- /dev/null +++ b/tests/page-templates/12-react/101-hard-nav-onload-no-xhr.js @@ -0,0 +1,31 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +describe("e2e/12-react/101-hard-nav-onload-no-xhr", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent one beacon", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 1); + }); + + it("Should have sent the beacon as http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + + it("Should have beacon with a t_done of regular NavTiming Page Load time", function() { + if (t.isNavigationTimingSupported()) { + var p = BOOMR.getPerformance(); + var pt = p.timing; + var pageLoad = (pt.loadEventStart - pt.navigationStart); + + assert.closeTo(tf.beacons[0].t_done, pageLoad, 2); + } + else { + // page load should be around the time of the img download - the difference between navStart and t_start + // hard to estimate, so skip for now + this.skip(); + } + }); +}); diff --git a/tests/page-templates/12-react/102-hard-nav-onload-xhr-after.html b/tests/page-templates/12-react/102-hard-nav-onload-xhr-after.html new file mode 100644 index 000000000..3576634ac --- /dev/null +++ b/tests/page-templates/12-react/102-hard-nav-onload-xhr-after.html @@ -0,0 +1,49 @@ +<%= header %> +<%= boomerangScript %> + + + + + +<%= footer %> diff --git a/tests/page-templates/12-react/102-hard-nav-onload-xhr-after.js b/tests/page-templates/12-react/102-hard-nav-onload-xhr-after.js new file mode 100644 index 000000000..f61848e94 --- /dev/null +++ b/tests/page-templates/12-react/102-hard-nav-onload-xhr-after.js @@ -0,0 +1,26 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ +describe("e2e/12-react/102-hard-nav-onload-xhr-after", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent one beacon", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 1); + }); + + it("Should have sent the beacon as http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + + it("Should have beacon with a t_done of approximately NavTiming through the last IMG", function() { + if (t.isMutationObserverSupported() && t.isNavigationTimingSupported() && t.isResourceTimingSupported()) { + var img = t.findFirstResource("img.jpg&id=xhr"); + + assert.closeTo(tf.beacons[0].t_done, img.responseEnd, 50); + } + else { + this.skip(); + } + }); +}); diff --git a/tests/page-templates/12-react/103-hard-nav-onload-fired-no-xhr-boomr-delayed.html b/tests/page-templates/12-react/103-hard-nav-onload-fired-no-xhr-boomr-delayed.html new file mode 100644 index 000000000..379b56f88 --- /dev/null +++ b/tests/page-templates/12-react/103-hard-nav-onload-fired-no-xhr-boomr-delayed.html @@ -0,0 +1,40 @@ +<%= header %> + + + + + + +<%= footer %> diff --git a/tests/page-templates/12-react/103-hard-nav-onload-fired-no-xhr-boomr-delayed.js b/tests/page-templates/12-react/103-hard-nav-onload-fired-no-xhr-boomr-delayed.js new file mode 100644 index 000000000..666146f67 --- /dev/null +++ b/tests/page-templates/12-react/103-hard-nav-onload-fired-no-xhr-boomr-delayed.js @@ -0,0 +1,29 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ +describe("e2e/12-react/103-hard-nav-onload-fired-no-xhr-boomr-delayed", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent one beacon", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 1); + }); + + it("Should have sent the beacon as http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + + it("Should have beacon with a t_done of regular NavTiming Page Load time", function() { + if (t.isMutationObserverSupported() && t.isNavigationTimingSupported()) { + var p = BOOMR.getPerformance(); + var pt = p.timing; + var pageLoad = (pt.loadEventEnd - pt.navigationStart); + + assert.closeTo(tf.beacons[0].t_done, pageLoad, 2); + } + else { + this.skip(); + } + }); +}); + diff --git a/tests/page-templates/12-react/104-hard-nav-onload-fired-xhr-after-load-boomr-delayed.html b/tests/page-templates/12-react/104-hard-nav-onload-fired-xhr-after-load-boomr-delayed.html new file mode 100644 index 000000000..db069f0f5 --- /dev/null +++ b/tests/page-templates/12-react/104-hard-nav-onload-fired-xhr-after-load-boomr-delayed.html @@ -0,0 +1,53 @@ +<%= header %> + + + + + +<%= footer %> diff --git a/tests/page-templates/12-react/104-hard-nav-onload-fired-xhr-after-load-boomr-delayed.js b/tests/page-templates/12-react/104-hard-nav-onload-fired-xhr-after-load-boomr-delayed.js new file mode 100644 index 000000000..50dac9b02 --- /dev/null +++ b/tests/page-templates/12-react/104-hard-nav-onload-fired-xhr-after-load-boomr-delayed.js @@ -0,0 +1,26 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ +describe("e2e/12-react/104-hard-nav-onload-fired-xhr-after-load-boomr-delayed", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent one beacon", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 1); + }); + + it("Should have sent the beacon as http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + + it("Should have beacon with a t_done of approximately NavTiming through the last IMG", function() { + if (t.isMutationObserverSupported() && t.isNavigationTimingSupported() && t.isResourceTimingSupported()) { + var img = t.findFirstResource("img.jpg&id=xhr"); + + assert.closeTo(tf.beacons[0].t_done, img.responseEnd, 50); + } + else { + this.skip(); + } + }); +}); diff --git a/tests/page-templates/12-react/105-no-xhr-before-hard-nav.html b/tests/page-templates/12-react/105-no-xhr-before-hard-nav.html new file mode 100644 index 000000000..5dfebe676 --- /dev/null +++ b/tests/page-templates/12-react/105-no-xhr-before-hard-nav.html @@ -0,0 +1,25 @@ +<%= header %> +<%= boomerangScript %> + + + + + + +<%= footer %> diff --git a/tests/page-templates/12-react/105-no-xhr-before-hard-nav.js b/tests/page-templates/12-react/105-no-xhr-before-hard-nav.js new file mode 100644 index 000000000..6642c6271 --- /dev/null +++ b/tests/page-templates/12-react/105-no-xhr-before-hard-nav.js @@ -0,0 +1,19 @@ +/* eslint-env mocha */ +/* global BOOMR_test,it,describe,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["xhr"]); + +describe("e2e/12-react/105-no-xhr-before-hard-nav", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent one beacon", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 1); + }); + + it("Should have sent the beacon as http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); +}); diff --git a/tests/page-templates/12-react/108-hard-nav-disable.html b/tests/page-templates/12-react/108-hard-nav-disable.html new file mode 100644 index 000000000..bb93ff1c0 --- /dev/null +++ b/tests/page-templates/12-react/108-hard-nav-disable.html @@ -0,0 +1,36 @@ +<%= header %> +<%= boomerangScript %> + + + + + +
+ + + + +<%= footer %> diff --git a/tests/page-templates/12-react/108-hard-nav-disable.js b/tests/page-templates/12-react/108-hard-nav-disable.js new file mode 100644 index 000000000..36092cdb3 --- /dev/null +++ b/tests/page-templates/12-react/108-hard-nav-disable.js @@ -0,0 +1,76 @@ +/* eslint-env mocha */ +/* global BOOMR_test,describe,it,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["imgs", "html5_mode", "call_page_ready", "nav_routes", "disableBoomerangHook", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2", "boomr_t_done", "i"]); + +describe("e2e/12-react/108-hard-nav-disable", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + var pathName = window.location.pathname; + + it("Should have sent three beacons", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 3); + }); + + describe("Beacon 1 (page load)", function() { + var i = 0; + + it("Should be navigation beacon", function() { + var b = tf.beacons[i]; + + if (t.isNavigationTimingSupported()) { + assert.equal(b["rt.start"], "navigation"); + } + else { + assert.equal(b["rt.start"], "none"); + } + + assert.isUndefined(b["http.initiator"]); + }); + + it("Should have a t_done close to 'timestamp - navigationStart'", function() { + var b = tf.beacons[i]; + + if (t.isNavigationTimingSupported()) { + var navStToBoomrTDoneDelta = window.boomr_t_done - window.performance.timing.navigationStart; + + assert.closeTo(navStToBoomrTDoneDelta, b.t_done, 100); + } + }); + }); + + describe("Beacon 2 (spa)", function() { + var i = 1; + + it("Should be soft navigation beacon", function() { + var b = tf.beacons[i]; + + assert.equal(b["http.initiator"], "spa"); + }); + + it("Should have a t_done close to 1s", function() { + var b = tf.beacons[i]; + + assert.closeTo(b.t_done, 1000, 250); + }); + }); + + describe("Beacon 3 (spa)", function() { + var i = 2; + + it("Should be soft navigation beacon", function() { + var b = tf.beacons[i]; + + assert.equal(b["http.initiator"], "spa"); + }); + + it("Should have a t_done close to 500ms", function() { + var b = tf.beacons[i]; + + assert.closeTo(b.t_done, 500, 250); + }); + }); +}); diff --git a/tests/page-templates/12-react/11-autoxhr-trigger-additional.html b/tests/page-templates/12-react/11-autoxhr-trigger-additional.html new file mode 100644 index 000000000..767e76461 --- /dev/null +++ b/tests/page-templates/12-react/11-autoxhr-trigger-additional.html @@ -0,0 +1,49 @@ +<%= header %> +<%= boomerangScript %> + + + + +
+ + + + + +<%= footer %> diff --git a/tests/page-templates/12-react/11-autoxhr-trigger-additional.js b/tests/page-templates/12-react/11-autoxhr-trigger-additional.js new file mode 100644 index 000000000..206aa6e82 --- /dev/null +++ b/tests/page-templates/12-react/11-autoxhr-trigger-additional.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["imgs", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2"]); + +describe("e2e/12-react/11-trigger-additional", function() { + BOOMR_test.templates.SPA["11-autoxhr-trigger-additional"](); +}); diff --git a/tests/page-templates/12-react/111-history-auto-hashtags.html b/tests/page-templates/12-react/111-history-auto-hashtags.html new file mode 100644 index 000000000..33031b221 --- /dev/null +++ b/tests/page-templates/12-react/111-history-auto-hashtags.html @@ -0,0 +1,37 @@ +<%= header %> +<%= boomerangScript %> + + + + + +
+ + + + + + +<%= footer %> diff --git a/tests/page-templates/12-react/111-history-auto-hashtags.js b/tests/page-templates/12-react/111-history-auto-hashtags.js new file mode 100644 index 000000000..c95e2513c --- /dev/null +++ b/tests/page-templates/12-react/111-history-auto-hashtags.js @@ -0,0 +1,27 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["imgs", "html5_mode", "nav_routes", "disableBoomerangHook", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2", "i"]); + +describe("e2e/12-react/111-history-auto-hashtags", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + var pathName = window.location.pathname; + + it("Should have sent three beacons", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 3); + }); + + it("Should have sent the first beacon as http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + + it("Should have sent all subsequent beacons as http.initiator = spa", function() { + for (var i = 1; i < 2; i++) { + assert.equal(tf.beacons[i]["http.initiator"], "spa"); + } + }); +}); diff --git a/tests/page-templates/12-react/111-route-change-fetch.html b/tests/page-templates/12-react/111-route-change-fetch.html new file mode 100644 index 000000000..612824b7e --- /dev/null +++ b/tests/page-templates/12-react/111-route-change-fetch.html @@ -0,0 +1,40 @@ +<%= header %> +<%= boomerangScript %> + + + + + + +
+ + + + + + + +<%= footer %> diff --git a/tests/page-templates/12-react/111-route-change-fetch.js b/tests/page-templates/12-react/111-route-change-fetch.js new file mode 100644 index 000000000..f9724fa2c --- /dev/null +++ b/tests/page-templates/12-react/111-route-change-fetch.js @@ -0,0 +1,22 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["imgs", "html5_mode", "nav_routes", "use_fetch", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2", "i"]); + +describe("e2e/12-react/111-route-change-fetch", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + if (t.isFetchApiSupported()) { + BOOMR_test.templates.SPA["04-route-change"](); + + it("Should have sent the second beacon with a duration of between 2 and 3 seconds", function() { + assert.operator(tf.beacons[1].t_done, ">=", 2000); + assert.operator(tf.beacons[1].t_done, "<=", 3000); + }); + } + else { + it.skip("Should only run tests if Fetch API is supported"); + } +}); diff --git a/tests/page-templates/12-react/112-replacestate-same-url.html b/tests/page-templates/12-react/112-replacestate-same-url.html new file mode 100644 index 000000000..d9c23a25d --- /dev/null +++ b/tests/page-templates/12-react/112-replacestate-same-url.html @@ -0,0 +1,55 @@ +<%= header %> + +<%= boomerangScript %> + + + + + + +
+ + + + + + + +<%= footer %> diff --git a/tests/page-templates/12-react/112-replacestate-same-url.js b/tests/page-templates/12-react/112-replacestate-same-url.js new file mode 100644 index 000000000..9466d7c84 --- /dev/null +++ b/tests/page-templates/12-react/112-replacestate-same-url.js @@ -0,0 +1,10 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert,angular */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["imgs", "html5_mode", "nav_routes", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2", "i"]); + +describe("e2e/12-react/112-replacestate-same-url", function() { + // use tests from #4 + BOOMR_test.templates.SPA["04-route-change"](); +}); diff --git a/tests/page-templates/12-react/115-monitorreplacestate.html b/tests/page-templates/12-react/115-monitorreplacestate.html new file mode 100644 index 000000000..2a5b273d2 --- /dev/null +++ b/tests/page-templates/12-react/115-monitorreplacestate.html @@ -0,0 +1,55 @@ +<%= header %> + +<%= boomerangScript %> + + + + + + +
+ + + + + + + +<%= footer %> diff --git a/tests/page-templates/12-react/115-monitorreplacestate.js b/tests/page-templates/12-react/115-monitorreplacestate.js new file mode 100644 index 000000000..ecf313481 --- /dev/null +++ b/tests/page-templates/12-react/115-monitorreplacestate.js @@ -0,0 +1,10 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert,angular */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["imgs", "html5_mode", "nav_routes", "disableBoomerangHook", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2", "i"]); + +describe("e2e/12-react/115-monitorreplacestate", function() { + // use tests from #4 + BOOMR_test.templates.SPA["04-route-change"](); +}); diff --git a/tests/page-templates/12-react/117-wait.html b/tests/page-templates/12-react/117-wait.html new file mode 100644 index 000000000..ae3bc00eb --- /dev/null +++ b/tests/page-templates/12-react/117-wait.html @@ -0,0 +1,87 @@ +<%= header %> + +<%= boomerangScript %> + + + + + + + +
+
+ + + + + +<%= footer %> diff --git a/tests/page-templates/12-react/117-wait.js b/tests/page-templates/12-react/117-wait.js new file mode 100644 index 000000000..e5fd92787 --- /dev/null +++ b/tests/page-templates/12-react/117-wait.js @@ -0,0 +1,138 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["routeWaits", "routeNumber", "imgs", "html5_mode", "nav_routes", "spaWaitCompleteTimes", "route_wait", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2", "i"]); + +describe("e2e/12-react/117-wait", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent four beacons", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 4); + }); + + it("Should have sent the first beacon as http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + + it("Should have sent all subsequent beacons as http.initiator = spa", function() { + for (var i = 1; i < 3; i++) { + assert.equal(tf.beacons[i]["http.initiator"], "spa"); + } + }); + + // + // Beacon 1 + // + describe("Beacon 1 (spa_hard)", function() { + var i = 0; + + it("Should have sent the first beacon for *-wait.html", function() { + var b = tf.beacons[i]; + + assert.isTrue(b.u.indexOf("-wait.html") !== -1); + }); + + it("Should have sent the first beacon without an additional wait (if ResourceTiming is supported)", function() { + if (t.isResourceTimingSupported()) { + var b = tf.beacons[i]; + // t_done should be close to the end time of the first request for widgets.json + var r = t.findFirstResource("widgets.json"); + + assert.isDefined(b.t_done); + assert.closeTo(r.responseEnd /* HR timestamp */, b.t_done, 100); + } + else { + this.skip(); + } + }); + }); + + // + // Beacon 2 + // + describe("Beacon 2 (spa)", function() { + var i = 1; + + it("Should have sent the second beacon for /widgets/1", function() { + var b = tf.beacons[i]; + + assert.isTrue(b.u.indexOf("/widgets/1") !== -1); + }); + + it("Should have sent the second beacon with the time it took to run the 5 second wait", function(done) { + t.ifAutoXHR( + done, + function() { + assert.operator(tf.beacons[i].t_done, ">=", 5000); + done(); + }, + this.skip.bind(this)); + }); + + it("Should have sent the second beacon with the end time (rt.end) of when the 5 second wait fired", function(done) { + t.ifAutoXHR( + done, + function() { + assert.closeTo(tf.beacons[i]["rt.end"], window.spaWaitCompleteTimes[i - 1], 100); + done(); + }, + this.skip.bind(this)); + }); + }); + + // + // Beacon 3 + // + describe("Beacon 3 (spa)", function() { + var i = 2; + + it("Should have sent the third beacon for /widgets/2", function() { + var b = tf.beacons[i]; + + assert.isTrue(b.u.indexOf("/widgets/2") !== -1); + }); + + it("Should have sent the third with a timestamp of at least 1ms", function(done) { + if (t.isMutationObserverSupported()) { + t.ifAutoXHR( + done, + function() { + assert.operator(tf.beacons[i].t_done, ">=", 1); + assert.operator(tf.beacons[i].t_done, "<", 300); + done(); + }, + this.skip.bind(this)); + } + else { + return this.skip(); + } + }); + }); + + // + // Beacon 4 + // + describe("Beacon 4 (spa)", function() { + var i = 3; + + it("Should have sent the fourth beacon for *-wait.html", function() { + var b = tf.beacons[i]; + + assert.isTrue(b.u.indexOf("-wait.html") !== -1); + }); + + it("Should have sent the fourth beacon with a timestamp of less than 1 second", function() { + // now that the initial page is cached, it should be a quick navigation + var b = tf.beacons[i]; + + assert.operator(b.t_done, "<=", 1000); + }); + }); +}); diff --git a/tests/page-templates/12-react/118-wait-hard-nav.html b/tests/page-templates/12-react/118-wait-hard-nav.html new file mode 100644 index 000000000..656e633c3 --- /dev/null +++ b/tests/page-templates/12-react/118-wait-hard-nav.html @@ -0,0 +1,46 @@ +<%= header %> +<%= boomerangSnippet %> + + + + + + +
+
+ + + + + +<%= footer %> diff --git a/tests/page-templates/12-react/118-wait-hard-nav.js b/tests/page-templates/12-react/118-wait-hard-nav.js new file mode 100644 index 000000000..c44a6b7d6 --- /dev/null +++ b/tests/page-templates/12-react/118-wait-hard-nav.js @@ -0,0 +1,30 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["imgs", "html5_mode", "spaWaitCompleteTimes", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2"]); + +describe("e2e/12-react/118-wait-hard-nav", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent 1 beacon", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 1); + }); + + it("Should have sent the first beacon as http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + + it("Should have sent the first beacon with a wait", function() { + var b = tf.beacons[0]; + + assert.isDefined(b.t_done); + assert.operator(b.t_done, ">=", 5000); + }); +}); diff --git a/tests/page-templates/12-react/119-wait-hard-nav-global-var.html b/tests/page-templates/12-react/119-wait-hard-nav-global-var.html new file mode 100644 index 000000000..768bbda27 --- /dev/null +++ b/tests/page-templates/12-react/119-wait-hard-nav-global-var.html @@ -0,0 +1,61 @@ +<%= header %> +<%= boomerangSnippet %> + + + + + + +
+
+ + + + + +<%= footer %> diff --git a/tests/page-templates/12-react/119-wait-hard-nav-global-var.js b/tests/page-templates/12-react/119-wait-hard-nav-global-var.js new file mode 100644 index 000000000..2fb15756f --- /dev/null +++ b/tests/page-templates/12-react/119-wait-hard-nav-global-var.js @@ -0,0 +1,30 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["imgs", "html5_mode", "spaWaitCompleteTimes", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2"]); + +describe("e2e/12-react/119-wait-hard-nav-global-var", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent 1 beacon", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 1); + }); + + it("Should have sent the first beacon as http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + + it("Should have sent the first beacon with a wait", function() { + var b = tf.beacons[0]; + + assert.isDefined(b.t_done); + assert.operator(b.t_done, ">=", 5000); + }); +}); diff --git a/tests/page-templates/12-react/12-autoxhr-trigger-additional-after-delay.html b/tests/page-templates/12-react/12-autoxhr-trigger-additional-after-delay.html new file mode 100644 index 000000000..82fa096b3 --- /dev/null +++ b/tests/page-templates/12-react/12-autoxhr-trigger-additional-after-delay.html @@ -0,0 +1,51 @@ +<%= header %> +<%= boomerangScript %> + + + + +
+ + + + + +<%= footer %> diff --git a/tests/page-templates/12-react/12-autoxhr-trigger-additional-after-delay.js b/tests/page-templates/12-react/12-autoxhr-trigger-additional-after-delay.js new file mode 100644 index 000000000..281d47302 --- /dev/null +++ b/tests/page-templates/12-react/12-autoxhr-trigger-additional-after-delay.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["imgs", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2"]); + +describe("e2e/12-react/12-autoxhr-trigger-additional-after-delay", function() { + BOOMR_test.templates.SPA["12-autoxhr-trigger-additional-after-delay"](); +}); diff --git a/tests/page-templates/12-react/13-autoxhr-disabled.html b/tests/page-templates/12-react/13-autoxhr-disabled.html new file mode 100644 index 000000000..61c3c2527 --- /dev/null +++ b/tests/page-templates/12-react/13-autoxhr-disabled.html @@ -0,0 +1,48 @@ +<%= header %> +<%= boomerangScript %> + + + + +
+ + + + +<%= footer %> diff --git a/tests/page-templates/12-react/13-autoxhr-disabled.js b/tests/page-templates/12-react/13-autoxhr-disabled.js new file mode 100644 index 000000000..0d6435e0f --- /dev/null +++ b/tests/page-templates/12-react/13-autoxhr-disabled.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["imgs", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2"]); + +describe("e2e/12-react/13-autoxhr-disabled", function() { + BOOMR_test.templates.SPA["13-autoxhr-disabled"](); +}); diff --git a/tests/page-templates/12-react/14-autoxhr-before-page-load.html b/tests/page-templates/12-react/14-autoxhr-before-page-load.html new file mode 100644 index 000000000..a2393e2e8 --- /dev/null +++ b/tests/page-templates/12-react/14-autoxhr-before-page-load.html @@ -0,0 +1,32 @@ +<%= header %> +<%= boomerangScript %> + + + + + +
+ + + + +<%= footer %> diff --git a/tests/page-templates/12-react/14-autoxhr-before-page-load.js b/tests/page-templates/12-react/14-autoxhr-before-page-load.js new file mode 100644 index 000000000..429125623 --- /dev/null +++ b/tests/page-templates/12-react/14-autoxhr-before-page-load.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["imgs", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2", "xhr"]); + +describe("e2e/12-react/14-autoxhr-before-page-load", function() { + BOOMR_test.templates.SPA["14-autoxhr-before-page-load"](); +}); diff --git a/tests/page-templates/12-react/15-delayed-boomerang.html b/tests/page-templates/12-react/15-delayed-boomerang.html new file mode 100644 index 000000000..df704062f --- /dev/null +++ b/tests/page-templates/12-react/15-delayed-boomerang.html @@ -0,0 +1,20 @@ +<%= header %> + + +<%= boomerangDelayedSnippet %> + + +
+ + + + + +<%= footer %> diff --git a/tests/page-templates/12-react/15-delayed-boomerang.js b/tests/page-templates/12-react/15-delayed-boomerang.js new file mode 100644 index 000000000..268291ce9 --- /dev/null +++ b/tests/page-templates/12-react/15-delayed-boomerang.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["BOOMR_script_delay", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2"]); + +describe("e2e/12-react/15-delayed-boomerang", function() { + BOOMR_test.templates.SPA["15-delayed-boomerang"](); +}); diff --git a/tests/page-templates/12-react/17-wait.html b/tests/page-templates/12-react/17-wait.html new file mode 100644 index 000000000..967909132 --- /dev/null +++ b/tests/page-templates/12-react/17-wait.html @@ -0,0 +1,82 @@ +<%= header %> +<%= boomerangScript %> + + + + + + + +
+
+ + + + + + +<%= footer %> diff --git a/tests/page-templates/12-react/17-wait.js b/tests/page-templates/12-react/17-wait.js new file mode 100644 index 000000000..30f0edb8f --- /dev/null +++ b/tests/page-templates/12-react/17-wait.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["routeWaits", "routeNumber", "imgs", "html5_mode", "nav_routes", "spaWaitCompleteTimes", "route_wait", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2", "i"]); + +describe("e2e/12-react/17-wait", function() { + BOOMR_test.templates.SPA["17-wait"](); +}); diff --git a/tests/page-templates/12-react/18-autoxhr-before-page-load-alwayssendxhr.html b/tests/page-templates/12-react/18-autoxhr-before-page-load-alwayssendxhr.html new file mode 100644 index 000000000..a4a4a8ef2 --- /dev/null +++ b/tests/page-templates/12-react/18-autoxhr-before-page-load-alwayssendxhr.html @@ -0,0 +1,31 @@ +<%= header %> +<%= boomerangScript %> + + + + +
+ + + + + + + + +<%= footer %> diff --git a/tests/page-templates/12-react/18-autoxhr-before-page-load-alwayssendxhr.js b/tests/page-templates/12-react/18-autoxhr-before-page-load-alwayssendxhr.js new file mode 100644 index 000000000..a3a3ed13e --- /dev/null +++ b/tests/page-templates/12-react/18-autoxhr-before-page-load-alwayssendxhr.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["imgs", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2"]); + +describe("e2e/12-react/18-autoxhr-before-page-load-alwayssendxhr", function() { + BOOMR_test.templates.SPA["18-autoxhr-before-page-load-alwayssendxhr"](); +}); diff --git a/tests/page-templates/12-react/19-autoxhr-during-nav-alwayssendxhr.html b/tests/page-templates/12-react/19-autoxhr-during-nav-alwayssendxhr.html new file mode 100644 index 000000000..63574d3a7 --- /dev/null +++ b/tests/page-templates/12-react/19-autoxhr-during-nav-alwayssendxhr.html @@ -0,0 +1,62 @@ +<%= header %> +<%= boomerangScript %> + + + + + + +
+ + + + + + + +<%= footer %> diff --git a/tests/page-templates/12-react/19-autoxhr-during-nav-alwayssendxhr.js b/tests/page-templates/12-react/19-autoxhr-during-nav-alwayssendxhr.js new file mode 100644 index 000000000..049b51e2e --- /dev/null +++ b/tests/page-templates/12-react/19-autoxhr-during-nav-alwayssendxhr.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["imgs", "nav_routes", "allowAnyFurtherRoutes", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2", "i"]); + +describe("e2e/12-react/19-autoxhr-during-nav-alwayssendxhr", function() { + BOOMR_test.templates.SPA["19-autoxhr-during-nav-alwayssendxhr"](); +}); diff --git a/tests/page-templates/12-react/20-no-resources.html b/tests/page-templates/12-react/20-no-resources.html new file mode 100644 index 000000000..4337dbaba --- /dev/null +++ b/tests/page-templates/12-react/20-no-resources.html @@ -0,0 +1,33 @@ +<%= header %> +<%= boomerangScript %> + + + + + + +
+ + + + + +<%= footer %> + diff --git a/tests/page-templates/12-react/20-no-resources.js b/tests/page-templates/12-react/20-no-resources.js new file mode 100644 index 000000000..bbd0dfee1 --- /dev/null +++ b/tests/page-templates/12-react/20-no-resources.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["imgs", "html5_mode", "nav_routes", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2", "i"]); + +describe("e2e/12-react/20-no-resources", function() { + BOOMR_test.templates.SPA["20-no-resources"](); +}); diff --git a/tests/page-templates/12-react/21-constant-mutations.html b/tests/page-templates/12-react/21-constant-mutations.html new file mode 100644 index 000000000..605378892 --- /dev/null +++ b/tests/page-templates/12-react/21-constant-mutations.html @@ -0,0 +1,31 @@ +<%= header %> +<%= boomerangScript %> + + + + +
+ +
+ + + + + + +<%= footer %> diff --git a/tests/page-templates/12-react/21-constant-mutations.js b/tests/page-templates/12-react/21-constant-mutations.js new file mode 100644 index 000000000..b26032ca2 --- /dev/null +++ b/tests/page-templates/12-react/21-constant-mutations.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2"]); + +describe("e2e/12-react/21-constant-mutations", function() { + BOOMR_test.templates.SPA["21-constant-mutations"](); +}); diff --git a/tests/page-templates/12-react/23-hard-wait-for-onload.html b/tests/page-templates/12-react/23-hard-wait-for-onload.html new file mode 100644 index 000000000..61b5c2b85 --- /dev/null +++ b/tests/page-templates/12-react/23-hard-wait-for-onload.html @@ -0,0 +1,27 @@ +<%= header %> +<%= boomerangScript %> + + + + + +
+ + + + + + + + +<%= footer %> diff --git a/tests/page-templates/12-react/23-hard-wait-for-onload.js b/tests/page-templates/12-react/23-hard-wait-for-onload.js new file mode 100644 index 000000000..00cd3e874 --- /dev/null +++ b/tests/page-templates/12-react/23-hard-wait-for-onload.js @@ -0,0 +1,8 @@ +/* eslint-env mocha */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["imgs", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2"]); + +describe("e2e/12-react/23-hard-wait-for-onload", function() { + BOOMR_test.templates.SPA["23-hard-wait-for-onload"](); +}); diff --git a/tests/page-templates/12-react/24-route-filter.html b/tests/page-templates/12-react/24-route-filter.html new file mode 100644 index 000000000..0e7e80dab --- /dev/null +++ b/tests/page-templates/12-react/24-route-filter.html @@ -0,0 +1,65 @@ +<%= header %> + +<%= boomerangScript %> + + + + + + +
+ + + + + + + +<%= footer %> diff --git a/tests/page-templates/12-react/24-route-filter.js b/tests/page-templates/12-react/24-route-filter.js new file mode 100644 index 000000000..3fb2bbd7e --- /dev/null +++ b/tests/page-templates/12-react/24-route-filter.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["navs", "imgs", "html5_mode", "nav_routes", "history_route_filter", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2", "i"]); + +describe("e2e/12-react/24-route-filter", function() { + BOOMR_test.templates.SPA["24-route-filter"](); +}); diff --git a/tests/page-templates/12-react/25-delayed-boomerang-pre-config-snippet.html b/tests/page-templates/12-react/25-delayed-boomerang-pre-config-snippet.html new file mode 100644 index 000000000..0f12779c3 --- /dev/null +++ b/tests/page-templates/12-react/25-delayed-boomerang-pre-config-snippet.html @@ -0,0 +1,43 @@ +<%= header %> +<%= boomerangDelayedSnippet %> + + + +
+ + + + + + + +<%= footer %> diff --git a/tests/page-templates/12-react/25-delayed-boomerang-pre-config-snippet.js b/tests/page-templates/12-react/25-delayed-boomerang-pre-config-snippet.js new file mode 100644 index 000000000..226f4f13b --- /dev/null +++ b/tests/page-templates/12-react/25-delayed-boomerang-pre-config-snippet.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["BOOMR_script_delay", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2", "timestamp"]); + +describe("e2e/12-react/25-delayed-boomerang-pre-config-snippet", function() { + BOOMR_test.templates.SPA["25-delayed-boomerang-pre-config-snippet"](); +}); diff --git a/tests/page-templates/12-react/27-route-change-back.html b/tests/page-templates/12-react/27-route-change-back.html new file mode 100644 index 000000000..d91c71fc7 --- /dev/null +++ b/tests/page-templates/12-react/27-route-change-back.html @@ -0,0 +1,48 @@ +<%= header %> +<%= boomerangScript %> + + + + + + +
+ + + + + + + +<%= footer %> diff --git a/tests/page-templates/12-react/27-route-change-back.js b/tests/page-templates/12-react/27-route-change-back.js new file mode 100644 index 000000000..0db31d08b --- /dev/null +++ b/tests/page-templates/12-react/27-route-change-back.js @@ -0,0 +1,10 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert,angular */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["imgs", "html5_mode", "nav_routes", "beaconNum", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2", "i"]); + +describe("e2e/12-react/27-route-change-back", function() { + // use tests from #4 + BOOMR_test.templates.SPA["04-route-change"](); +}); diff --git a/tests/page-templates/12-react/28-route-change-history-auto.html b/tests/page-templates/12-react/28-route-change-history-auto.html new file mode 100644 index 000000000..8111ef1a9 --- /dev/null +++ b/tests/page-templates/12-react/28-route-change-history-auto.html @@ -0,0 +1,36 @@ +<%= header %> +<%= boomerangScript %> + + + + + + +
+ + + + + + + +<%= footer %> diff --git a/tests/page-templates/12-react/28-route-change-history-auto.js b/tests/page-templates/12-react/28-route-change-history-auto.js new file mode 100644 index 000000000..426c07892 --- /dev/null +++ b/tests/page-templates/12-react/28-route-change-history-auto.js @@ -0,0 +1,10 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert,angular */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["imgs", "html5_mode", "nav_routes", "disableBoomerangHook", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2", "i"]); + +describe("e2e/12-react/28-route-change-history-auto", function() { + // use tests from #4 + BOOMR_test.templates.SPA["04-route-change"](); +}); diff --git a/tests/page-templates/12-react/29-route-change-early-beacon.html b/tests/page-templates/12-react/29-route-change-early-beacon.html new file mode 100644 index 000000000..4b959826e --- /dev/null +++ b/tests/page-templates/12-react/29-route-change-early-beacon.html @@ -0,0 +1,152 @@ +<%= header %> +<%= boomerangScript %> + + + + + + +
+ + + + + + + +<%= footer %> diff --git a/tests/page-templates/12-react/29-route-change-early-beacon.js b/tests/page-templates/12-react/29-route-change-early-beacon.js new file mode 100644 index 000000000..5bacde5da --- /dev/null +++ b/tests/page-templates/12-react/29-route-change-early-beacon.js @@ -0,0 +1,9 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["imgs", "html5_mode", "nav_routes", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2", "img", "i"]); + +describe("e2e/12-react/29-route-change-early-beacon", function() { + BOOMR_test.templates.SPA["29-route-change-early-beacon"](); +}); diff --git a/tests/page-templates/12-react/31-change-img-src.html b/tests/page-templates/12-react/31-change-img-src.html new file mode 100644 index 000000000..64c6845e6 --- /dev/null +++ b/tests/page-templates/12-react/31-change-img-src.html @@ -0,0 +1,84 @@ +<%= header %> + +<%= boomerangScript %> + + + + + + +
+ + + + + + + +<%= footer %> diff --git a/tests/page-templates/12-react/31-change-img-src.js b/tests/page-templates/12-react/31-change-img-src.js new file mode 100644 index 000000000..59060a1ff --- /dev/null +++ b/tests/page-templates/12-react/31-change-img-src.js @@ -0,0 +1,10 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert,angular */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["imgs", "html5_mode", "nav_routes", "disableBoomerangHook", "custom_metric_1", "custom_metric_2", "custom_timer_1", "custom_timer_2", "i"]); + +describe("e2e/12-react/31-change-img-src", function() { + // use tests from #4 + BOOMR_test.templates.SPA["04-route-change"](); +}); diff --git a/tests/page-templates/12-react/support/app.jsx b/tests/page-templates/12-react/support/app.jsx new file mode 100644 index 000000000..e1cf8efd9 --- /dev/null +++ b/tests/page-templates/12-react/support/app.jsx @@ -0,0 +1,336 @@ +import React from 'react'; +import { render } from 'react-dom'; +import { useBasename } from "history"; +import createHashHistory from 'history/lib/createHashHistory'; +import createBrowserHistory from 'history/lib/createBrowserHistory'; + +import { Router, Route, IndexRoute, Link, History } from 'react-router'; + +var hadRouteChange = false; +var history; +var hookOptions = {}; + +if (window.html5_mode === true) { + history = useBasename(createBrowserHistory)({ + basename: location.pathname + }); +} +else { + history = createHashHistory(); +} + +var subscribed = false; + +if (window.route_wait) { + hookOptions.routeChangeWaitFilter = window.route_wait; +} + +if (window.history_route_filter) { + hookOptions.routeFilter = window.history_route_filter; +} + +function hookHistoryBoomerang() { + if (window.BOOMR && BOOMR.version) { + if (BOOMR.plugins && BOOMR.plugins.History) { + BOOMR.plugins.History.hook(history, hadRouteChange, hookOptions); + } + return true; + } +} + +if (!window.disableBoomerangHook) { + if (!hookHistoryBoomerang()) { + if (document.addEventListener) { + document.addEventListener("onBoomerangLoaded", hookHistoryBoomerang); + } else if (document.attachEvent) { + document.attachEvent("onpropertychange", function(e) { + e = e || window.event; + if (e && e.propertyName === "onBoomerangLoaded") { + hookHistoryBoomerang(); + } + }); + } + } +} + +const App = React.createClass({ + getInitialState() { + var that = this; + if (window.nav_routes && window.nav_routes.hasOwnProperty("length") && window.nav_routes.length > 0) { + if (!subscribed) { + BOOMR.subscribe("beacon", function(beacon) { + // only continue for non-early SPA beacons + if ((!BOOMR.utils.inArray(beacon["http.initiator"], BOOMR.constants.BEACON_TYPE_SPAS) || + typeof beacon.early !== "undefined") && !window.call_page_ready) { + return; + } + + if (window.nav_routes.length > 0) { + var newRoute = window.nav_routes.shift(); + setTimeout(function() { + history.pushState(null, `${newRoute}`); + // delay for 1s to allow for any late-loading images to appear + }, 1000); + } + }); + } + subscribed = true; + } + + return {}; + }, + render(){ + return ( +
+ Home + {this.props.children} +
+ ); + } +}); + +const Home = React.createClass({ + getInitialState() { + var images; + + // these overwrite what was in the HTML + window.custom_metric_1 = 11; + window.custom_metric_2 = function() { + return 22; + }; + + window.custom_timer_1 = 11; + window.custom_timer_2 = function() { + return 22; + }; + + if (typeof window.performance !== "undefined" && + typeof window.performance.mark === "function") { + window.performance.mark("mark_usertiming"); + } + + if (typeof window.imgs !== "undefined" && window.imgs.hasOwnProperty("length")) { + images = window.imgs; + } else { + images = [0]; + } + + var state = { + imgs: images, + rnd: '' + (Math.random() * 1000) + }; + + if (images[0] === -1) { + state.hide_imgs = true; + } + + hadRouteChange = true; + return state; + }, + componentDidMount() { + if (!window.use_fetch) { + var homeXHR = new XMLHttpRequest(); + homeXHR.addEventListener("load", function(homeHtml) { + if (this.isMounted()) { + this.setState({ + home: homeHtml.target.response + }); + } + }.bind(this)); + homeXHR.open("GET", "support/home.html?rnd=" + (Math.round(Math.random() * 1000))); + homeXHR.send(null); + + var widgetsXHR = new XMLHttpRequest(); + widgetsXHR.addEventListener("load", function(result) { + if (this.isMounted()) { + this.setState({ + widgets: JSON.parse(result.target.response) + }); + } + }.bind(this)); + widgetsXHR.open("GET", "/delay?delay=250&file=/pages/12-react/support/widgets.json?rnd=" + (Math.round(Math.random() * 1000))); + widgetsXHR.send(null); + } + else { + var that = this; + fetch("support/home.html?rnd=" + (Math.round(Math.random() * 1000))) + .then((response) => response.text()) + .then(function(data) { + if (that.isMounted()) { + that.setState({ + home: data + }); + } + }); + + fetch("/delay?delay=250&file=/pages/12-react/support/widgets.json?rnd=" + (Math.round(Math.random() * 1000))) + .then((response) => response.json()) + .then(function(data) { + if (that.isMounted()) { + that.setState({ + widgets: data + }); + } + }); + } + }, + cartMarkup() { + return { __html: this.state.home }; + }, + renderWidgets() { + var widgetsElements = []; + for (var widgetIndex in this.state.widgets ) { + var link = Widgets {widgetIndex}; + widgetsElements.push(
  • {link}
  • ); + } + return widgetsElements; + }, + imageOnload() { + if (window.call_page_ready && !window.boomr_t_done) { + window.boomr_t_done = BOOMR.now(); + BOOMR.page_ready(); + } + }, + render() { + var widgetsElements = this.renderWidgets(); + if (window.window.late_imgs && !this.state.addedLates) { + var rnd = this.state.rnd; + this.state.addedLates = true; + setTimeout(function() { + var content = document.getElementById("root"); + + window.window.late_imgs.forEach(function(imgDelay) { + var img = document.createElement("img"); + if (imgDelay > 0) { + img.src = `/delay?delay=${imgDelay}&id=late&file=pages/12-react/support/img.jpg&id=home&rnd=${rnd}`; + content.appendChild(img); + } + }); + }, 500); + } + if (!this.state.hide_imgs) { + var images = []; + for (var delayIndex in this.state.imgs) { + var style = { + width: 300 + "px", + height: "auto" + }; + var src = `/delay?delay=${this.state.imgs[delayIndex]}&file=pages/12-react/support/img.jpg&id=home&rnd=${this.state.rnd}`; + images.push(
    + +
    ); + } + return ( +
    +
    +
    +
    $444.44
    +
    + {images} +
    +
      + {widgetsElements} +
    +
    +
    + ); + } else { + return ( +
    +
    +
    +
      + {widgetsElements} +
    +
    +
    + ); + } + } +}); + +const Widget = React.createClass({ + getInitialState() { + var wid = this.props.params.id; + + // these overwrite what was in the HTML + window.custom_metric_1 = wid; + window.custom_metric_2 = function() { + return 10 * wid; + }; + + window.custom_timer_1 = wid; + window.custom_timer_2 = function() { + return 10 * wid; + }; + + return { + rnd: '' + (Math.random() * 1000), + id: wid + }; + }, + componentDidMount() { + var url; + if (this.props.params.delay) { + url = "/delay?delay=" + this.props.params.delay + "&file=/pages/12-react/support/widget.html?rnd=" + (Math.round(Math.random() * 1000)); + } + else { + url = "support/widget.html?rnd=" + (Math.round(Math.random() * 1000)); + } + + if (!window.use_fetch) { + var widgetXHR = new XMLHttpRequest(); + widgetXHR.addEventListener("load", function(widgetHtml) { + if (this.isMounted()) { + this.setState({ + widgetHtml: widgetHtml.target.response + }); + } + }.bind(this)); + widgetXHR.open("GET", url); + widgetXHR.send(null); + } + else { + var that = this; + fetch(url) + .then((response) => response.text()) + .then(function(data) { + if (that.isMounted()) { + that.setState({ + widgetHtml: data + }); + } + }); + } + }, + widgetMarkup() { + return { __html: this.state.widgetHtml }; + }, + render() { + var style = { + width: 300 + "px", + height: "auto" + }; + var image =
    ; + var carttotal = this.props.params.id * 11.11; + return ( +
    +
    +
    +
    ${carttotal}
    +
    + {image} +
    + ); + } +}); + +var routerInstance = render(( + + + + + + + +), document.getElementById("root")); diff --git a/tests/page-templates/12-react/support/function-bind-polyfill.js b/tests/page-templates/12-react/support/function-bind-polyfill.js new file mode 100644 index 000000000..dd4f33af1 --- /dev/null +++ b/tests/page-templates/12-react/support/function-bind-polyfill.js @@ -0,0 +1,36 @@ +/** + * Fix issues with Function.prototype.bind not being available. + * via: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind#Polyfill + **/ +(function() { + if (!Function.prototype.bind) { + /*eslint-disable no-extend-native*/ + Function.prototype.bind = function(that) { + if (typeof this !== "function") { + // closest thing possible to the ECMAScript 5 + // internal IsCallable function + throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable"); + } + + var aArgs = Array.prototype.slice.call(arguments, 1), + fToBind = this, + fNOP = function() {}, + fBound = function() { + return fToBind.apply(this instanceof fNOP ? + this : + that, + aArgs.concat(Array.prototype.slice.call(arguments))); + }; + + if (this.prototype) { + // native functions don't have a prototype + fNOP.prototype = this.prototype; + } + + fBound.prototype = new fNOP(); + + return fBound; + }; + /*eslint-enable no-extend-native*/ + } +}()); diff --git a/tests/page-templates/12-react/support/home.html b/tests/page-templates/12-react/support/home.html new file mode 100644 index 000000000..613c6cb2c --- /dev/null +++ b/tests/page-templates/12-react/support/home.html @@ -0,0 +1,2 @@ +

    Home

    + diff --git a/tests/page-templates/12-react/support/img.jpg b/tests/page-templates/12-react/support/img.jpg new file mode 100644 index 000000000..de3a1f7e9 Binary files /dev/null and b/tests/page-templates/12-react/support/img.jpg differ diff --git a/tests/page-templates/12-react/support/widget.html b/tests/page-templates/12-react/support/widget.html new file mode 100644 index 000000000..ad9c7de7e --- /dev/null +++ b/tests/page-templates/12-react/support/widget.html @@ -0,0 +1 @@ +

    Widget

    diff --git a/tests/page-templates/12-react/support/widgets.json b/tests/page-templates/12-react/support/widgets.json new file mode 100644 index 000000000..736c8f1bf --- /dev/null +++ b/tests/page-templates/12-react/support/widgets.json @@ -0,0 +1,14 @@ +[ + { + "id": 1, + "name": "Widget 1" + }, + { + "id": 2, + "name": "Widget 2" + }, + { + "id": 3, + "name": "Widget 3" + } +] diff --git a/tests/page-templates/12-react/tests.json5 b/tests/page-templates/12-react/tests.json5 new file mode 100644 index 000000000..38763e1e7 --- /dev/null +++ b/tests/page-templates/12-react/tests.json5 @@ -0,0 +1,8 @@ +{ + all: { + requires: ["spa", "history", "auto-xhr"] + }, + "29-route-change-early-beacon": { + requires: ["early"] + } +} diff --git a/tests/page-templates/14-errors/00-send.html b/tests/page-templates/14-errors/00-send.html new file mode 100644 index 000000000..51fe77cb2 --- /dev/null +++ b/tests/page-templates/14-errors/00-send.html @@ -0,0 +1,18 @@ +<%= header %> +<%= boomerangScriptMin %> + + +<%= footer %> diff --git a/tests/page-templates/14-errors/00-send.js b/tests/page-templates/14-errors/00-send.js new file mode 100644 index 000000000..0c82b36a7 --- /dev/null +++ b/tests/page-templates/14-errors/00-send.js @@ -0,0 +1,124 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["errorFunction"]); + +describe("e2e/14-errors/00-send", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + var C = BOOMR.utils.Compression; + + it("Should have sent a single beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have put the err on the beacon", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b.err); + }); + + it("Should have had a single error", function() { + var b = tf.lastBeacon(); + + assert.equal(C.jsUrlDecompress(b.err).length, 1); + }); + + it("Should have count = 1", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.count, 1); + }); + + it("Should have had a recent timestamp", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.closeTo(err.timestamp, BOOMR.now(), 10000); + }); + + it("Should have fileName of the page (if set)", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.fileName) { + assert.include(err.fileName, "00-send.html"); + } + else { + this.skip(); + } + }); + + it("Should have functionName of 'errorFunction'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.functionName) { + assert.equal(err.functionName, "errorFunction"); + } + else { + this.skip(); + } + }); + + it("Should have message = 'ERROR!'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.message, "ERROR!"); + }); + + it("Should have source = APP", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.source, BOOMR.plugins.Errors.SOURCE_APP); + }); + + it("Should have stack with the stack", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.isDefined(err.stack); + }); + + it("Should have type = 'Error'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.type, "Error"); + }); + + it("Should have via = APP", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.via, BOOMR.plugins.Errors.VIA_APP); + }); + + it("Should have columNumber to be a number if specified", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (typeof err.columnNumber !== "undefined") { + assert.isTrue(err.columnNumber >= 0); + } + else { + this.skip(); + } + }); + + it("Should have lineNumber ~ " + (HEADER_LINES + 3), function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.lineNumber) { + assert.closeTo(err.lineNumber, HEADER_LINES + 3, 5); + } + else { + this.skip(); + } + }); +}); diff --git a/tests/page-templates/14-errors/01-send-after-onload-disabled.html b/tests/page-templates/14-errors/01-send-after-onload-disabled.html new file mode 100644 index 000000000..c61599ed7 --- /dev/null +++ b/tests/page-templates/14-errors/01-send-after-onload-disabled.html @@ -0,0 +1,21 @@ +<%= header %> +<%= boomerangScriptMin %> + + +<%= footer %> diff --git a/tests/page-templates/14-errors/01-send-after-onload-disabled.js b/tests/page-templates/14-errors/01-send-after-onload-disabled.js new file mode 100644 index 000000000..92dfa9098 --- /dev/null +++ b/tests/page-templates/14-errors/01-send-after-onload-disabled.js @@ -0,0 +1,22 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["errorFunction"]); + +describe("e2e/14-errors/01-send-after-onload-disabled", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + var C = BOOMR.utils.Compression; + + it("Should have only sent a page-load beacon", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 1); + }); + + it("Should have no error on the page-load beacon", function() { + var b = tf.lastBeacon(); + + assert.isUndefined(b.err); + }); +}); diff --git a/tests/page-templates/14-errors/02-send-after-onload-enabled.html b/tests/page-templates/14-errors/02-send-after-onload-enabled.html new file mode 100644 index 000000000..87df0d251 --- /dev/null +++ b/tests/page-templates/14-errors/02-send-after-onload-enabled.html @@ -0,0 +1,20 @@ +<%= header %> +<%= boomerangScriptMin %> + + +<%= footer %> diff --git a/tests/page-templates/14-errors/02-send-after-onload-enabled.js b/tests/page-templates/14-errors/02-send-after-onload-enabled.js new file mode 100644 index 000000000..dba7367dd --- /dev/null +++ b/tests/page-templates/14-errors/02-send-after-onload-enabled.js @@ -0,0 +1,124 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["errorFunction"]); + +describe("e2e/14-errors/02-send-after-onload-enabled", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + var C = BOOMR.utils.Compression; + + it("Should have sent two beacons", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 2); + }); + + it("Should have put the err on the second beacon", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b.err); + }); + + it("Should have set http.initiator = 'error' on the second beacon", function() { + var b = tf.lastBeacon(); + + assert.equal(b["http.initiator"], "error"); + }); + + it("Should have had a single error", function() { + var b = tf.lastBeacon(); + + assert.equal(C.jsUrlDecompress(b.err).length, 1); + }); + + it("Should have count = 1", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.count, 1); + }); + + it("Should have fileName of the page (if set)", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.fileName) { + assert.include(err.fileName, "02-send-after-onload-enabled.html"); + } + else { + return this.skip(); + } + }); + + it("Should have functionName of 'errorFunction'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.functionName) { + assert.equal(err.functionName, "errorFunction"); + } + else { + return this.skip(); + } + }); + + it("Should have message = 'ERROR!'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.message, "ERROR!"); + }); + + it("Should have source = APP", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.source, BOOMR.plugins.Errors.SOURCE_APP); + }); + + it("Should have stack with the stack", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.isDefined(err.stack); + }); + + it("Should have type = 'Error'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.type, "Error"); + }); + + it("Should have via = APP", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.via, BOOMR.plugins.Errors.VIA_APP); + }); + + it("Should have columNumber to be a number if specified", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (typeof err.columnNumber !== "undefined") { + assert.isTrue(err.columnNumber >= 0); + } + else { + return this.skip(); + } + }); + + it("Should have lineNumber ~ " + (HEADER_LINES + 3), function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.lineNumber) { + assert.closeTo(err.lineNumber, HEADER_LINES + 3, 5); + } + else { + return this.skip(); + } + }); +}); diff --git a/tests/page-templates/14-errors/03-onerror.html b/tests/page-templates/14-errors/03-onerror.html new file mode 100644 index 000000000..765262243 --- /dev/null +++ b/tests/page-templates/14-errors/03-onerror.html @@ -0,0 +1,42 @@ +<%= header %> +<%= boomerangScriptMin %> + + +<%= footer %> diff --git a/tests/page-templates/14-errors/03-onerror.js b/tests/page-templates/14-errors/03-onerror.js new file mode 100644 index 000000000..9da9ab837 --- /dev/null +++ b/tests/page-templates/14-errors/03-onerror.js @@ -0,0 +1,23 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["errorFunction", "onErrorFired"]); + +describe("e2e/14-errors/03-onerror", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + var C = BOOMR.utils.Compression; + + it("Should have sent two beacons", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 2); + }); + + it("Should have only sent the 'BOOM2' beacon", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.message, "BOOM2"); + }); +}); diff --git a/tests/page-templates/14-errors/04-dupe.html b/tests/page-templates/14-errors/04-dupe.html new file mode 100644 index 000000000..ed2529303 --- /dev/null +++ b/tests/page-templates/14-errors/04-dupe.html @@ -0,0 +1,21 @@ +<%= header %> +<%= boomerangScriptMin %> + + +<%= footer %> diff --git a/tests/page-templates/14-errors/04-dupe.js b/tests/page-templates/14-errors/04-dupe.js new file mode 100644 index 000000000..40eff9513 --- /dev/null +++ b/tests/page-templates/14-errors/04-dupe.js @@ -0,0 +1,117 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["errorFunction", "i"]); + +describe("e2e/14-errors/04-dupe", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + var C = BOOMR.utils.Compression; + + it("Should have sent a single beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have put the err on the beacon", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b.err); + }); + + it("Should have had a single error", function() { + var b = tf.lastBeacon(); + + assert.equal(C.jsUrlDecompress(b.err).length, 1); + }); + + it("Should have count = 2", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.count, 2); + }); + + it("Should have fileName of the page (if set)", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.fileName) { + assert.include(err.fileName, "04-dupe.html"); + } + else { + return this.skip(); + } + }); + + it("Should have functionName of 'errorFunction'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.functionName) { + assert.equal(err.functionName, "errorFunction"); + } + else { + return this.skip(); + } + }); + + it("Should have message = 'ERROR!'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.message, "ERROR!"); + }); + + it("Should have source = APP", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.source, BOOMR.plugins.Errors.SOURCE_APP); + }); + + it("Should have stack with the stack", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.isDefined(err.stack); + }); + + it("Should have type = 'Error'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.type, "Error"); + }); + + it("Should have via = APP", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.via, BOOMR.plugins.Errors.VIA_APP); + }); + + it("Should have columNumber to be a number if specified", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (typeof err.columnNumber !== "undefined") { + assert.isTrue(err.columnNumber >= 0); + } + else { + return this.skip(); + } + }); + + it("Should have lineNumber ~ " + (HEADER_LINES + 3), function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.lineNumber) { + assert.closeTo(err.lineNumber, HEADER_LINES + 3, 5); + } + else { + return this.skip(); + } + }); +}); diff --git a/tests/page-templates/14-errors/05-send-after-onload-multiple.html b/tests/page-templates/14-errors/05-send-after-onload-multiple.html new file mode 100644 index 000000000..3d59bbbe4 --- /dev/null +++ b/tests/page-templates/14-errors/05-send-after-onload-multiple.html @@ -0,0 +1,35 @@ +<%= header %> +<%= boomerangScriptMin %> + + +<%= footer %> diff --git a/tests/page-templates/14-errors/05-send-after-onload-multiple.js b/tests/page-templates/14-errors/05-send-after-onload-multiple.js new file mode 100644 index 000000000..67b06e1e6 --- /dev/null +++ b/tests/page-templates/14-errors/05-send-after-onload-multiple.js @@ -0,0 +1,160 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["a"]); + +describe("e2e/14-errors/05-send-after-onload-multiple", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + var C = BOOMR.utils.Compression; + + it("Should have sent two beacons", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 2); + }); + + it("Should have put the err on the second beacon", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b.err); + }); + + it("Should have had 3 errors", function() { + var b = tf.lastBeacon(); + + assert.equal(C.jsUrlDecompress(b.err).length, 3); + }); + + it("Should have set http.initiator = 'error' on the second beacon", function() { + var b = tf.lastBeacon(); + + assert.equal(b["http.initiator"], "error"); + }); + + it("Should have the proper counts", function() { + var b = tf.lastBeacon(); + var errs = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err)); + + assert.equal(errs[0].count, 3); + assert.equal(errs[1].count, 1); + assert.equal(errs[2].count, 1); + }); + + it("Should have all errors with the fileName of the page", function() { + var b = tf.lastBeacon(); + var errs = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err)); + + for (var i = 0; i < errs.length; i++) { + var err = errs[i]; + + if (err.fileName) { + assert.include(err.fileName, "05-send-after-onload-multiple.html"); + } + else { + return this.skip(); + } + } + }); + + it("Should have functionName of 'error-,function' if defined", function() { + var b = tf.lastBeacon(); + var errs = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err)); + + for (var i = 0; i < errs.length; i++) { + var err = errs[i]; + + if (typeof err.functionName !== "undefined") { + assert.include(err.functionName, "error-,function"); + } + else { + return this.skip(); + } + } + }); + + it("Should have message = 'ERROR!'", function() { + var b = tf.lastBeacon(); + var errs = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err)); + + assert.equal(errs[0].message, "BOOM1"); + assert.equal(errs[1].message, "BOOM2"); + assert.equal(errs[2].message, "BOOM3"); + }); + + it("Should have source = APP", function() { + var b = tf.lastBeacon(); + var errs = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err)); + + for (var i = 0; i < errs.length; i++) { + var err = errs[i]; + + assert.equal(err.source, BOOMR.plugins.Errors.SOURCE_APP); + } + }); + + it("Should have stack with the stack", function() { + var b = tf.lastBeacon(); + var errs = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err)); + + for (var i = 0; i < errs.length; i++) { + var err = errs[i]; + + assert.isDefined(err.stack); + } + }); + + it("Should have type = 'Error'", function() { + var b = tf.lastBeacon(); + var errs = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err)); + + for (var i = 0; i < errs.length; i++) { + var err = errs[i]; + + assert.equal(err.type, "Error"); + } + }); + + it("Should have via = APP", function() { + var b = tf.lastBeacon(); + var errs = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err)); + + for (var i = 0; i < errs.length; i++) { + var err = errs[i]; + + assert.equal(err.via, BOOMR.plugins.Errors.VIA_APP); + } + }); + + it("Should have columNumber to be a number if specified", function() { + var b = tf.lastBeacon(); + var errs = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err)); + + for (var i = 0; i < errs.length; i++) { + var err = errs[i]; + + if (typeof err.columnNumber !== "undefined") { + assert.isTrue(err.columnNumber >= 0); + } + else { + return this.skip(); + } + } + }); + + it("Should have lineNumber ~ " + (HEADER_LINES + 4), function() { + var b = tf.lastBeacon(); + var errs = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err)); + + for (var i = 0; i < errs.length; i++) { + var err = errs[i]; + + if (err.lineNumber) { + assert.closeTo(err.lineNumber, HEADER_LINES + 4, 5); + } + else { + return this.skip(); + } + } + }); +}); diff --git a/tests/page-templates/14-errors/06-send-after-onload-dupe-of-load.html b/tests/page-templates/14-errors/06-send-after-onload-dupe-of-load.html new file mode 100644 index 000000000..047dcda27 --- /dev/null +++ b/tests/page-templates/14-errors/06-send-after-onload-dupe-of-load.html @@ -0,0 +1,25 @@ +<%= header %> +<%= boomerangScriptMin %> + + + + +<%= footer %> diff --git a/tests/page-templates/14-errors/06-send-after-onload-dupe-of-load.js b/tests/page-templates/14-errors/06-send-after-onload-dupe-of-load.js new file mode 100644 index 000000000..2f5835123 --- /dev/null +++ b/tests/page-templates/14-errors/06-send-after-onload-dupe-of-load.js @@ -0,0 +1,118 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["errorFunction"]); + +describe("e2e/14-errors/06-send-after-onload-dupe-of-load", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + var C = BOOMR.utils.Compression; + + it("Should have sent a single beacon", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 1); + }); + + it("Should have put the err on the beacon", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b.err); + }); + + it("Should have had a single error", function() { + var b = tf.lastBeacon(); + + assert.equal(C.jsUrlDecompress(b.err).length, 1); + }); + + it("Should have count = 1", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.isTrue(err.count >= 2, "err.count >= 2"); + }); + + it("Should have fileName of the page (if set)", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.fileName) { + assert.include(err.fileName, "06-send-after-onload-dupe-of-load.html"); + } + else { + return this.skip(); + } + }); + + it("Should have functionName of 'errorFunction'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.functionName) { + assert.equal(err.functionName, "errorFunction"); + } + else { + return this.skip(); + } + }); + + it("Should have message = 'ERROR!'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.message, "ERROR!"); + }); + + it("Should have source = APP", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.source, BOOMR.plugins.Errors.SOURCE_APP); + }); + + it("Should have stack with the stack", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.isDefined(err.stack); + }); + + it("Should have type = 'Error'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.type, "Error"); + }); + + it("Should have via = APP", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.via, BOOMR.plugins.Errors.VIA_APP); + }); + + it("Should have columNumber to be a number if specified", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (typeof err.columnNumber !== "undefined") { + assert.isTrue(err.columnNumber >= 0); + } + else { + return this.skip(); + } + }); + + it("Should have lineNumber ~ " + (HEADER_LINES + 3), function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.lineNumber) { + assert.closeTo(err.lineNumber, HEADER_LINES + 3, 5); + } + else { + return this.skip(); + } + }); +}); diff --git a/tests/page-templates/14-errors/07-global.html b/tests/page-templates/14-errors/07-global.html new file mode 100644 index 000000000..9080da2c4 --- /dev/null +++ b/tests/page-templates/14-errors/07-global.html @@ -0,0 +1,27 @@ +<%= header %> +<%= boomerangScriptMin %> + + + + +<%= footer %> diff --git a/tests/page-templates/14-errors/07-global.js b/tests/page-templates/14-errors/07-global.js new file mode 100644 index 000000000..d3691163e --- /dev/null +++ b/tests/page-templates/14-errors/07-global.js @@ -0,0 +1,123 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["errorFunction"]); + +describe("e2e/14-errors/07-global", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + var C = BOOMR.utils.Compression; + + it("Should have sent a single beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have put the err on the beacon", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b.err); + }); + + it("Should have had a single error", function() { + var b = tf.lastBeacon(); + + assert.equal(C.jsUrlDecompress(b.err).length, 1); + }); + + it("Should have count = 1", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.count, 1); + }); + + it("Should have fileName of the page (if set)", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.fileName) { + assert.include(err.fileName, "07-global.html"); + } + else { + return this.skip(); + } + }); + + it("Should have functionName of 'errorFunction'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.functionName) { + assert.equal(err.functionName, "errorFunction"); + } + else { + return this.skip(); + } + }); + + it("Should have message = 'a is not defined' or 'Can't find variable: a' or ''a' is undefined'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + // Chrome, Firefox == a is not defined, Safari = Can't find variable, Edge = 'a' is not defined + assert.isTrue( + err.message.indexOf("a is not defined") !== -1 || + err.message.indexOf("Can't find variable: a") !== -1 || + err.message.indexOf("'a' is undefined") !== -1 || + err.message.indexOf("'a' is not defined") !== -1); + }); + + it("Should have source = APP", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.source, BOOMR.plugins.Errors.SOURCE_APP); + }); + + it("Should have stack with the stack", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.isDefined(err.stack); + }); + + it("Should have type = 'ReferenceError' or 'Error'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + // Chrome, Firefox == ReferenceError, Safari = Error + assert.isTrue(err.type === "ReferenceError" || err.type === "Error"); + }); + + it("Should have via = GLOBAL_EXCEPTION_HANDLER", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.via, BOOMR.plugins.Errors.VIA_GLOBAL_EXCEPTION_HANDLER); + }); + + it("Should have columNumber to be a number if specified", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (typeof err.columnNumber !== "undefined") { + assert.isTrue(err.columnNumber >= 0); + } + else { + return this.skip(); + } + }); + + it("Should have lineNumber ~ " + (HEADER_LINES + 4), function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.lineNumber) { + assert.closeTo(err.lineNumber, HEADER_LINES + 4, 5); + } + else { + return this.skip(); + } + }); +}); diff --git a/tests/page-templates/14-errors/08-network.html b/tests/page-templates/14-errors/08-network.html new file mode 100644 index 000000000..79c920a60 --- /dev/null +++ b/tests/page-templates/14-errors/08-network.html @@ -0,0 +1,36 @@ +<%= header %> +<%= boomerangScriptMin %> + + +<%= footer %> diff --git a/tests/page-templates/14-errors/08-network.js b/tests/page-templates/14-errors/08-network.js new file mode 100644 index 000000000..82a048c44 --- /dev/null +++ b/tests/page-templates/14-errors/08-network.js @@ -0,0 +1,185 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["t"]); + +describe("e2e/14-errors/08-network", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + var C = BOOMR.utils.Compression; + + if (!BOOMR.plugins.AutoXHR) { + it("Skipping on non-AutoXHR supporting browser", function() { + return this.skip(); + }); + + return; + } + + it("Should have only sent three beacons, one page-load and two XHRs (if Fetch API is supported)", function(done) { + if (!t.isFetchApiSupported()) { + return this.skip(); + } + + this.timeout(10000); + t.ensureBeaconCount(done, 3); + }); + + it("Should have only sent two beacons, one page-load and one XHR (if Fetch API is not supported)", function(done) { + if (t.isFetchApiSupported()) { + return this.skip(); + } + + this.timeout(10000); + t.ensureBeaconCount(done, 2); + }); + + describe("Beacon 2 (xhr) for failed XHR request", function() { + var i = 1; + + it("Should have put the err on the XHR beacon", function() { + var b = tf.beacons[i]; + + assert.isDefined(b.err); + }); + + it("Should have had a single error", function() { + var b = tf.beacons[i]; + + assert.equal(C.jsUrlDecompress(b.err).length, 1); + }); + + it("Should have count = 1", function() { + var b = tf.beacons[i]; + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.count, 1); + }); + + it("Should have message = '/404'", function() { + var b = tf.beacons[i]; + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.include(err.message, "/404"); + }); + + it("Should have source = APP", function() { + var b = tf.beacons[i]; + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.source, BOOMR.plugins.Errors.SOURCE_APP); + }); + + it("Should have type = 'Error'", function() { + var b = tf.beacons[i]; + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.type, "Error"); + }); + + it("Should have via = NETWORK", function() { + var b = tf.beacons[i]; + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.via, BOOMR.plugins.Errors.VIA_NETWORK); + }); + + it("Should have code = 404", function() { + var b = tf.beacons[i]; + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.code, "404"); + }); + }); + + describe("Beacon 3 (xhr) for failed Fetch request (if Fetch API is supported)", function() { + var i = 2; + + it("Should have put the err on the XHR beacon", function() { + if (!t.isFetchApiSupported()) { + return this.skip(); + } + + var b = tf.beacons[i]; + + assert.isDefined(b.err); + }); + + it("Should have had a single error", function() { + if (!t.isFetchApiSupported()) { + return this.skip(); + } + + var b = tf.beacons[i]; + + assert.equal(C.jsUrlDecompress(b.err).length, 1); + }); + + it("Should have count = 1", function() { + if (!t.isFetchApiSupported()) { + return this.skip(); + } + + var b = tf.beacons[i]; + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.count, 1); + }); + + it("Should have message = '/404'", function() { + if (!t.isFetchApiSupported()) { + return this.skip(); + } + + var b = tf.beacons[i]; + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.include(err.message, "/404"); + }); + + it("Should have source = APP", function() { + if (!t.isFetchApiSupported()) { + return this.skip(); + } + + var b = tf.beacons[i]; + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.source, BOOMR.plugins.Errors.SOURCE_APP); + }); + + it("Should have type = 'Error'", function() { + if (!t.isFetchApiSupported()) { + return this.skip(); + } + + var b = tf.beacons[i]; + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.type, "Error"); + }); + + it("Should have via = NETWORK", function() { + if (!t.isFetchApiSupported()) { + return this.skip(); + } + + var b = tf.beacons[i]; + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.via, BOOMR.plugins.Errors.VIA_NETWORK); + }); + + it("Should have code = 404", function() { + if (!t.isFetchApiSupported()) { + return this.skip(); + } + + var b = tf.beacons[i]; + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.code, "404"); + }); + }); +}); diff --git a/tests/page-templates/14-errors/09-console.html b/tests/page-templates/14-errors/09-console.html new file mode 100644 index 000000000..e88ad9754 --- /dev/null +++ b/tests/page-templates/14-errors/09-console.html @@ -0,0 +1,22 @@ +<%= header %> +<%= boomerangScriptMin %> + + +<%= footer %> diff --git a/tests/page-templates/14-errors/09-console.js b/tests/page-templates/14-errors/09-console.js new file mode 100644 index 000000000..a3f7498f5 --- /dev/null +++ b/tests/page-templates/14-errors/09-console.js @@ -0,0 +1,146 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["errorFunction"]); + +describe("e2e/14-errors/09-console", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + var C = BOOMR.utils.Compression; + + it("Should have only sent one page load beacon", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 1); + }); + + it("Should have put the err on the page load beacon", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b.err); + }); + + it("Should have had three errors", function() { + var b = tf.lastBeacon(); + + assert.equal(C.jsUrlDecompress(b.err).length, 3); + }); + + it("Should have count = 1 for the first error", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.count, 1); + }); + + it("Should have fileName of the page (if set) for the first error", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.fileName) { + assert.include(err.fileName, "09-console.html"); + } + else { + return this.skip(); + } + }); + + it("Should have functionName of 'errorFunction' for the first error", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.functionName) { + assert.equal(err.functionName, "errorFunction"); + } + else { + return this.skip(); + } + }); + + it("Should have message = 'Console error!' for the first error", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.include(err.message, "Console error!"); + }); + + it("Should have source = APP for the first error", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.source, BOOMR.plugins.Errors.SOURCE_APP); + }); + + it("Should have stack with the stack for the first error", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.isDefined(err.stack); + }); + + it("Should not have BOOMR.window.console.error on the stack", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.notInclude(err.stack, "BOOMR.window.console.error"); + }); + + it("Should not have the wrapped function in the stack for the first error", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.notInclude(err.stack, "BOOMR_plugins_errors"); + }); + + it("Should have type = 'Error' for the first error", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.type, "Error"); + }); + + it("Should have via = CONSOLE for the first error", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.via, BOOMR.plugins.Errors.VIA_CONSOLE); + }); + + it("Should have columNumber to be a number if specified for the first error", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (typeof err.columnNumber !== "undefined") { + assert.isTrue(err.columnNumber >= 0); + } + else { + return this.skip(); + } + }); + + it("Should have lineNumber ~ " + (HEADER_LINES + 11) + " for the first error", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.lineNumber) { + assert.closeTo(err.lineNumber, HEADER_LINES + 11, 5); + } + else { + return this.skip(); + } + }); + + it("Should have message = 'Console error 2!' for the second error", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[1]; + + assert.include(err.message, "Console error 2!,second arg"); + }); + + it("Should have message = 'Console error 3!' for the third error", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[2]; + + assert.include(err.message, "Console error 3!,second arg"); + }); +}); diff --git a/tests/page-templates/14-errors/10-events-window.html b/tests/page-templates/14-errors/10-events-window.html new file mode 100644 index 000000000..9bd5bdc15 --- /dev/null +++ b/tests/page-templates/14-errors/10-events-window.html @@ -0,0 +1,45 @@ +<%= header %> +<%= boomerangScriptMin %> + + + + +<%= footer %> diff --git a/tests/page-templates/14-errors/10-events-window.js b/tests/page-templates/14-errors/10-events-window.js new file mode 100644 index 000000000..73d6fba07 --- /dev/null +++ b/tests/page-templates/14-errors/10-events-window.js @@ -0,0 +1,132 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["errorFunction", "callCount"]); + +describe("e2e/14-errors/10-events-window", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + var C = BOOMR.utils.Compression; + + if (!window.addEventListener) { + it("Skipping on browser that doesn't support addEventListener", function() { + return this.skip(); + }); + + return; + } + + it("Should have only sent one page load beacon", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 1); + }); + + it("Should have put the err on the page load beacon", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b.err); + }); + + it("Should have had a single error", function() { + var b = tf.lastBeacon(); + + assert.equal(C.jsUrlDecompress(b.err).length, 1); + }); + + it("Should have count = 1", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.count, 1); + }); + + it("Should have fileName of the page (if set)", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.fileName) { + assert.include(err.fileName, window.location.pathname.substring(window.location.pathname.lastIndexOf("/") + 1)); + } + else { + return this.skip(); + } + }); + + it("Should have functionName of 'errorFunction'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.functionName) { + assert.equal(err.functionName, "errorFunction"); + } + }); + + it("Should have message = 'a is not defined' or 'Can't find variable: a' or ''a' is undefined'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + // Chrome, Firefox == a is not defined, Safari = Can't find variable, Edge = 'a' is not defined + assert.isTrue( + err.message.indexOf("a is not defined") !== -1 || + err.message.indexOf("Can't find variable: a") !== -1 || + err.message.indexOf("'a' is undefined") !== -1 || + err.message.indexOf("'a' is not defined") !== -1); + }); + + it("Should have source = APP", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.source, BOOMR.plugins.Errors.SOURCE_APP); + }); + + it("Should have stack with the stack", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.isDefined(err.stack); + }); + + it("Should have type = 'ReferenceError' or 'Error'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.isTrue(err.type === "ReferenceError" || err.type === "Error"); + }); + + it("Should have via = EVENTHANDLER", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.via, BOOMR.plugins.Errors.VIA_EVENTHANDLER); + }); + + it("Should have columNumber to be a number if specified", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (typeof err.columnNumber !== "undefined") { + assert.isTrue(err.columnNumber >= 0); + } + else { + return this.skip(); + } + }); + + it("Should have lineNumber ~ " + (HEADER_LINES + 26), function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.lineNumber) { + assert.closeTo(err.lineNumber, HEADER_LINES + 26, 5); + } + else { + return this.skip(); + } + }); + + it("Should have only been called once after removeEventListener was called", function() { + assert.equal(1, window.callCount); + }); +}); diff --git a/tests/page-templates/14-errors/11-events-element.html b/tests/page-templates/14-errors/11-events-element.html new file mode 100644 index 000000000..0ba907ca2 --- /dev/null +++ b/tests/page-templates/14-errors/11-events-element.html @@ -0,0 +1,47 @@ +<%= header %> +<%= boomerangScriptMin %> + + + + +<%= footer %> diff --git a/tests/page-templates/14-errors/11-events-element.js b/tests/page-templates/14-errors/11-events-element.js new file mode 100644 index 000000000..84d35ded0 --- /dev/null +++ b/tests/page-templates/14-errors/11-events-element.js @@ -0,0 +1,131 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["img", "img2", "errorFunction2"]); + +describe("e2e/14-errors/11-events-element", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + var C = BOOMR.utils.Compression; + + if (!window.addEventListener) { + it("Skipping on browser that doesn't support addEventListener", function() { + return this.skip(); + }); + + return; + } + + it("Should have only sent one page load beacon", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 1); + }); + + it("Should have put the err on the page load beacon", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b.err); + }); + + it("Should have had a single error", function() { + var b = tf.lastBeacon(); + + assert.equal(C.jsUrlDecompress(b.err).length, 1); + }); + + it("Should have count = 1", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.count, 1); + }); + + it("Should have fileName of the page (if set)", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.fileName) { + assert.include(err.fileName, window.location.pathname.substring(window.location.pathname.lastIndexOf("/") + 1)); + } + else { + return this.skip(); + } + }); + + it("Should have functionName of 'errorFunction'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.functionName) { + assert.include(err.functionName, "errorFunction"); + } + else { + return this.skip(); + } + }); + + it("Should have message = 'a is not defined' or 'Can't find variable: a' or ''a' is undefined'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + // Chrome, Firefox == a is not defined, Safari = Can't find variable, Edge = 'a' is not defined + assert.isTrue( + err.message.indexOf("a is not defined") !== -1 || + err.message.indexOf("Can't find variable: a") !== -1 || + err.message.indexOf("'a' is undefined") !== -1 || + err.message.indexOf("'a' is not defined") !== -1); + }); + + it("Should have source = APP", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.source, BOOMR.plugins.Errors.SOURCE_APP); + }); + + it("Should have stack with the stack", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.isDefined(err.stack); + }); + + it("Should have type = 'ReferenceError' or 'Error'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.isTrue(err.type === "ReferenceError" || err.type === "Error"); + }); + + it("Should have via = EVENTHANDLER", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.via, BOOMR.plugins.Errors.VIA_EVENTHANDLER); + }); + + it("Should have columNumber to be a number if specified", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (typeof err.columnNumber !== "undefined") { + assert.isTrue(err.columnNumber >= 0); + } + else { + return this.skip(); + } + }); + + it("Should have lineNumber ~ " + (HEADER_LINES + 18), function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.lineNumber) { + assert.closeTo(err.lineNumber, HEADER_LINES + 18, 5); + } + else { + return this.skip(); + } + }); +}); diff --git a/tests/page-templates/14-errors/12-events-xhr.html b/tests/page-templates/14-errors/12-events-xhr.html new file mode 100644 index 000000000..83aefb5ca --- /dev/null +++ b/tests/page-templates/14-errors/12-events-xhr.html @@ -0,0 +1,33 @@ +<%= header %> +<%= boomerangScriptMin %> + + + + +<%= footer %> diff --git a/tests/page-templates/14-errors/12-events-xhr.js b/tests/page-templates/14-errors/12-events-xhr.js new file mode 100644 index 000000000..9dffa1dc1 --- /dev/null +++ b/tests/page-templates/14-errors/12-events-xhr.js @@ -0,0 +1,138 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["xhr"]); + +describe("e2e/14-errors/12-events-xhr", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + var C = BOOMR.utils.Compression; + + if (!BOOMR.plugins.AutoXHR) { + it("Skipping on non-AutoXHR supporting browser", function() { + return this.skip(); + }); + + return; + } + + it("Should have only sent one page load beacon", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 1); + }); + + it("Should have put the err on the page load beacon", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b.err); + }); + + it("Should have had a single error", function() { + var b = tf.lastBeacon(); + + assert.equal(BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err)).length, 1); + }); + + it("Should have count = 1", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.count, 1); + }); + + it("Should have fileName of the page (if set)", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.fileName) { + assert.include(err.fileName, window.location.pathname.substring(window.location.pathname.lastIndexOf("/") + 1)); + } + else { + return this.skip(); + } + }); + + it("Should have functionName of 'errorFunction'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.functionName) { + assert.include(err.functionName, "errorFunction"); + } + else { + return this.skip(); + } + }); + + it("Should have message = 'a is not defined' or 'Can't find variable: a' or ''a' is undefined'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + // Chrome, Firefox == a is not defined, Safari = Can't find variable, Edge = 'a' is not defined + assert.isTrue( + err.message.indexOf("a is not defined") !== -1 || + err.message.indexOf("Can't find variable: a") !== -1 || + err.message.indexOf("'a' is undefined") !== -1 || + err.message.indexOf("'a' is not defined") !== -1); + }); + + it("Should have source = APP", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.source, BOOMR.plugins.Errors.SOURCE_APP); + }); + + it("Should have stack with the stack", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.isDefined(err.stack); + }); + + it("Should have not have BOOMR_plugins_errors_wrap on the stack", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.notInclude(err.stack, "BOOMR_plugins_errors_wrap"); + }); + + it("Should have type = 'ReferenceError' or 'Error'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.isTrue(err.type === "ReferenceError" || err.type === "Error"); + }); + + it("Should have via = EVENTHANDLER", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.via, BOOMR.plugins.Errors.VIA_EVENTHANDLER); + }); + + it("Should have columNumber to be a number if specified", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (typeof err.columnNumber !== "undefined") { + assert.isTrue(err.columnNumber >= 0); + } + else { + return this.skip(); + } + }); + + it("Should have lineNumber ~ " + (HEADER_LINES + 20), function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.lineNumber) { + assert.closeTo(err.lineNumber, HEADER_LINES + 20, 5); + } + else { + return this.skip(); + } + }); +}); diff --git a/tests/page-templates/14-errors/13-settimeout.html b/tests/page-templates/14-errors/13-settimeout.html new file mode 100644 index 000000000..ce8c3bef8 --- /dev/null +++ b/tests/page-templates/14-errors/13-settimeout.html @@ -0,0 +1,27 @@ +<%= header %> +<%= boomerangScriptMin %> + + + + +<%= footer %> diff --git a/tests/page-templates/14-errors/13-settimeout.js b/tests/page-templates/14-errors/13-settimeout.js new file mode 100644 index 000000000..6cd074a6f --- /dev/null +++ b/tests/page-templates/14-errors/13-settimeout.js @@ -0,0 +1,142 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["bar"]); + +describe("e2e/14-errors/13-settimeout", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + var C = BOOMR.utils.Compression; + + if (typeof window.setTimeout === "object") { + it("Skipping on IE <= 8, as setTimeout isn't overwritable easily", function() { + return this.skip(); + }); + + return; + } + + it("Should have only sent one page load beacon", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 1); + }); + + it("Should have put the err on the page load beacon", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b.err); + }); + + it("Should have had a single error", function() { + var b = tf.lastBeacon(); + + assert.equal(BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err)).length, 1); + }); + + it("Should have count = 1", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.count, 1); + }); + + it("Should have fileName of the page (if set)", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.fileName) { + assert.include(err.fileName, window.location.pathname.substring(window.location.pathname.lastIndexOf("/") + 1)); + } + else { + return this.skip(); + } + }); + + it("Should have functionName of 'errorFunction'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.functionName) { + assert.include(err.functionName, "errorFunction"); + } + else { + return this.skip(); + } + }); + + it("Should have message = 'a is not defined' or 'Can't find variable: a' or ''a' is undefined'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + // Chrome, Firefox == a is not defined, Safari = Can't find variable, Edge = 'a' is not defined + assert.isTrue( + err.message.indexOf("a is not defined") !== -1 || + err.message.indexOf("Can't find variable: a") !== -1 || + err.message.indexOf("'a' is undefined") !== -1 || + err.message.indexOf("'a' is not defined") !== -1); + }); + + it("Should have source = APP", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.source, BOOMR.plugins.Errors.SOURCE_APP); + }); + + it("Should have stack with the stack", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.isDefined(err.stack); + }); + + it("Should have not have BOOMR_plugins_errors_wrap on the stack", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.notInclude(err.stack, "BOOMR_plugins_errors_wrap"); + }); + + it("Should have type = 'ReferenceError' or 'Error'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.isTrue(err.type === "ReferenceError" || err.type === "Error"); + }); + + it("Should have via = TIMEOUT", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.via, BOOMR.plugins.Errors.VIA_TIMEOUT); + }); + + it("Should have columNumber to be a number if specified", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (typeof err.columnNumber !== "undefined") { + assert.isTrue(err.columnNumber >= 0); + } + else { + return this.skip(); + } + }); + + it("Should have lineNumber ~ " + (HEADER_LINES + 14), function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.lineNumber) { + assert.closeTo(err.lineNumber, HEADER_LINES + 14, 5); + } + else { + return this.skip(); + } + }); + + it("Should have had 'bar' set on window", function() { + assert.equal(window.bar, 1); + }); +}); diff --git a/tests/page-templates/14-errors/14-setinterval.html b/tests/page-templates/14-errors/14-setinterval.html new file mode 100644 index 000000000..c09f36547 --- /dev/null +++ b/tests/page-templates/14-errors/14-setinterval.html @@ -0,0 +1,24 @@ +<%= header %> +<%= boomerangScriptMin %> + + + + +<%= footer %> diff --git a/tests/page-templates/14-errors/14-setinterval.js b/tests/page-templates/14-errors/14-setinterval.js new file mode 100644 index 000000000..a2a364d74 --- /dev/null +++ b/tests/page-templates/14-errors/14-setinterval.js @@ -0,0 +1,138 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["intId"]); + +describe("e2e/14-errors/14-setinterval", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + var C = BOOMR.utils.Compression; + + if (typeof window.setInterval === "object") { + it("Skipping on IE <= 8, as setInterval isn't overwritable easily", function() { + return this.skip(); + }); + + return; + } + + it("Should have only sent one page load beacon", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 1); + }); + + it("Should have put the err on the page load beacon", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b.err); + }); + + it("Should have had a single error", function() { + var b = tf.lastBeacon(); + + assert.equal(BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err)).length, 1); + }); + + it("Should have count >= 1", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.isTrue(err.count >= 1); + }); + + it("Should have fileName of the page (if set)", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.fileName) { + assert.include(err.fileName, window.location.pathname.substring(window.location.pathname.lastIndexOf("/") + 1)); + } + else { + return this.skip(); + } + }); + + it("Should have functionName of 'errorFunction'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.functionName) { + assert.include(err.functionName, "errorFunction"); + } + else { + return this.skip(); + } + }); + + it("Should have message = 'a is not defined' or 'Can't find variable: a' or ''a' is undefined'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + // Chrome, Firefox == a is not defined, Safari = Can't find variable, Edge = 'a' is not defined + assert.isTrue( + err.message.indexOf("a is not defined") !== -1 || + err.message.indexOf("Can't find variable: a") !== -1 || + err.message.indexOf("'a' is undefined") !== -1 || + err.message.indexOf("'a' is not defined") !== -1); + }); + + it("Should have source = APP", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.source, BOOMR.plugins.Errors.SOURCE_APP); + }); + + it("Should have stack with the stack", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.isDefined(err.stack); + }); + + it("Should have not have BOOMR_plugins_errors_wrap on the stack", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.notInclude(err.stack, "BOOMR_plugins_errors_wrap"); + }); + + it("Should have type = 'ReferenceError' or 'Error'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.isTrue(err.type === "ReferenceError" || err.type === "Error"); + }); + + it("Should have via = TIMEOUT", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.via, BOOMR.plugins.Errors.VIA_TIMEOUT); + }); + + it("Should have columNumber to be a number if specified", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (typeof err.columnNumber !== "undefined") { + assert.isTrue(err.columnNumber >= 0); + } + else { + return this.skip(); + } + }); + + it("Should have lineNumber ~ " + (HEADER_LINES + 16), function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.lineNumber) { + assert.closeTo(err.lineNumber, (HEADER_LINES + 16), 5); + } + else { + return this.skip(); + } + }); +}); diff --git a/tests/page-templates/14-errors/15-send-exception.html b/tests/page-templates/14-errors/15-send-exception.html new file mode 100644 index 000000000..57edab72b --- /dev/null +++ b/tests/page-templates/14-errors/15-send-exception.html @@ -0,0 +1,24 @@ +<%= header %> +<%= boomerangScriptMin %> + + + +<%= footer %> diff --git a/tests/page-templates/14-errors/15-send-exception.js b/tests/page-templates/14-errors/15-send-exception.js new file mode 100644 index 000000000..58ad862ab --- /dev/null +++ b/tests/page-templates/14-errors/15-send-exception.js @@ -0,0 +1,127 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["JSON3", "errorFunction"]); + +describe("e2e/14-errors/15-send-exception", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + var C = BOOMR.utils.Compression; + + it("Should have sent a single beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have put the err on the beacon", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b.err); + }); + + it("Should have had a single error", function() { + var b = tf.lastBeacon(); + + assert.equal(C.jsUrlDecompress(b.err).length, 1); + }); + + it("Should have count = 1", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.count, 1); + }); + + it("Should have fileName of the page (if set)", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.fileName) { + assert.include(err.fileName, window.location.pathname.substring(window.location.pathname.lastIndexOf("/") + 1)); + } + else { + return this.skip(); + } + }); + + it("Should have functionName of 'errorFunction'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.functionName) { + assert.equal(err.functionName, "errorFunction"); + } + else { + return this.skip(); + } + }); + + it("Should have message = 'a is not defined' or 'Can't find variable: a' or ''a' is undefined'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + // Skip for IE6 + if (err.message === "[object Error]") { + return this.skip(); + } + + // Chrome, Firefox == a is not defined, Safari = Can't find variable, Edge = 'a' is not defined + assert.isTrue( + err.message.indexOf("a is not defined") !== -1 || + err.message.indexOf("Can't find variable: a") !== -1 || + err.message.indexOf("'a' is undefined") !== -1 || + err.message.indexOf("'a' is not defined") !== -1); + }); + + it("Should have source = APP", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.source, BOOMR.plugins.Errors.SOURCE_APP); + }); + + it("Should have stack with the stack", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.isDefined(err.stack); + }); + + it("Should have type = 'ReferenceError' or 'Error'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.isTrue(err.type === "ReferenceError" || err.type === "Error"); + }); + + it("Should have via = APP", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.via, BOOMR.plugins.Errors.VIA_APP); + }); + + it("Should have columNumber to be a number if specified", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (typeof err.columnNumber !== "undefined") { + assert.isTrue(err.columnNumber >= 0); + } + else { + return this.skip(); + } + }); + + it("Should have lineNumber ~ " + (HEADER_LINES + 5), function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.lineNumber) { + assert.closeTo(err.lineNumber, HEADER_LINES + 5, 5); + } + else { + return this.skip(); + } + }); +}); diff --git a/tests/page-templates/14-errors/16-wrap.html b/tests/page-templates/14-errors/16-wrap.html new file mode 100644 index 000000000..f26b647b2 --- /dev/null +++ b/tests/page-templates/14-errors/16-wrap.html @@ -0,0 +1,30 @@ +<%= header %> +<%= boomerangScriptMin %> + + +<%= footer %> diff --git a/tests/page-templates/14-errors/16-wrap.js b/tests/page-templates/14-errors/16-wrap.js new file mode 100644 index 000000000..af8ac7c09 --- /dev/null +++ b/tests/page-templates/14-errors/16-wrap.js @@ -0,0 +1,138 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["errorFunction", "goodFunction", "wrappedBad"]); + +describe("e2e/14-errors/16-wrap", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + var C = BOOMR.utils.Compression; + + it("Should have sent a single beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have put the err on the beacon", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b.err); + }); + + it("Should have had a single error", function() { + var b = tf.lastBeacon(); + + assert.equal(C.jsUrlDecompress(b.err).length, 1); + }); + + it("Should have count = 1", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.count, 1); + }); + + it("Should have fileName of the page (if set)", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.fileName) { + assert.include(err.fileName, window.location.pathname.substring(window.location.pathname.lastIndexOf("/") + 1)); + } + else { + return this.skip(); + } + }); + + it("Should have functionName of 'errorFunction'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.functionName) { + assert.equal(err.functionName, "errorFunction"); + } + else { + return this.skip(); + } + }); + + it("Should have message = 'a is not defined' or 'Can't find variable: a' or ''a' is undefined'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + // Skip for IE6 + if (err.message === "[object Error]") { + return this.skip(); + } + + // Chrome, Firefox == a is not defined, Safari = Can't find variable, Edge = 'a' is not defined + assert.isTrue( + err.message.indexOf("a is not defined") !== -1 || + err.message.indexOf("Can't find variable: a") !== -1 || + err.message.indexOf("'a' is undefined") !== -1 || + err.message.indexOf("'a' is not defined") !== -1); + }); + + it("Should have source = APP", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.source, BOOMR.plugins.Errors.SOURCE_APP); + }); + + it("Should have stack with the stack", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.isDefined(err.stack); + }); + + it("Should have not have BOOMR_plugins_errors_wrap on the stack", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.notInclude(err.stack, "BOOMR_plugins_errors_wrap"); + }); + + it("Should have type = 'ReferenceError' or 'Error'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.isTrue(err.type === "ReferenceError" || err.type === "Error"); + }); + + it("Should have via = APP", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.via, BOOMR.plugins.Errors.VIA_APP); + }); + + it("Should have columNumber to be a number if specified", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (typeof err.columnNumber !== "undefined") { + assert.isTrue(err.columnNumber >= 0); + } + else { + return this.skip(); + } + }); + + it("Should have lineNumber ~ " + (HEADER_LINES + 3), function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.lineNumber) { + assert.closeTo(err.lineNumber, HEADER_LINES + 3, 5); + } + else { + return this.skip(); + } + }); + + it("Should have not set ranAnyways", function() { + assert.isUndefined(window.ranAnyways); + }); +}); diff --git a/tests/page-templates/14-errors/17-test.html b/tests/page-templates/14-errors/17-test.html new file mode 100644 index 000000000..4d5e8c7d4 --- /dev/null +++ b/tests/page-templates/14-errors/17-test.html @@ -0,0 +1,54 @@ +<%= header %> +<%= boomerangScriptMin %> + + + +<%= footer %> diff --git a/tests/page-templates/14-errors/17-test.js b/tests/page-templates/14-errors/17-test.js new file mode 100644 index 000000000..36fc9bcdc --- /dev/null +++ b/tests/page-templates/14-errors/17-test.js @@ -0,0 +1,159 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["JSON3", "errorFunction", "goodFunction", "errorFunctionThrown", "foo", "result", "testResult1", "testResult2", "testResult3", "result4", "testResult4"]); + +describe("e2e/14-errors/17-test", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + var C = BOOMR.utils.Compression; + + it("Should have sent a single beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have put the err on the beacon", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b.err); + }); + + it("Should have had a single error", function() { + var b = tf.lastBeacon(); + + assert.equal(C.jsUrlDecompress(b.err).length, 1); + }); + + it("Should have count = 1", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.count, 1); + }); + + it("Should have fileName of the page (if set)", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.fileName) { + assert.include(err.fileName, window.location.pathname.substring(window.location.pathname.lastIndexOf("/") + 1)); + } + else { + return this.skip(); + } + }); + + it("Should have functionName of 'errorFunction'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.functionName) { + assert.include(err.functionName, "errorFunction"); + } + else { + return this.skip(); + } + }); + + it("Should have message = 'a is not defined' or 'Can't find variable: a' or ''a' is undefined'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + // Skip for IE6 + if (err.message === "[object Error]") { + return this.skip(); + } + + // Chrome, Firefox == a is not defined, Safari = Can't find variable, Edge = 'a' is not defined + assert.isTrue( + err.message.indexOf("a is not defined") !== -1 || + err.message.indexOf("Can't find variable: a") !== -1 || + err.message.indexOf("'a' is undefined") !== -1 || + err.message.indexOf("'a' is not defined") !== -1); + }); + + it("Should have source = APP", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.source, BOOMR.plugins.Errors.SOURCE_APP); + }); + + it("Should have stack with the stack", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.isDefined(err.stack); + }); + + it("Should have not have BOOMR_plugins_errors_wrap on the stack", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.notInclude(err.stack, "BOOMR_plugins_errors_wrap"); + }); + + it("Should have type = 'ReferenceError' or 'Error'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.isTrue(err.type === "ReferenceError" || err.type === "Error"); + }); + + it("Should have via = APP", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.via, BOOMR.plugins.Errors.VIA_APP); + }); + + it("Should have columNumber to be a number if specified", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (typeof err.columnNumber !== "undefined") { + assert.isTrue(err.columnNumber >= 0); + } + else { + return this.skip(); + } + }); + + it("Should have lineNumber ~ " + (HEADER_LINES + 4), function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.lineNumber) { + assert.closeTo(err.lineNumber, HEADER_LINES + 4, 5); + } + else { + return this.skip(); + } + }); + + it("Should have thrown an exception for errorFunction", function() { + assert.isDefined(window.errorFunctionThrown); + assert.isTrue(window.errorFunctionThrown.reported); + }); + + it("Should have saved the result of the good function in testResult1", function() { + assert.equal(window.testResult1, 2); + assert.equal(window.result, 2); + }); + + it("Should have saved the result of the good function in testResult2", function() { + assert.equal(window.testResult2, 2); + assert.equal(window.foo.result, 2); + }); + + it("Should have saved the result of the good function in testResult3", function() { + assert.equal(window.testResult3, 3); + assert.equal(window.foo.result3, 3); + }); + + it("Should have saved the result of the good function in testResult4", function() { + assert.equal(window.testResult4, 4); + assert.equal(window.result4, 4); + }); +}); diff --git a/tests/page-templates/14-errors/18-maxerrors.html b/tests/page-templates/14-errors/18-maxerrors.html new file mode 100644 index 000000000..b90516949 --- /dev/null +++ b/tests/page-templates/14-errors/18-maxerrors.html @@ -0,0 +1,22 @@ +<%= header %> +<%= boomerangScriptMin %> + + +<%= footer %> diff --git a/tests/page-templates/14-errors/18-maxerrors.js b/tests/page-templates/14-errors/18-maxerrors.js new file mode 100644 index 000000000..6caa2ae41 --- /dev/null +++ b/tests/page-templates/14-errors/18-maxerrors.js @@ -0,0 +1,130 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["errorFunction"]); + +describe("e2e/14-errors/18-maxerrors", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + var C = BOOMR.utils.Compression; + + it("Should have sent a single beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have put the err on the beacon", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b.err); + }); + + it("Should have had two errors (not 3!)", function() { + var b = tf.lastBeacon(); + + assert.equal(C.jsUrlDecompress(b.err).length, 2); + }); + + it("Should have count = 1", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.count, 1); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[1]; + + assert.equal(err.count, 1); + }); + + it("Should have fileName of the page (if set)", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.fileName) { + assert.include(err.fileName, window.location.pathname.substring(window.location.pathname.lastIndexOf("/") + 1)); + } + else { + return this.skip(); + } + }); + + it("Should have functionName of 'errorFunction'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.functionName) { + assert.include(err.functionName, "errorFunction"); + } + else { + return this.skip(); + } + }); + + it("Should have message = 'MSG1' and 'MSG2'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.message, "MSG1"); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[1]; + + assert.equal(err.message, "MSG2"); + }); + + it("Should have source = APP", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.source, BOOMR.plugins.Errors.SOURCE_APP); + }); + + it("Should have stack with the stack", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.isDefined(err.stack); + }); + + it("Should have not have BOOMR_plugins_errors_wrap on the stack", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.notInclude(err.stack, "BOOMR_plugins_errors_wrap"); + }); + + it("Should have type = 'Error'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.type, "Error"); + }); + + it("Should have via = APP", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.via, BOOMR.plugins.Errors.VIA_APP); + }); + + it("Should have columNumber to be a number if specified", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (typeof err.columnNumber !== "undefined") { + assert.isTrue(err.columnNumber >= 0); + } + else { + return this.skip(); + } + }); + + it("Should have lineNumber ~ " + (HEADER_LINES + 3), function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.lineNumber) { + assert.closeTo(err.lineNumber, HEADER_LINES + 3, 5); + } + else { + return this.skip(); + } + }); +}); diff --git a/tests/page-templates/14-errors/19-boomr-errors.html b/tests/page-templates/14-errors/19-boomr-errors.html new file mode 100644 index 000000000..0f450cda9 --- /dev/null +++ b/tests/page-templates/14-errors/19-boomr-errors.html @@ -0,0 +1,33 @@ +<%= header %> +<%= boomerangScriptMin %> + + +<%= footer %> diff --git a/tests/page-templates/14-errors/19-boomr-errors.js b/tests/page-templates/14-errors/19-boomr-errors.js new file mode 100644 index 000000000..70c216048 --- /dev/null +++ b/tests/page-templates/14-errors/19-boomr-errors.js @@ -0,0 +1,196 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["errorFunction"]); + +describe("e2e/14-errors/19-boomr-errors", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + var C = BOOMR.utils.Compression; + + it("Should have sent a single beacon", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have put the err on the beacon", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b.err); + }); + + it("Should have had 4 errors", function() { + var b = tf.lastBeacon(); + + assert.equal(C.jsUrlDecompress(b.err).length, 4); + }); + + it("Should have each error with a count = 1", function() { + var b = tf.lastBeacon(); + var errs = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err)); + + for (var i = 0; i < errs.length; i++) { + assert.equal(errs[i].count, 1); + } + }); + + it("Should have fileName of the page (if set)", function() { + var b = tf.lastBeacon(), + found = false; + var errs = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err)); + + for (var i = 0; i < errs.length; i++) { + if (errs[i].fileName) { + assert.include(errs[i].fileName, window.location.pathname.substring(window.location.pathname.lastIndexOf("/") + 1)); + found = true; + } + } + + if (!found) { + return this.skip(); + } + }); + + it("Should have error #1 functionName of 'errorFunction'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.functionName) { + assert.include(err.functionName, "errorFunction"); + } + else { + return this.skip(); + } + }); + + it("Should have error #2 functionName of 'BOOMRtest2'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[1]; + + if (err.functionName) { + assert.include(err.functionName, "BOOMRtest2"); + } + else { + return this.skip(); + } + }); + + it("Should have error #3 functionName of 'BOOMRtest3'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[2]; + + if (err.functionName) { + assert.include(err.functionName, "BOOMRtest3"); + } + else { + return this.skip(); + } + }); + + it("Should have error #4 functionName of 'BOOMRtest4'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[3]; + + if (err.functionName) { + assert.include(err.functionName, "BOOMRtest4"); + } + else { + return this.skip(); + } + }); + + it("Should have error #1 message", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.isDefined(err.message); + }); + + it("Should have error #2 message = 'Fault 2'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[1]; + + assert.equal(err.message, "Fault 2"); + }); + + it("Should have error #3 message = 'Fault 3'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[2]; + + assert.equal(err.message, "Fault 3"); + }); + + it("Should have error #4 message = 'Fault 4'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[3]; + + assert.equal(err.message, "Fault 4"); + }); + + it("Should have source = BOOMERANG", function() { + var b = tf.lastBeacon(); + var errs = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err)); + + for (var i = 0; i < errs.length; i++) { + assert.equal(errs[i].source, BOOMR.plugins.Errors.SOURCE_BOOMERANG); + } + }); + + it("Should have stack with the stack", function() { + var b = tf.lastBeacon(); + var errs = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err)); + + for (var i = 0; i < errs.length; i++) { + assert.isDefined(errs[i].stack); + } + }); + + it("Should have not have BOOMR_plugins_errors_wrap on the stack", function() { + var b = tf.lastBeacon(); + var errs = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err)); + + for (var i = 0; i < errs.length; i++) { + assert.notInclude(errs[i].stack, "BOOMR_plugins_errors_wrap"); + } + }); + + it("Should have not have BOOMR_addError on the stack", function() { + var b = tf.lastBeacon(); + var errs = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err)); + + for (var i = 0; i < errs.length; i++) { + assert.notInclude(errs[i].stack, "BOOMR_addError"); + } + }); + + it("Should have via = APP", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.via, BOOMR.plugins.Errors.VIA_APP); + }); + + it("Should have columNumber to be a number if specified", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (typeof err.columnNumber !== "undefined") { + assert.isTrue(err.columnNumber >= 0); + } + else { + return this.skip(); + } + }); + + it("Should have lineNumber ~ " + (HEADER_LINES + 15), function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.lineNumber) { + assert.closeTo(err.lineNumber, HEADER_LINES + 15, 5); + } + else { + return this.skip(); + } + }); +}); diff --git a/tests/page-templates/14-errors/20-remove-event-listener.html b/tests/page-templates/14-errors/20-remove-event-listener.html new file mode 100644 index 000000000..b4fae0bfa --- /dev/null +++ b/tests/page-templates/14-errors/20-remove-event-listener.html @@ -0,0 +1,142 @@ +<%= header %> +<%= boomerangScriptMin %> + + + + +<%= footer %> diff --git a/tests/page-templates/14-errors/20-remove-event-listener.js b/tests/page-templates/14-errors/20-remove-event-listener.js new file mode 100644 index 000000000..a0545c19c --- /dev/null +++ b/tests/page-templates/14-errors/20-remove-event-listener.js @@ -0,0 +1,95 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["img1", "img2", "img3", "img4", "img5", "img6", "img7", "img8", "errorFunction1", "errorFunction2", "errorFunction3", "errorFunction4", "errorFunction5", "options", "aELPassiveSupported", "aELCaptureSupported"]); + +describe("e2e/14-errors/20-remove-event-listener", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + var C = BOOMR.utils.Compression; + + function getError(error, regex) { + var errors = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(error)); + + for (var i = 0; i < errors.length; i++) { + if (regex.test(errors[i].stack)) { + return errors[i]; + } + } + } + + if (!window.addEventListener) { + it("Skipping on browser that doesn't support addEventListener", function() { + return this.skip(); + }); + + return; + } + + it("Should have only sent one page load beacon", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 1); + }); + + it("Should have put the err on the page load beacon", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b.err); + }); + + it("Should have had 4 errors", function() { + var b = tf.lastBeacon(); + + assert.equal(C.jsUrlDecompress(b.err).length, 4); + }); + + it("Should have errorFunction1 have count = 1", function() { + var b = tf.lastBeacon(); + var err = getError(b.err, /errorFunction1/); + + assert.equal((err && err.count) || 0, 1); + }); + + it("Should have errorFunction2 have count = 1", function() { + var b = tf.lastBeacon(); + var err = getError(b.err, /errorFunction2/); + + assert.equal((err && err.count) || 0, 1); + }); + + it("Should have errorFunction3 have count = 1", function() { + var b = tf.lastBeacon(); + var err = getError(b.err, /errorFunction3/); + + assert.equal((err && err.count) || 0, 1); + }); + + it("Should have errorFunction4 have count = 1", function() { + var b = tf.lastBeacon(); + var err = getError(b.err, /errorFunction4/); + + assert.equal((err && err.count) || 0, 1); + }); + + it("Should not have errorFunction5", function() { + var b = tf.lastBeacon(); + var err = getError(b.err, /errorFunction5/); + + assert.isUndefined(err); + }); + + // informational + it("INFO: addEventListener passive flag was tested for errorFunction5", function() { + if (!window.aELPassiveSupported) { + this.skip(); + } + }); + + // informational + it("INFO: addEventListener capture flag was tested for errorFunction5", function() { + if (!window.aELCaptureSupported) { + this.skip(); + } + }); +}); diff --git a/tests/page-templates/14-errors/21-max-stack.html b/tests/page-templates/14-errors/21-max-stack.html new file mode 100644 index 000000000..f84b7e3f9 --- /dev/null +++ b/tests/page-templates/14-errors/21-max-stack.html @@ -0,0 +1,23 @@ +<%= header %> +<%= boomerangScriptMin %> + + +<%= footer %> diff --git a/tests/page-templates/14-errors/21-max-stack.js b/tests/page-templates/14-errors/21-max-stack.js new file mode 100644 index 000000000..a56c3f1ca --- /dev/null +++ b/tests/page-templates/14-errors/21-max-stack.js @@ -0,0 +1,35 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["stack", "i"]); + +describe("e2e/14-errors/21-max-stack", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + var C = BOOMR.utils.Compression; + + it("Should have sent a single beacon", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have put the err on the beacon", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b.err); + }); + + it("Should have had 1 error", function() { + var b = tf.lastBeacon(); + + assert.equal(C.jsUrlDecompress(b.err).length, 1); + }); + + it("Should have had a stack with a max length of around 5000 chars", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + // allow for some wiggle room in message length, but close to 5000 + assert.closeTo(err.stack.length, "a is not defined".length + 5000, 10, "stack: " + err.stack); + }); +}); diff --git a/tests/page-templates/14-errors/22-empty-message-error.html b/tests/page-templates/14-errors/22-empty-message-error.html new file mode 100644 index 000000000..7f8119257 --- /dev/null +++ b/tests/page-templates/14-errors/22-empty-message-error.html @@ -0,0 +1,20 @@ +<%= header %> +<%= boomerangScript %> + + + +<%= footer %> diff --git a/tests/page-templates/14-errors/22-empty-message-error.js b/tests/page-templates/14-errors/22-empty-message-error.js new file mode 100644 index 000000000..22d840478 --- /dev/null +++ b/tests/page-templates/14-errors/22-empty-message-error.js @@ -0,0 +1,50 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert,describe,it */ + +describe("e2e/14-errors/22-empty-message-error", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + var C = BOOMR.utils.Compression; + + it("Should have sent two beacons", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 2); + }); + + it("Should have put the err on the second beacon", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b.err); + }); + + it("Should have an error count = 1", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.count, 1); + }); + + it("Should have fileName of the page (if set)", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.fileName) { + assert.include(err.fileName, window.location.pathname.substring(window.location.pathname.lastIndexOf("/") + 1)); + } + else { + return this.skip(); + } + }); + + it("Should have functionName of 'afterFirstBeacon'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.functionName) { + assert.include(err.functionName, "afterFirstBeacon"); + } + else { + return this.skip(); + } + }); +}); diff --git a/tests/page-templates/14-errors/23-autorun-false.html b/tests/page-templates/14-errors/23-autorun-false.html new file mode 100644 index 000000000..3ea48c449 --- /dev/null +++ b/tests/page-templates/14-errors/23-autorun-false.html @@ -0,0 +1,20 @@ +<%= header %> +<%= boomerangScript %> + + + +<%= footer %> diff --git a/tests/page-templates/14-errors/23-autorun-false.js b/tests/page-templates/14-errors/23-autorun-false.js new file mode 100644 index 000000000..b9cdfd89c --- /dev/null +++ b/tests/page-templates/14-errors/23-autorun-false.js @@ -0,0 +1,84 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert,describe,it */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["a"]); + +describe("e2e/14-errors/23-autorun-false", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + var C = BOOMR.utils.Compression; + + it("Should have sent one beacon", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 1); + }); + + it("Should have http.initiator = error", function() { + var b = tf.lastBeacon(); + + assert.equal(b["http.initiator"], "error"); + }); + + it("Should have put err on the beacon", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b.err); + }); + + it("Should have an error count = 1", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.count, 1); + }); + + it("Should have fileName of the page (if set)", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.fileName) { + assert.include(err.fileName, window.location.pathname.substring(window.location.pathname.lastIndexOf("/") + 1)); + } + else { + return this.skip(); + } + }); + + it("Should have functionName of 'Global code' (if set)", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.functionName) { + // not set in Chrome and FF, Edge = 'Global code', Safari = 'global code' + assert.include(err.functionName.toLowerCase(), "global code"); + } + else { + return this.skip(); + } + }); + + it("Should have rt.end", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["rt.end"]); + }); + + it("Should have rt.sl = 1", function() { + var b = tf.lastBeacon(); + + assert.equal(b["rt.sl"], 1); + }); + + it("Should have rt.tstart", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["rt.tstart"]); + }); + + it("Should have rt.start = manual", function() { + var b = tf.lastBeacon(); + + assert.equal(b["rt.start"], "manual"); + }); +}); diff --git a/tests/page-templates/14-errors/23-duplicate-event-listener.html b/tests/page-templates/14-errors/23-duplicate-event-listener.html new file mode 100644 index 000000000..ea7fc56f5 --- /dev/null +++ b/tests/page-templates/14-errors/23-duplicate-event-listener.html @@ -0,0 +1,89 @@ +<%= header %> +<%= boomerangScriptMin %> + + + + +<%= footer %> diff --git a/tests/page-templates/14-errors/23-duplicate-event-listener.js b/tests/page-templates/14-errors/23-duplicate-event-listener.js new file mode 100644 index 000000000..79a530c75 --- /dev/null +++ b/tests/page-templates/14-errors/23-duplicate-event-listener.js @@ -0,0 +1,138 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["img1", "img2", "img3", "img4", "img5", "img6", "img7", "img8", "errorFunction1", "errorFunction2", "errorFunction3", "errorFunction4"]); + +describe("e2e/14-errors/23-duplicate-event-listener", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + var C = BOOMR.utils.Compression; + + function findError(errs, name) { + for (var i = 0; i < errs.length; i++) { + if (errs[i] && errs[i].functionName && errs[i].functionName.indexOf(name) !== -1) { + return errs[i]; + } + } + } + + if (!window.addEventListener) { + it("Skipping on browser that doesn't support addEventListener", function() { + return this.skip(); + }); + + return; + } + + it("Should have only sent one page load beacon", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 1); + }); + + it("Should have put the err on the page load beacon", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b.err); + }); + + it("Should have had 4 errors (if the error stack is supported)", function() { + var b = tf.lastBeacon(); + var errs = C.jsUrlDecompress(b.err); + + if (errs.length === 1 && errs[0].n === 5) { + // older browsers won't have function name so they'll all collapse + // into the same error + return this.skip(); + } + + assert.equal(errs.length, 4); + }); + + it("Should have had 1 error (if the error stack is NOT supported)", function() { + var b = tf.lastBeacon(); + var errs = C.jsUrlDecompress(b.err); + + if (errs.length === 4) { + return this.skip(); + } + + assert.equal(errs.length, 1); + + var err = BOOMR.plugins.Errors.decompressErrors(errs)[0]; + + assert.equal(err.count, 5); + }); + + it("Should have errorFunction1 have count = 2 (if the error stack is supported)", function() { + var b = tf.lastBeacon(); + var errs = C.jsUrlDecompress(b.err); + + if (errs.length === 1 && errs[0].n === 5) { + // older browsers won't have function name so they'll all collapse + // into the same error + return this.skip(); + } + + var err = findError(BOOMR.plugins.Errors.decompressErrors(errs), "errorFunction1"); + + assert.isDefined(err); + assert.include(err.functionName, "errorFunction1"); + assert.include(err.stack, "errorFunction1"); + assert.equal(err.count, 2); + }); + + it("Should have errorFunction2 have count = 1 (if the error stack is supported)", function() { + var b = tf.lastBeacon(); + var errs = C.jsUrlDecompress(b.err); + + if (errs.length === 1 && errs[0].n === 5) { + // older browsers won't have function name so they'll all collapse + // into the same error + return this.skip(); + } + + var err = findError(BOOMR.plugins.Errors.decompressErrors(errs), "errorFunction2"); + + assert.isDefined(err); + assert.include(err.functionName, "errorFunction2"); + assert.include(err.stack, "errorFunction2"); + assert.equal(err.count, 1); + }); + + it("Should have errorFunction3 have count = 1 (if the error stack is supported)", function() { + var b = tf.lastBeacon(); + var errs = C.jsUrlDecompress(b.err); + + if (errs.length === 1 && errs[0].n === 5) { + // older browsers won't have function name so they'll all collapse + // into the same error + return this.skip(); + } + + var err = findError(BOOMR.plugins.Errors.decompressErrors(errs), "errorFunction3"); + + assert.isDefined(err); + assert.include(err.functionName, "errorFunction3"); + assert.include(err.stack, "errorFunction3"); + assert.equal(err.count, 1); + }); + + it("Should have errorFunction4 have count = 1 (if the error stack is supported)", function() { + var b = tf.lastBeacon(); + var errs = C.jsUrlDecompress(b.err); + + if (errs.length === 1 && errs[0].n === 5) { + // older browsers won't have function name so they'll all collapse + // into the same error + return this.skip(); + } + + var err = findError(BOOMR.plugins.Errors.decompressErrors(errs), "errorFunction4"); + + assert.isDefined(err); + assert.include(err.functionName, "errorFunction4"); + assert.include(err.stack, "errorFunction4"); + assert.equal(err.count, 1); + }); +}); diff --git a/tests/page-templates/14-errors/24-autorun-false-multiple-then-load.html b/tests/page-templates/14-errors/24-autorun-false-multiple-then-load.html new file mode 100644 index 000000000..2ee4d3373 --- /dev/null +++ b/tests/page-templates/14-errors/24-autorun-false-multiple-then-load.html @@ -0,0 +1,29 @@ +<%= header %> +<%= boomerangScriptMin %> + + + + +<%= footer %> diff --git a/tests/page-templates/14-errors/24-autorun-false-multiple-then-load.js b/tests/page-templates/14-errors/24-autorun-false-multiple-then-load.js new file mode 100644 index 000000000..55979a646 --- /dev/null +++ b/tests/page-templates/14-errors/24-autorun-false-multiple-then-load.js @@ -0,0 +1,122 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert,describe,it */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["a", "b"]); + +describe("e2e/14-errors/24-autorun-false-multiple-then-load", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + var C = BOOMR.utils.Compression; + + it("Should have sent two beacons", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 2); + }); + + it("Should have http.initiator = error on the first beacon", function() { + var b = tf.beacons[0]; + + assert.equal(b["http.initiator"], "error"); + }); + + it("Should have put err on the first beacon", function() { + var b = tf.beacons[0]; + + assert.isDefined(b.err); + }); + + it("Should have two errors on the first beacon", function() { + var b = tf.beacons[0]; + var errs = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err)); + + assert.equal(errs.length, 2); + }); + + it("Should have an error count = 1 on the first beacon first error", function() { + var b = tf.beacons[0]; + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.count, 1); + }); + + it("Should have an error coming from the html page on the first beacon first error", function() { + var b = tf.beacons[0]; + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.fileName) { + assert.include(err.fileName, window.location.pathname.substring(window.location.pathname.lastIndexOf("/") + 1)); + } + else { + return this.skip(); + } + }); + + it("Should have functionName of 'Global code' (if set) on the first beacon first error", function() { + var b = tf.beacons[0]; + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.functionName) { + // not set in Chrome and FF, Edge = 'Global code', Safari = 'global code' + assert.include(err.functionName.toLowerCase(), "global code"); + } + else { + return this.skip(); + } + }); + + it("Should have rt.end on the first beacon", function() { + var b = tf.beacons[0]; + + assert.isDefined(b["rt.end"]); + }); + + it("Should have rt.tstart on the first beacon", function() { + var b = tf.beacons[0]; + + assert.isDefined(b["rt.tstart"]); + }); + + it("Should have rt.start = manual on the first beacon", function() { + var b = tf.beacons[0]; + + assert.equal(b["rt.start"], "manual"); + }); + + it("Should have no http.initiator the second beacon", function() { + var b = tf.beacons[1]; + + assert.isUndefined(b["http.initiator"]); + }); + + it("Should have rt.start=navigation on the second beacon (if NavigationTiming is supported)", function() { + if (t.isNavigationTimingSupported()) { + assert.equal(tf.beacons[1]["rt.start"], "navigation"); + } + else { + assert.equal(tf.beacons[1]["rt.start"], "none"); + } + }); + + it("Should have put NavigationTiming metrics on the second beacon (if NavigationTiming is supported)", function() { + if (t.isNavigationTimingSupported()) { + assert.isDefined(tf.beacons[1].nt_nav_st); + assert.isDefined(tf.beacons[1].nt_load_st); + } + else { + return this.skip(); + } + }); + + it("Should have rt.sl = 1 on the first beacon", function() { + var b = tf.beacons[0]; + + assert.equal(b["rt.sl"], 1); + }); + + it("Should have rt.sl = 1 on the second beacon", function() { + var b = tf.beacons[1]; + + assert.equal(b["rt.sl"], 1); + }); +}); diff --git a/tests/page-templates/14-errors/25-autorun-false-onload-happens.html b/tests/page-templates/14-errors/25-autorun-false-onload-happens.html new file mode 100644 index 000000000..e35ff3f0b --- /dev/null +++ b/tests/page-templates/14-errors/25-autorun-false-onload-happens.html @@ -0,0 +1,23 @@ +<%= header %> +<%= boomerangScriptMin %> + + + + +<%= footer %> diff --git a/tests/page-templates/14-errors/25-autorun-false-onload-happens.js b/tests/page-templates/14-errors/25-autorun-false-onload-happens.js new file mode 100644 index 000000000..ecd55ab3d --- /dev/null +++ b/tests/page-templates/14-errors/25-autorun-false-onload-happens.js @@ -0,0 +1,101 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert,describe,it */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["a"]); + +describe("e2e/14-errors/25-autorun-false-onload-happens", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + var C = BOOMR.utils.Compression; + + it("Should have sent one beacon", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 1); + }); + + it("Should have no http.initiator", function() { + var b = tf.lastBeacon(); + + assert.isUndefined(b["http.initiator"]); + }); + + it("Should have put err on the beacon", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b.err); + }); + + it("Should have an error count = 1", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.count, 1); + }); + + it("Should have fileName of the page (if set)", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.fileName) { + assert.include(err.fileName, window.location.pathname.substring(window.location.pathname.lastIndexOf("/") + 1)); + } + else { + return this.skip(); + } + }); + + it("Should have functionName of 'Global code' (if set)", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.functionName) { + // not set in Chrome and FF, Edge = 'Global code', Safari = 'global code' + assert.include(err.functionName.toLowerCase(), "global code"); + } + else { + return this.skip(); + } + }); + + it("Should have rt.sl = 1", function() { + var b = tf.lastBeacon(); + + assert.equal(b["rt.sl"], 1); + }); + + it("Should have rt.end", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["rt.end"]); + }); + + it("Should have rt.tstart (if NavigationTiming is supported)", function() { + var b = tf.lastBeacon(); + + if (t.isNavigationTimingSupported()) { + assert.isDefined(b["rt.tstart"]); + } + else { + return this.skip(); + } + }); + + it("Should have rt.start=navigation (if NavigationTiming is supported)", function() { + if (t.isNavigationTimingSupported()) { + assert.equal(tf.beacons[0]["rt.start"], "navigation"); + } + else { + assert.equal(tf.beacons[0]["rt.start"], "none"); + } + }); + + it("Should have put NavigationTiming metrics on the beacon (if NavigationTiming is supported)", function() { + if (t.isNavigationTimingSupported()) { + assert.isDefined(tf.beacons[0].nt_nav_st); + } + else { + return this.skip(); + } + }); +}); diff --git a/tests/page-templates/14-errors/26-freed-events.html b/tests/page-templates/14-errors/26-freed-events.html new file mode 100644 index 000000000..8877ff87a --- /dev/null +++ b/tests/page-templates/14-errors/26-freed-events.html @@ -0,0 +1,45 @@ +<%= header %> +<%= boomerangScriptMin %> + + + + + + +<%= footer %> diff --git a/tests/page-templates/14-errors/26-freed-events.js b/tests/page-templates/14-errors/26-freed-events.js new file mode 100644 index 000000000..e6df37091 --- /dev/null +++ b/tests/page-templates/14-errors/26-freed-events.js @@ -0,0 +1,29 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["testFrame", "testFrameDoc"]); + +describe("e2e/14-errors/26-freed-events", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + if (!window.addEventListener) { + it("Skipping on browser that doesn't support addEventListener", function() { + return this.skip(); + }); + + return; + } + + it("Should have only sent a page-load beacon", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 1); + }); + + it("Should have no error on the page-load beacon", function() { + var b = tf.lastBeacon(); + + assert.isUndefined(b.err); + }); +}); diff --git a/tests/page-templates/14-errors/26-loader-snippet.html b/tests/page-templates/14-errors/26-loader-snippet.html new file mode 100644 index 000000000..e6c45c92e --- /dev/null +++ b/tests/page-templates/14-errors/26-loader-snippet.html @@ -0,0 +1,46 @@ +<%= header %> + +<%= captureErrorsSnippet %> + + +<%= boomerangScriptMin %> + + + + + +<%= footer %> diff --git a/tests/page-templates/14-errors/26-loader-snippet.js b/tests/page-templates/14-errors/26-loader-snippet.js new file mode 100644 index 000000000..debc8a4db --- /dev/null +++ b/tests/page-templates/14-errors/26-loader-snippet.js @@ -0,0 +1,160 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["errorsLogged", "errorFunction1", "errorFunction2", "errorFunction3"]); + +describe("e2e/14-errors/26-loader-snippet", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + var C = BOOMR.utils.Compression; + + it("Should have sent a single beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have put the err on the beacon", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b.err); + }); + + it("Should have had 3 errors", function() { + var b = tf.lastBeacon(); + + assert.equal(C.jsUrlDecompress(b.err).length, 3); + }); + + it("Should have count = 1 for each error", function() { + var b = tf.lastBeacon(); + var errs = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err)); + + for (var i = 0; i < 3; i++) { + var err = errs[i]; + + assert.equal(err.count, 1); + } + }); + + it("Should have fileName of the page (if set) for each error", function() { + var b = tf.lastBeacon(), + found = false; + var errs = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err)); + + for (var i = 0; i < 3; i++) { + var err = errs[i]; + + if (err.fileName) { + assert.include(err.fileName, window.location.pathname.substring(window.location.pathname.lastIndexOf("/") + 1)); + found = true; + } + } + + if (!found) { + return this.skip(); + } + }); + + it("Should have functionName of 'errorFunction' for each error", function() { + var b = tf.lastBeacon(), + found = false; + var errs = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err)); + + for (var i = 0; i < 3; i++) { + var err = errs[i]; + + if (err.functionName) { + assert.include(err.functionName, "errorFunction"); + found = true; + } + } + + if (!found) { + return this.skip(); + } + }); + + it("Should have message = 'a is not defined' or 'Can't find variable: a' or ''a' is undefined' for each error", function() { + var b = tf.lastBeacon(); + var errs = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err)); + + for (var i = 0; i < 3; i++) { + var err = errs[i]; + + // Chrome, Firefox == a is not defined, Safari = Can't find variable, Edge = 'a' is not defined + assert.isTrue( + err.message.indexOf("a is not defined") !== -1 || + err.message.indexOf("Can't find variable: a") !== -1 || + err.message.indexOf("'a' is undefined") !== -1 || + err.message.indexOf("'a' is not defined") !== -1); + } + }); + + it("Should have source = APP for each error", function() { + var b = tf.lastBeacon(); + var errs = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err)); + + for (var i = 0; i < 3; i++) { + var err = errs[i]; + + assert.equal(err.source, BOOMR.plugins.Errors.SOURCE_APP); + } + }); + + it("Should have stack with the stack for each error", function() { + var b = tf.lastBeacon(); + var errs = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err)); + + for (var i = 0; i < 3; i++) { + var err = errs[i]; + + assert.isDefined(err.stack); + } + }); + + it("Should have type = 'ReferenceError' or 'Error' for each error", function() { + var b = tf.lastBeacon(); + var errs = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err)); + + for (var i = 0; i < 3; i++) { + var err = errs[i]; + + // Chrome, Firefox == ReferenceError, Safari = Error + assert.isTrue(err.type === "ReferenceError" || err.type === "Error"); + } + }); + + it("Should have via = GLOBAL_EXCEPTION_HANDLER for each error", function() { + var b = tf.lastBeacon(); + var errs = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err)); + + for (var i = 0; i < 3; i++) { + var err = errs[i]; + + assert.equal(err.via, BOOMR.plugins.Errors.VIA_GLOBAL_EXCEPTION_HANDLER); + } + }); + + it("Should have columNumber to be a number if specified for each error", function() { + var b = tf.lastBeacon(), + found = false; + var errs = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err)); + + for (var i = 0; i < 3; i++) { + var err = errs[i]; + + if (typeof err.columnNumber !== "undefined") { + assert.isTrue(err.columnNumber >= 0); + found = true; + } + } + + if (!found) { + return this.skip(); + } + }); + + it("Should have called the original window.onerror for each error", function() { + assert.equal(window.errorsLogged, 3); + }); +}); diff --git a/tests/page-templates/14-errors/27-freed-timeout.html b/tests/page-templates/14-errors/27-freed-timeout.html new file mode 100644 index 000000000..640decc28 --- /dev/null +++ b/tests/page-templates/14-errors/27-freed-timeout.html @@ -0,0 +1,39 @@ +<%= header %> +<%= boomerangScriptMin %> + + + + + + +<%= footer %> diff --git a/tests/page-templates/14-errors/27-freed-timeout.js b/tests/page-templates/14-errors/27-freed-timeout.js new file mode 100644 index 000000000..64568cc4e --- /dev/null +++ b/tests/page-templates/14-errors/27-freed-timeout.js @@ -0,0 +1,21 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["testFrame", "testFrameDoc"]); + +describe("e2e/14-errors/27-freed-timeout", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have only sent a page-load beacon", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 1); + }); + + it("Should have no error on the page-load beacon", function() { + var b = tf.lastBeacon(); + + assert.isUndefined(b.err); + }); +}); diff --git a/tests/page-templates/14-errors/27-loader-snippet-overwritten.html b/tests/page-templates/14-errors/27-loader-snippet-overwritten.html new file mode 100644 index 000000000..6dc171062 --- /dev/null +++ b/tests/page-templates/14-errors/27-loader-snippet-overwritten.html @@ -0,0 +1,54 @@ +<%= header %> + +<%= captureErrorsSnippet %> + + +<%= boomerangScriptMin %> + + + + + +<%= footer %> diff --git a/tests/page-templates/14-errors/27-loader-snippet-overwritten.js b/tests/page-templates/14-errors/27-loader-snippet-overwritten.js new file mode 100644 index 000000000..93ee8f9f8 --- /dev/null +++ b/tests/page-templates/14-errors/27-loader-snippet-overwritten.js @@ -0,0 +1,35 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["errorsLogged1", "errorFunction1", "errorFunction2", "errorFunction3", "oldOnError", "errorsLogged2"]); + +describe("e2e/14-errors/26-loader-snippet", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + var C = BOOMR.utils.Compression; + + it("Should have sent a single beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have put the err on the beacon", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b.err); + }); + + it("Should have had 3 errors", function() { + var b = tf.lastBeacon(); + + assert.equal(C.jsUrlDecompress(b.err).length, 3); + }); + + it("Should have called the original window.onerror for each error", function() { + assert.equal(window.errorsLogged1, 3); + }); + + it("Should have called the second window.onerror for each error", function() { + assert.equal(window.errorsLogged2, 3); + }); +}); diff --git a/tests/page-templates/14-errors/28-send-plugin-disabled.html b/tests/page-templates/14-errors/28-send-plugin-disabled.html new file mode 100644 index 000000000..3a587163c --- /dev/null +++ b/tests/page-templates/14-errors/28-send-plugin-disabled.html @@ -0,0 +1,18 @@ +<%= header %> +<%= boomerangScriptMin %> + + +<%= footer %> diff --git a/tests/page-templates/14-errors/28-send-plugin-disabled.js b/tests/page-templates/14-errors/28-send-plugin-disabled.js new file mode 100644 index 000000000..312e85a48 --- /dev/null +++ b/tests/page-templates/14-errors/28-send-plugin-disabled.js @@ -0,0 +1,20 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["errorFunction"]); + +describe("e2e/14-errors/28-send-plugin-disabled", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a single beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should not have put the err on the beacon", function() { + var b = tf.lastBeacon(); + + assert.isUndefined(b.err); + }); +}); diff --git a/tests/page-templates/14-errors/29-boomr-errors-plugin-disabled.html b/tests/page-templates/14-errors/29-boomr-errors-plugin-disabled.html new file mode 100644 index 000000000..300329613 --- /dev/null +++ b/tests/page-templates/14-errors/29-boomr-errors-plugin-disabled.html @@ -0,0 +1,34 @@ +<%= header %> +<%= boomerangScriptMin %> + + +<%= footer %> diff --git a/tests/page-templates/14-errors/29-boomr-errors-plugin-disabled.js b/tests/page-templates/14-errors/29-boomr-errors-plugin-disabled.js new file mode 100644 index 000000000..fd7b91810 --- /dev/null +++ b/tests/page-templates/14-errors/29-boomr-errors-plugin-disabled.js @@ -0,0 +1,61 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["errorFunction"]); + +describe("e2e/14-errors/29-boomr-errors-plugin-disabled", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a single beacon", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should not have put the err on the beacon", function() { + var b = tf.lastBeacon(); + + assert.isUndefined(b.err); + }); + + it("Should have put the errors on the beacon", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b.errors); + }); + + it("Should have had 4 errors", function() { + var b = tf.lastBeacon(); + + assert.equal(b.errors.split("\n").length, 4); + }); + + // the expected message in this test is browser specific and might need tweaking + it("Should have error #1 match expected", function() { + var b = tf.lastBeacon(); + var err = b.errors.split("\n")[0]; + + assert.match(err, /^\[BOOMRtest:\d{13}\] ReferenceError: (?:'?a'? is not defined|Can't find variable: a|'?a'? is undefined)$/); + }); + + it("Should have error #2 match expected", function() { + var b = tf.lastBeacon(); + var err = b.errors.split("\n")[1]; + + assert.match(err, /^\[BOOMRtest2:\d{13}\] Fault 2$/); + }); + + it("Should have error #3 match expected", function() { + var b = tf.lastBeacon(); + var err = b.errors.split("\n")[2]; + + assert.match(err, /^\[BOOMRtest3:\d{13}\] Fault 3:: Extra stuff$/); + }); + + it("Should have error #4 match expected", function() { + var b = tf.lastBeacon(); + var err = b.errors.split("\n")[3]; + + assert.match(err, /^\[BOOMRtest4:\d{13}\] Fault 4:: \[object Object\]$/); + }); +}); diff --git a/tests/page-templates/14-errors/30-events-xhr-wrap-xhret.html b/tests/page-templates/14-errors/30-events-xhr-wrap-xhret.html new file mode 100644 index 000000000..fed898605 --- /dev/null +++ b/tests/page-templates/14-errors/30-events-xhr-wrap-xhret.html @@ -0,0 +1,50 @@ +<%= header %> + +<%= boomerangScriptMin %> + + + + +<%= footer %> diff --git a/tests/page-templates/14-errors/30-events-xhr-wrap-xhret.js b/tests/page-templates/14-errors/30-events-xhr-wrap-xhret.js new file mode 100644 index 000000000..3a9edf55f --- /dev/null +++ b/tests/page-templates/14-errors/30-events-xhr-wrap-xhret.js @@ -0,0 +1,150 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["orig_AEL", "xhr", "listenerCalled"]); + +describe("e2e/14-errors/30-events-xhr-wrap-xhret", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + var C = BOOMR.utils.Compression; + + if (!BOOMR.plugins.AutoXHR) { + it("Skipping on non-AutoXHR supporting browser", function() { + return this.skip(); + }); + + return; + } + + if (!window.XMLHttpRequestEventTarget) { + it("Skipping on non-XMLHttpRequestEventTarget supporting browser", function() { + return this.skip(); + }); + + return; + } + + it("Should have only sent one page load beacon", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 1); + }); + + it("Should have put the err on the page load beacon", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b.err); + }); + + it("Should have had a single error", function() { + var b = tf.lastBeacon(); + + assert.equal(BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err)).length, 1); + }); + + it("Should have count = 1", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.count, 1); + }); + + it("Should have fileName of the page (if set)", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.fileName) { + assert.include(err.fileName, window.location.pathname.substring(window.location.pathname.lastIndexOf("/") + 1)); + } + else { + return this.skip(); + } + }); + + it("Should have functionName of 'errorFunction'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.functionName) { + assert.include(err.functionName, "errorFunction"); + } + else { + return this.skip(); + } + }); + + it("Should have message = 'a is not defined' or 'Can't find variable: a' or ''a' is undefined'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + // Chrome, Firefox == a is not defined, Safari = Can't find variable, Edge = 'a' is not defined + assert.isTrue( + err.message.indexOf("a is not defined") !== -1 || + err.message.indexOf("Can't find variable: a") !== -1 || + err.message.indexOf("'a' is undefined") !== -1 || + err.message.indexOf("'a' is not defined") !== -1); + }); + + it("Should have source = APP", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.source, BOOMR.plugins.Errors.SOURCE_APP); + }); + + it("Should have stack with the stack", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.isDefined(err.stack); + }); + + it("Should have not have BOOMR_plugins_errors_wrap on the stack", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.notInclude(err.stack, "BOOMR_plugins_errors_wrap"); + }); + + it("Should have type = 'ReferenceError' or 'Error'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.isTrue(err.type === "ReferenceError" || err.type === "Error"); + }); + + it("Should have via = EVENTHANDLER", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.via, BOOMR.plugins.Errors.VIA_EVENTHANDLER); + }); + + it("Should have columNumber to be a number if specified", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (typeof err.columnNumber !== "undefined") { + assert.isTrue(err.columnNumber >= 0); + } + else { + return this.skip(); + } + }); + + it("Should have lineNumber ~ " + (HEADER_LINES + 42), function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.lineNumber) { + assert.closeTo(err.lineNumber, HEADER_LINES + 42, 5); + } + else { + return this.skip(); + } + }); + + it("Should have called the wrapped XMLHttpRequestEventTarget addEventListener once", function() { + assert.equal(window.listenerCalled, 1); + }); +}); diff --git a/tests/page-templates/14-errors/31-events-xhr-wrap-xhr.html b/tests/page-templates/14-errors/31-events-xhr-wrap-xhr.html new file mode 100644 index 000000000..2ee3ed485 --- /dev/null +++ b/tests/page-templates/14-errors/31-events-xhr-wrap-xhr.html @@ -0,0 +1,47 @@ +<%= header %> + +<%= boomerangScriptMin %> + + + + +<%= footer %> diff --git a/tests/page-templates/14-errors/31-events-xhr-wrap-xhr.js b/tests/page-templates/14-errors/31-events-xhr-wrap-xhr.js new file mode 100644 index 000000000..4d2fa7469 --- /dev/null +++ b/tests/page-templates/14-errors/31-events-xhr-wrap-xhr.js @@ -0,0 +1,142 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["orig_AEL", "xhr", "listenerCalled"]); + +describe("e2e/14-errors/31-events-xhr-wrap-xhr", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + var C = BOOMR.utils.Compression; + + if (!BOOMR.plugins.AutoXHR) { + it("Skipping on non-AutoXHR supporting browser", function() { + return this.skip(); + }); + + return; + } + + it("Should have only sent one page load beacon", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 1); + }); + + it("Should have put the err on the page load beacon", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b.err); + }); + + it("Should have had a single error", function() { + var b = tf.lastBeacon(); + + assert.equal(BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err)).length, 1); + }); + + it("Should have count = 1", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.count, 1); + }); + + it("Should have fileName of the page (if set)", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.fileName) { + assert.include(err.fileName, window.location.pathname.substring(window.location.pathname.lastIndexOf("/") + 1)); + } + else { + return this.skip(); + } + }); + + it("Should have functionName of 'errorFunction'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.functionName) { + assert.include(err.functionName, "errorFunction"); + } + else { + return this.skip(); + } + }); + + it("Should have message = 'a is not defined' or 'Can't find variable: a' or ''a' is undefined'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + // Chrome, Firefox == a is not defined, Safari = Can't find variable, Edge = 'a' is not defined + assert.isTrue( + err.message.indexOf("a is not defined") !== -1 || + err.message.indexOf("Can't find variable: a") !== -1 || + err.message.indexOf("'a' is undefined") !== -1 || + err.message.indexOf("'a' is not defined") !== -1); + }); + + it("Should have source = APP", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.source, BOOMR.plugins.Errors.SOURCE_APP); + }); + + it("Should have stack with the stack", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.isDefined(err.stack); + }); + + it("Should have not have BOOMR_plugins_errors_wrap on the stack", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.notInclude(err.stack, "BOOMR_plugins_errors_wrap"); + }); + + it("Should have type = 'ReferenceError' or 'Error'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.isTrue(err.type === "ReferenceError" || err.type === "Error"); + }); + + it("Should have via = EVENTHANDLER", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.via, BOOMR.plugins.Errors.VIA_EVENTHANDLER); + }); + + it("Should have columNumber to be a number if specified", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (typeof err.columnNumber !== "undefined") { + assert.isTrue(err.columnNumber >= 0); + } + else { + return this.skip(); + } + }); + + it("Should have lineNumber ~ " + (HEADER_LINES + 36), function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.lineNumber) { + assert.closeTo(err.lineNumber, HEADER_LINES + 36, 5); + } + else { + return this.skip(); + } + }); + + it("Should have called the wrapped XMLHttpRequest addEventListener once", function() { + assert.equal(window.listenerCalled, 1); + }); +}); diff --git a/tests/page-templates/14-errors/32-events-xhr-wrap-et.html b/tests/page-templates/14-errors/32-events-xhr-wrap-et.html new file mode 100644 index 000000000..0c8ce36e0 --- /dev/null +++ b/tests/page-templates/14-errors/32-events-xhr-wrap-et.html @@ -0,0 +1,47 @@ +<%= header %> + +<%= boomerangScriptMin %> + + + + +<%= footer %> diff --git a/tests/page-templates/14-errors/32-events-xhr-wrap-et.js b/tests/page-templates/14-errors/32-events-xhr-wrap-et.js new file mode 100644 index 000000000..c30965204 --- /dev/null +++ b/tests/page-templates/14-errors/32-events-xhr-wrap-et.js @@ -0,0 +1,151 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["orig_AEL", "xhr", "listenerCalled"]); + +describe("e2e/14-errors/32-events-xhr-wrap-et", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + var C = BOOMR.utils.Compression; + + if (!BOOMR.plugins.AutoXHR) { + it("Skipping on non-AutoXHR supporting browser", function() { + return this.skip(); + }); + + return; + } + + if (!window.EventTarget) { + it("Skipping on non-EventTarget supporting browser", function() { + return this.skip(); + }); + + return; + } + + it("Should have only sent one page load beacon", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 1); + }); + + it("Should have put the err on the page load beacon", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b.err); + }); + + it("Should have had a single error", function() { + var b = tf.lastBeacon(); + + assert.equal(BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err)).length, 1); + }); + + it("Should have count = 1", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.count, 1); + }); + + it("Should have fileName of the page (if set)", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.fileName) { + assert.include(err.fileName, window.location.pathname.substring(window.location.pathname.lastIndexOf("/") + 1)); + } + else { + return this.skip(); + } + }); + + it("Should have functionName of 'errorFunction'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.functionName) { + assert.include(err.functionName, "errorFunction"); + } + else { + return this.skip(); + } + }); + + it("Should have message = 'a is not defined' or 'Can't find variable: a' or ''a' is undefined'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + // Chrome, Firefox == a is not defined, Safari = Can't find variable, Edge = 'a' is not defined + assert.isTrue( + err.message.indexOf("a is not defined") !== -1 || + err.message.indexOf("Can't find variable: a") !== -1 || + err.message.indexOf("'a' is undefined") !== -1 || + err.message.indexOf("'a' is not defined") !== -1); + }); + + it("Should have source = APP", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.source, BOOMR.plugins.Errors.SOURCE_APP); + }); + + it("Should have stack with the stack", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.isDefined(err.stack); + }); + + it("Should have not have BOOMR_plugins_errors_wrap on the stack", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.notInclude(err.stack, "BOOMR_plugins_errors_wrap"); + }); + + it("Should have type = 'ReferenceError' or 'Error'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.isTrue(err.type === "ReferenceError" || err.type === "Error"); + }); + + it("Should have via = EVENTHANDLER", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.via, BOOMR.plugins.Errors.VIA_EVENTHANDLER); + }); + + it("Should have columNumber to be a number if specified", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (typeof err.columnNumber !== "undefined") { + assert.isTrue(err.columnNumber >= 0); + } + else { + return this.skip(); + } + }); + + it("Should have lineNumber ~ " + (HEADER_LINES + 36), function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.lineNumber) { + assert.closeTo(err.lineNumber, HEADER_LINES + 36, 5); + } + else { + return this.skip(); + } + }); + + it("Should have called the wrapped EventTarget addEventListener at least once", function() { + // we don't know the exact count because EventTarget AEL could be called for many other objects + assert.operator(window.listenerCalled, ">=", 1); + }); +}); diff --git a/tests/page-templates/14-errors/33-events-xhr-wrap-xhret-before.html b/tests/page-templates/14-errors/33-events-xhr-wrap-xhret-before.html new file mode 100644 index 000000000..c0d8c5362 --- /dev/null +++ b/tests/page-templates/14-errors/33-events-xhr-wrap-xhret-before.html @@ -0,0 +1,55 @@ +<%= header %> + + +<%= boomerangScriptMin %> + + + + +<%= footer %> diff --git a/tests/page-templates/14-errors/33-events-xhr-wrap-xhret-before.js b/tests/page-templates/14-errors/33-events-xhr-wrap-xhret-before.js new file mode 100644 index 000000000..a142dafb4 --- /dev/null +++ b/tests/page-templates/14-errors/33-events-xhr-wrap-xhret-before.js @@ -0,0 +1,150 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["orig_AEL", "listenerCalled", "xhr"]); + +describe("e2e/14-errors/33-events-xhr-wrap-xhret-before", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + var C = BOOMR.utils.Compression; + + if (!BOOMR.plugins.AutoXHR) { + it("Skipping on non-AutoXHR supporting browser", function() { + return this.skip(); + }); + + return; + } + + if (!window.XMLHttpRequestEventTarget) { + it("Skipping on non-XMLHttpRequestEventTarget supporting browser", function() { + return this.skip(); + }); + + return; + } + + it("Should have only sent one page load beacon", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 1); + }); + + it("Should have put the err on the page load beacon", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b.err); + }); + + it("Should have had a single error", function() { + var b = tf.lastBeacon(); + + assert.equal(BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err)).length, 1); + }); + + it("Should have count = 1", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.count, 1); + }); + + it("Should have fileName of the page (if set)", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.fileName) { + assert.include(err.fileName, window.location.pathname.substring(window.location.pathname.lastIndexOf("/") + 1)); + } + else { + return this.skip(); + } + }); + + it("Should have functionName of 'errorFunction'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.functionName) { + assert.include(err.functionName, "errorFunction"); + } + else { + return this.skip(); + } + }); + + it("Should have message = 'a is not defined' or 'Can't find variable: a' or ''a' is undefined'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + // Chrome, Firefox == a is not defined, Safari = Can't find variable, Edge = 'a' is not defined + assert.isTrue( + err.message.indexOf("a is not defined") !== -1 || + err.message.indexOf("Can't find variable: a") !== -1 || + err.message.indexOf("'a' is undefined") !== -1 || + err.message.indexOf("'a' is not defined") !== -1); + }); + + it("Should have source = APP", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.source, BOOMR.plugins.Errors.SOURCE_APP); + }); + + it("Should have stack with the stack", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.isDefined(err.stack); + }); + + it("Should have not have BOOMR_plugins_errors_wrap on the stack", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.notInclude(err.stack, "BOOMR_plugins_errors_wrap"); + }); + + it("Should have type = 'ReferenceError' or 'Error'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.isTrue(err.type === "ReferenceError" || err.type === "Error"); + }); + + it("Should have via = GLOBAL_EXCEPTION_HANDLER", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.via, BOOMR.plugins.Errors.VIA_GLOBAL_EXCEPTION_HANDLER); + }); + + it("Should have columNumber to be a number if specified", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (typeof err.columnNumber !== "undefined") { + assert.isTrue(err.columnNumber >= 0); + } + else { + return this.skip(); + } + }); + + it("Should have lineNumber ~ " + (HEADER_LINES + 43), function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.lineNumber) { + assert.closeTo(err.lineNumber, HEADER_LINES + 43, 5); + } + else { + return this.skip(); + } + }); + + it("Should have called the wrapped XMLHttpRequestEventTarget addEventListener once", function() { + assert.equal(window.listenerCalled, 1); + }); +}); diff --git a/tests/page-templates/14-errors/34-events-xhr-wrap-xhr-before.html b/tests/page-templates/14-errors/34-events-xhr-wrap-xhr-before.html new file mode 100644 index 000000000..8c0c7ace7 --- /dev/null +++ b/tests/page-templates/14-errors/34-events-xhr-wrap-xhr-before.html @@ -0,0 +1,52 @@ +<%= header %> + + +<%= boomerangScriptMin %> + + + + +<%= footer %> diff --git a/tests/page-templates/14-errors/34-events-xhr-wrap-xhr-before.js b/tests/page-templates/14-errors/34-events-xhr-wrap-xhr-before.js new file mode 100644 index 000000000..93ee8b164 --- /dev/null +++ b/tests/page-templates/14-errors/34-events-xhr-wrap-xhr-before.js @@ -0,0 +1,174 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["orig_AEL", "listenerCalled", "xhr"]); + +describe("e2e/14-errors/34-events-xhr-wrap-xhr-before", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + var C = BOOMR.utils.Compression; + + if (!BOOMR.plugins.AutoXHR) { + it("Skipping on non-AutoXHR supporting browser", function() { + return this.skip(); + }); + + return; + } + + it("Should have only sent one page load beacon", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 1); + }); + + it("Should have put the err on the page load beacon", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b.err); + }); + + it("Should have had a single error in a browser that has the error object in onerror", function() { + var b = tf.lastBeacon(); + + if (!t.isErrorObjInOnErrorSupported()) { + return this.skip(); + } + + assert.equal(BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err)).length, 1); + }); + + it("Should have had 2 errors in a browser that does not have the error object in onerror", function() { + var b = tf.lastBeacon(); + + if (t.isErrorObjInOnErrorSupported()) { + return this.skip(); + } + + // PhantomJS, older IE and Safari will report 2 errors, one from the addEventListener wrapper and another from the global onerror + assert.equal(BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err)).length, 2); + }); + + it("Should have count = 1", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.count, 1); + }); + + it("Should have fileName of the page (if set)", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.fileName) { + assert.include(err.fileName, window.location.pathname.substring(window.location.pathname.lastIndexOf("/") + 1)); + } + else { + return this.skip(); + } + }); + + it("Should have functionName of 'errorFunction'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.functionName) { + assert.include(err.functionName, "errorFunction"); + } + else { + return this.skip(); + } + }); + + it("Should have message = 'a is not defined' or 'Can't find variable: a' or ''a' is undefined'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + // Chrome, Firefox == a is not defined, Safari = Can't find variable, Edge = 'a' is not defined + assert.isTrue( + err.message.indexOf("a is not defined") !== -1 || + err.message.indexOf("Can't find variable: a") !== -1 || + err.message.indexOf("'a' is undefined") !== -1 || + err.message.indexOf("'a' is not defined") !== -1); + }); + + it("Should have source = APP", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.source, BOOMR.plugins.Errors.SOURCE_APP); + }); + + it("Should have stack with the stack", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.isDefined(err.stack); + }); + + it("Should have not have BOOMR_plugins_errors_wrap on the stack", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.notInclude(err.stack, "BOOMR_plugins_errors_wrap"); + }); + + it("Should have type = 'ReferenceError' or 'Error'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.isTrue(err.type === "ReferenceError" || err.type === "Error"); + }); + + it("Should have via = GLOBAL_EXCEPTION_HANDLER on EventTarget supported browsers", function() { + if (window.EventTarget) { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.via, BOOMR.plugins.Errors.VIA_GLOBAL_EXCEPTION_HANDLER); + } + else { + return this.skip(); + } + }); + + it("Should have via = EVENTHANDLER on non-EventTarget supported browsers", function() { + if (!window.EventTarget) { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.via, BOOMR.plugins.Errors.VIA_EVENTHANDLER); + } + else { + return this.skip(); + } + }); + + it("Should have columNumber to be a number if specified", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (typeof err.columnNumber !== "undefined") { + assert.isTrue(err.columnNumber >= 0); + } + else { + return this.skip(); + } + }); + + it("Should have lineNumber ~ " + (HEADER_LINES + 44), function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.lineNumber) { + assert.closeTo(err.lineNumber, HEADER_LINES + 44, 5); + } + else { + return this.skip(); + } + }); + + it("Should have called the wrapped XMLHttpRequest addEventListener once", function() { + assert.equal(window.listenerCalled, 1); + }); +}); diff --git a/tests/page-templates/14-errors/35-events-xhr-wrap-et-before.html b/tests/page-templates/14-errors/35-events-xhr-wrap-et-before.html new file mode 100644 index 000000000..d4c445768 --- /dev/null +++ b/tests/page-templates/14-errors/35-events-xhr-wrap-et-before.html @@ -0,0 +1,52 @@ +<%= header %> + + +<%= boomerangScriptMin %> + + + + +<%= footer %> diff --git a/tests/page-templates/14-errors/35-events-xhr-wrap-et-before.js b/tests/page-templates/14-errors/35-events-xhr-wrap-et-before.js new file mode 100644 index 000000000..1743e941c --- /dev/null +++ b/tests/page-templates/14-errors/35-events-xhr-wrap-et-before.js @@ -0,0 +1,151 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["orig_AEL", "listenerCalled", "xhr"]); + +describe("e2e/14-errors/35-events-xhr-wrap-et-before", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + var C = BOOMR.utils.Compression; + + if (!BOOMR.plugins.AutoXHR) { + it("Skipping on non-AutoXHR supporting browser", function() { + return this.skip(); + }); + + return; + } + + if (!window.EventTarget) { + it("Skipping on non-EventTarget supporting browser", function() { + return this.skip(); + }); + + return; + } + + it("Should have only sent one page load beacon", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 1); + }); + + it("Should have put the err on the page load beacon", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b.err); + }); + + it("Should have had a single error", function() { + var b = tf.lastBeacon(); + + assert.equal(BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err)).length, 1); + }); + + it("Should have count = 1", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.count, 1); + }); + + it("Should have fileName of the page (if set)", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.fileName) { + assert.include(err.fileName, window.location.pathname.substring(window.location.pathname.lastIndexOf("/") + 1)); + } + else { + return this.skip(); + } + }); + + it("Should have functionName of 'errorFunction'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.functionName) { + assert.include(err.functionName, "errorFunction"); + } + else { + return this.skip(); + } + }); + + it("Should have message = 'a is not defined' or 'Can't find variable: a' or ''a' is undefined'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + // Chrome, Firefox == a is not defined, Safari = Can't find variable, Edge = 'a' is not defined + assert.isTrue( + err.message.indexOf("a is not defined") !== -1 || + err.message.indexOf("Can't find variable: a") !== -1 || + err.message.indexOf("'a' is undefined") !== -1 || + err.message.indexOf("'a' is not defined") !== -1); + }); + + it("Should have source = APP", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.source, BOOMR.plugins.Errors.SOURCE_APP); + }); + + it("Should have stack with the stack", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.isDefined(err.stack); + }); + + it("Should have not have BOOMR_plugins_errors_wrap on the stack", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.notInclude(err.stack, "BOOMR_plugins_errors_wrap"); + }); + + it("Should have type = 'ReferenceError' or 'Error'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.isTrue(err.type === "ReferenceError" || err.type === "Error"); + }); + + it("Should have via = VIA_EVENTHANDLER", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.via, BOOMR.plugins.Errors.VIA_EVENTHANDLER); + }); + + it("Should have columNumber to be a number if specified", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (typeof err.columnNumber !== "undefined") { + assert.isTrue(err.columnNumber >= 0); + } + else { + return this.skip(); + } + }); + + it("Should have lineNumber ~ " + (HEADER_LINES + 44), function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.lineNumber) { + assert.closeTo(err.lineNumber, HEADER_LINES + 44, 5); + } + else { + return this.skip(); + } + }); + + it("Should have called the wrapped EventTarget addEventListener at least once", function() { + // we don't know the exact count because EventTarget AEL could be called for many other objects + assert.operator(window.listenerCalled, ">=", 1); + }); +}); diff --git a/tests/page-templates/14-errors/36-events-window-reported.html b/tests/page-templates/14-errors/36-events-window-reported.html new file mode 100644 index 000000000..bc1893297 --- /dev/null +++ b/tests/page-templates/14-errors/36-events-window-reported.html @@ -0,0 +1,41 @@ +<%= header %> + +<%= boomerangScriptMin %> + + + + +<%= footer %> diff --git a/tests/page-templates/14-errors/36-events-window-reported.js b/tests/page-templates/14-errors/36-events-window-reported.js new file mode 100644 index 000000000..1b28b48fd --- /dev/null +++ b/tests/page-templates/14-errors/36-events-window-reported.js @@ -0,0 +1,154 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["errorFunction"]); + +describe("e2e/14-errors/36-events-window-reported", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + var C = BOOMR.utils.Compression; + + if (!window.addEventListener) { + it("Skipping on browser that doesn't support addEventListener", function() { + return this.skip(); + }); + + return; + } + + if (!window.postMessage) { + it("Skipping on browser that doesn't support postMessage", function() { + return this.skip(); + }); + + return; + } + + it("Should have only sent one page load beacon", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 1); + }); + + it("Should have put the err on the page load beacon", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b.err); + }); + + it("Should have had a single error in a browser that has the error object in onerror", function() { + var b = tf.lastBeacon(); + + if (!t.isErrorObjInOnErrorSupported()) { + return this.skip(); + } + + assert.equal(BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err)).length, 1); + }); + + it("Should have had 2 errors in a browser that does not have the error object in onerror", function() { + var b = tf.lastBeacon(); + + if (t.isErrorObjInOnErrorSupported()) { + return this.skip(); + } + + // PhantomJS, older IE and Safari will report 2 errors, one from the addEventListener wrapper and another from the global onerror + assert.equal(BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err)).length, 2); + }); + + it("Should have count = 1", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.count, 1); + }); + + it("Should have fileName of the page (if set)", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.fileName) { + assert.include(err.fileName, window.location.pathname.substring(window.location.pathname.lastIndexOf("/") + 1)); + } + else { + return this.skip(); + } + }); + + it("Should have functionName of 'errorFunction'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.functionName) { + assert.equal(err.functionName, "errorFunction"); + } + else { + return this.skip(); + } + }); + + it("Should have message = 'a is not defined' or 'Can't find variable: a' or ''a' is undefined'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + // Chrome, Firefox == a is not defined, Safari = Can't find variable, Edge = 'a' is not defined + assert.isTrue( + err.message.indexOf("a is not defined") !== -1 || + err.message.indexOf("Can't find variable: a") !== -1 || + err.message.indexOf("'a' is undefined") !== -1 || + err.message.indexOf("'a' is not defined") !== -1); + }); + + it("Should have source = APP", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.source, BOOMR.plugins.Errors.SOURCE_APP); + }); + + it("Should have stack with the stack", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.isDefined(err.stack); + }); + + it("Should have type = 'ReferenceError' or 'Error'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.isTrue(err.type === "ReferenceError" || err.type === "Error"); + }); + + it("Should have via = EVENTHANDLER", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.via, BOOMR.plugins.Errors.VIA_EVENTHANDLER); + }); + + it("Should have columNumber to be a number if specified", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (typeof err.columnNumber !== "undefined") { + assert.isTrue(err.columnNumber >= 0); + } + else { + return this.skip(); + } + }); + + it("Should have lineNumber ~ " + (HEADER_LINES + 26), function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.lineNumber) { + assert.closeTo(err.lineNumber, HEADER_LINES + 26, 5); + } + else { + return this.skip(); + } + }); +}); diff --git a/tests/page-templates/14-errors/37-events-element-reported.html b/tests/page-templates/14-errors/37-events-element-reported.html new file mode 100644 index 000000000..fcaa16f05 --- /dev/null +++ b/tests/page-templates/14-errors/37-events-element-reported.html @@ -0,0 +1,49 @@ +<%= header %> + +<%= boomerangScriptMin %> + + + + +<%= footer %> diff --git a/tests/page-templates/14-errors/37-events-element-reported.js b/tests/page-templates/14-errors/37-events-element-reported.js new file mode 100644 index 000000000..7a70919fa --- /dev/null +++ b/tests/page-templates/14-errors/37-events-element-reported.js @@ -0,0 +1,146 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["img", "img2", "errorFunction2"]); + +describe("e2e/14-errors/37-events-element-reported", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + var C = BOOMR.utils.Compression; + + if (!window.addEventListener) { + it("Skipping on browser that doesn't support addEventListener", function() { + return this.skip(); + }); + + return; + } + + it("Should have only sent one page load beacon", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 1); + }); + + it("Should have put the err on the page load beacon", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b.err); + }); + + it("Should have had a single error in a browser that has the error object in onerror", function() { + var b = tf.lastBeacon(); + + if (!t.isErrorObjInOnErrorSupported()) { + return this.skip(); + } + + assert.equal(BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err)).length, 1); + }); + + it("Should have had 2 errors in a browser that does not have the error object in onerror", function() { + var b = tf.lastBeacon(); + + if (t.isErrorObjInOnErrorSupported()) { + return this.skip(); + } + + // PhantomJS, older IE and Safari will report 2 errors, one from the addEventListener wrapper and another from the global onerror + assert.equal(BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err)).length, 2); + }); + + it("Should have count = 1", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.count, 1); + }); + + it("Should have fileName of the page (if set)", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.fileName) { + assert.include(err.fileName, window.location.pathname.substring(window.location.pathname.lastIndexOf("/") + 1)); + } + else { + return this.skip(); + } + }); + + it("Should have functionName of 'errorFunction'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.functionName) { + assert.include(err.functionName, "errorFunction"); + } + else { + return this.skip(); + } + }); + + it("Should have message = 'a is not defined' or 'Can't find variable: a' or ''a' is undefined'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + // Chrome, Firefox == a is not defined, Safari = Can't find variable, Edge = 'a' is not defined + assert.isTrue( + err.message.indexOf("a is not defined") !== -1 || + err.message.indexOf("Can't find variable: a") !== -1 || + err.message.indexOf("'a' is undefined") !== -1 || + err.message.indexOf("'a' is not defined") !== -1); + }); + + it("Should have source = APP", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.source, BOOMR.plugins.Errors.SOURCE_APP); + }); + + it("Should have stack with the stack", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.isDefined(err.stack); + }); + + it("Should have type = 'ReferenceError' or 'Error'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.isTrue(err.type === "ReferenceError" || err.type === "Error"); + }); + + it("Should have via = EVENTHANDLER", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.via, BOOMR.plugins.Errors.VIA_EVENTHANDLER); + }); + + it("Should have columNumber to be a number if specified", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (typeof err.columnNumber !== "undefined") { + assert.isTrue(err.columnNumber >= 0); + } + else { + return this.skip(); + } + }); + + it("Should have lineNumber ~ " + (HEADER_LINES + 24), function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.lineNumber) { + assert.closeTo(err.lineNumber, HEADER_LINES + 24, 5); + } + else { + return this.skip(); + } + }); +}); diff --git a/tests/page-templates/14-errors/38-events-xhr-reported.html b/tests/page-templates/14-errors/38-events-xhr-reported.html new file mode 100644 index 000000000..a52983671 --- /dev/null +++ b/tests/page-templates/14-errors/38-events-xhr-reported.html @@ -0,0 +1,35 @@ +<%= header %> + +<%= boomerangScriptMin %> + + + + +<%= footer %> diff --git a/tests/page-templates/14-errors/38-events-xhr-reported.js b/tests/page-templates/14-errors/38-events-xhr-reported.js new file mode 100644 index 000000000..f9bbb61eb --- /dev/null +++ b/tests/page-templates/14-errors/38-events-xhr-reported.js @@ -0,0 +1,153 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["xhr"]); + +describe("e2e/14-errors/38-events-xhr-reported", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + var C = BOOMR.utils.Compression; + + if (!BOOMR.plugins.AutoXHR) { + it("Skipping on non-AutoXHR supporting browser", function() { + return this.skip(); + }); + + return; + } + + it("Should have only sent one page load beacon", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 1); + }); + + it("Should have put the err on the page load beacon", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b.err); + }); + + it("Should have had a single error in a browser that has the error object in onerror", function() { + var b = tf.lastBeacon(); + + if (!t.isErrorObjInOnErrorSupported()) { + return this.skip(); + } + + assert.equal(BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err)).length, 1); + }); + + it("Should have had 2 errors in a browser that does not have the error object in onerror", function() { + var b = tf.lastBeacon(); + + if (t.isErrorObjInOnErrorSupported()) { + return this.skip(); + } + + // PhantomJS, older IE and Safari will report 2 errors, one from the addEventListener wrapper and another from the global onerror + assert.equal(BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err)).length, 2); + }); + + it("Should have count = 1", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.count, 1); + }); + + it("Should have fileName of the page (if set)", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.fileName) { + assert.include(err.fileName, window.location.pathname.substring(window.location.pathname.lastIndexOf("/") + 1)); + } + else { + return this.skip(); + } + }); + + it("Should have functionName of 'errorFunction'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.functionName) { + assert.include(err.functionName, "errorFunction"); + } + else { + return this.skip(); + } + }); + + it("Should have message = 'a is not defined' or 'Can't find variable: a' or ''a' is undefined'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + // Chrome, Firefox == a is not defined, Safari = Can't find variable, Edge = 'a' is not defined + assert.isTrue( + err.message.indexOf("a is not defined") !== -1 || + err.message.indexOf("Can't find variable: a") !== -1 || + err.message.indexOf("'a' is undefined") !== -1 || + err.message.indexOf("'a' is not defined") !== -1); + }); + + it("Should have source = APP", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.source, BOOMR.plugins.Errors.SOURCE_APP); + }); + + it("Should have stack with the stack", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.isDefined(err.stack); + }); + + it("Should have not have BOOMR_plugins_errors_wrap on the stack", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.notInclude(err.stack, "BOOMR_plugins_errors_wrap"); + }); + + it("Should have type = 'ReferenceError' or 'Error'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.isTrue(err.type === "ReferenceError" || err.type === "Error"); + }); + + it("Should have via = EVENTHANDLER", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.via, BOOMR.plugins.Errors.VIA_EVENTHANDLER); + }); + + it("Should have columNumber to be a number if specified", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (typeof err.columnNumber !== "undefined") { + assert.isTrue(err.columnNumber >= 0); + } + else { + return this.skip(); + } + }); + + it("Should have lineNumber ~ " + (HEADER_LINES + 26), function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.lineNumber) { + assert.closeTo(err.lineNumber, HEADER_LINES + 26, 5); + } + else { + return this.skip(); + } + }); +}); diff --git a/tests/page-templates/14-errors/39-settimeout-reported.html b/tests/page-templates/14-errors/39-settimeout-reported.html new file mode 100644 index 000000000..7c9696f83 --- /dev/null +++ b/tests/page-templates/14-errors/39-settimeout-reported.html @@ -0,0 +1,30 @@ +<%= header %> + +<%= boomerangScriptMin %> + + + + +<%= footer %> diff --git a/tests/page-templates/14-errors/39-settimeout-reported.js b/tests/page-templates/14-errors/39-settimeout-reported.js new file mode 100644 index 000000000..e2fbe7ec9 --- /dev/null +++ b/tests/page-templates/14-errors/39-settimeout-reported.js @@ -0,0 +1,138 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["errorFunction"]); + +describe("e2e/14-errors/39-settimeout-reported", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + var C = BOOMR.utils.Compression; + + it("Should have only sent one page load beacon", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 1); + }); + + it("Should have put the err on the page load beacon", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b.err); + }); + + it("Should have had a single error in a browser that has the error object in onerror", function() { + var b = tf.lastBeacon(); + + if (!t.isErrorObjInOnErrorSupported()) { + return this.skip(); + } + + assert.equal(BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err)).length, 1); + }); + + it("Should have had 2 errors in a browser that does not have the error object in onerror", function() { + var b = tf.lastBeacon(); + + if (t.isErrorObjInOnErrorSupported()) { + return this.skip(); + } + + // PhantomJS, older IE and Safari will report 2 errors, one from the settimeout wrapper and another from the global onerror + assert.equal(BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err)).length, 2); + }); + + it("Should have count = 1", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.count, 1); + }); + + it("Should have fileName of the page (if set)", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.fileName) { + assert.include(err.fileName, window.location.pathname.substring(window.location.pathname.lastIndexOf("/") + 1)); + } + else { + return this.skip(); + } + }); + + it("Should have functionName of 'errorFunction'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.functionName) { + assert.equal(err.functionName, "errorFunction"); + } + else { + return this.skip(); + } + }); + + it("Should have message = 'a is not defined' or 'Can't find variable: a' or ''a' is undefined'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + // Chrome, Firefox == a is not defined, Safari = Can't find variable, Edge = 'a' is not defined + assert.isTrue( + err.message.indexOf("a is not defined") !== -1 || + err.message.indexOf("Can't find variable: a") !== -1 || + err.message.indexOf("'a' is undefined") !== -1 || + err.message.indexOf("'a' is not defined") !== -1); + }); + + it("Should have source = APP", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.source, BOOMR.plugins.Errors.SOURCE_APP); + }); + + it("Should have stack with the stack", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.isDefined(err.stack); + }); + + it("Should have type = 'ReferenceError' or 'Error'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.isTrue(err.type === "ReferenceError" || err.type === "Error"); + }); + + it("Should have via = TIMEOUT", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.via, BOOMR.plugins.Errors.VIA_TIMEOUT); + }); + + it("Should have columNumber to be a number if specified", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (typeof err.columnNumber !== "undefined") { + assert.isTrue(err.columnNumber >= 0); + } + else { + return this.skip(); + } + }); + + it("Should have lineNumber ~ " + (HEADER_LINES + 25), function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.lineNumber) { + assert.closeTo(err.lineNumber, HEADER_LINES + 25, 5); + } + else { + return this.skip(); + } + }); +}); diff --git a/tests/page-templates/14-errors/40-unbound-addeventlistener.html b/tests/page-templates/14-errors/40-unbound-addeventlistener.html new file mode 100644 index 000000000..2abf7c328 --- /dev/null +++ b/tests/page-templates/14-errors/40-unbound-addeventlistener.html @@ -0,0 +1,14 @@ +<%= header %> +<%= boomerangSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/14-errors/40-unbound-addeventlistener.js b/tests/page-templates/14-errors/40-unbound-addeventlistener.js new file mode 100644 index 000000000..dee159222 --- /dev/null +++ b/tests/page-templates/14-errors/40-unbound-addeventlistener.js @@ -0,0 +1,32 @@ +/* eslint-env mocha */ + +describe("e2e/14-errors/40-unbound-addeventlistener", function() { + it("Should infer BOOMR.window as context for unbound addEventListener calls", function(done) { + if (typeof window.EventTarget === "undefined") { + return this.skip(); + } + + assert.equal(BOOMR.window, top); + assert.equal(BOOMR.window, window.parent); + (function(){ + // this is the unbound addEventListener call + addEventListener("foo", function(){ + // this assert is redundant, if we get our callback, it means it fired on the right window + assert.equal(this, BOOMR.window); + done(); + }); + + var event; + + try { + event = new Event("foo"); + } + catch (err) { + event = window.document.createEvent("Event"); + event.initEvent("foo", true, true); + } + + this.dispatchEvent(event); + }).apply(BOOMR.window); + }); +}); diff --git a/tests/page-templates/14-errors/41-addeventlistener-dedupping.html b/tests/page-templates/14-errors/41-addeventlistener-dedupping.html new file mode 100644 index 000000000..58e45d305 --- /dev/null +++ b/tests/page-templates/14-errors/41-addeventlistener-dedupping.html @@ -0,0 +1,28 @@ +<%= header %> +<%= boomerangDelayedSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/14-errors/41-addeventlistener-dedupping.js b/tests/page-templates/14-errors/41-addeventlistener-dedupping.js new file mode 100644 index 000000000..346c50d6f --- /dev/null +++ b/tests/page-templates/14-errors/41-addeventlistener-dedupping.js @@ -0,0 +1,15 @@ +/* eslint-env mocha */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["BOOMR_script_delay", "handler", "attach"]); + +describe("e2e/14-errors/41-addeventlistener-dedupping", function() { + var tf = BOOMR.plugins.TestFramework; + + it("Should have only fired the foo handler once", function(done) { + var b = tf.lastBeacon(); + + assert.equal(b.foo, "bar"); + done(); + }); +}); diff --git a/tests/page-templates/14-errors/42-unbound-removeeventlistener.html b/tests/page-templates/14-errors/42-unbound-removeeventlistener.html new file mode 100644 index 000000000..d237e0ed1 --- /dev/null +++ b/tests/page-templates/14-errors/42-unbound-removeeventlistener.html @@ -0,0 +1,14 @@ +<%= header %> +<%= boomerangSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/14-errors/42-unbound-removeeventlistener.js b/tests/page-templates/14-errors/42-unbound-removeeventlistener.js new file mode 100644 index 000000000..99207fd51 --- /dev/null +++ b/tests/page-templates/14-errors/42-unbound-removeeventlistener.js @@ -0,0 +1,37 @@ +/* eslint-env mocha */ + +describe("e2e/14-errors/42-unbound-removeeventlistener", function() { + it("Should infer BOOMR.window as context for unbound removeEventListener calls", function(done) { + if (typeof window.EventTarget === "undefined") { + return this.skip(); + } + + assert.equal(BOOMR.window, top); + assert.equal(BOOMR.window, window.parent); + (function(){ + function callback(){ + assert.fail(0, 1, "Event should not fire"); + }; + this.addEventListener("foo", callback); + + // this is the unbound removeEventListener call + removeEventListener("foo", callback); + + var event; + + try { + event = new Event("foo"); + } + catch (err) { + event = window.document.createEvent("Event"); + event.initEvent("foo", true, true); + } + + this.dispatchEvent(event); + + setTimeout(function(){ + done(); + }, 10); + }).apply(BOOMR.window); + }); +}); diff --git a/tests/page-templates/14-errors/43-errorevent.html b/tests/page-templates/14-errors/43-errorevent.html new file mode 100644 index 000000000..9ac971f22 --- /dev/null +++ b/tests/page-templates/14-errors/43-errorevent.html @@ -0,0 +1,30 @@ +<%= header %> + +<%= boomerangScriptMin %> + + +<%= footer %> diff --git a/tests/page-templates/14-errors/43-errorevent.js b/tests/page-templates/14-errors/43-errorevent.js new file mode 100644 index 000000000..7223d5dc5 --- /dev/null +++ b/tests/page-templates/14-errors/43-errorevent.js @@ -0,0 +1,183 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["errorFunction"]); + +describe("e2e/14-errors/43-errorevent", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + var C = BOOMR.utils.Compression; + + it("Should have sent a single beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have put the err on the beacon (if ErrorEvent is supported)", function() { + if (!window.ErrorEvent) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isDefined(b.err); + }); + + it("Should have had a single error (if ErrorEvent is supported)", function() { + if (!window.ErrorEvent) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.equal(C.jsUrlDecompress(b.err).length, 1); + }); + + it("Should have count = 1 (if ErrorEvent is supported)", function() { + if (!window.ErrorEvent) { + return this.skip(); + } + + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.count, 1); + }); + + it("Should have had a recent timestamp (if ErrorEvent is supported)", function() { + if (!window.ErrorEvent) { + return this.skip(); + } + + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.closeTo(err.timestamp, BOOMR.now(), 10000); + }); + + it("Should have a fileName (if ErrorEvent is supported)", function() { + if (!window.ErrorEvent) { + return this.skip(); + } + + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.fileName) { + assert.include(err.fileName, "43-errorevent.html"); + } + else { + this.skip(); + } + }); + + it("Should have functionName of 'errorFunction' (if ErrorEvent is supported)", function() { + if (!window.ErrorEvent) { + return this.skip(); + } + + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.functionName, "errorFunction"); + }); + + it("Should have message = 'errorMessage' (if ErrorEvent is supported)", function() { + if (!window.ErrorEvent) { + return this.skip(); + } + + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (!t.isIE()) { + assert.equal(err.message, "errorMessage"); + } + else { + // IE has ErrorEvent but doesn't implement the ErrorEvent constructor + assert.equal(err.message, "Object doesn't support this action"); + } + }); + + it("Should have source = APP (if ErrorEvent is supported)", function() { + if (!window.ErrorEvent) { + return this.skip(); + } + + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.source, BOOMR.plugins.Errors.SOURCE_APP); + }); + + it("Should have stack with the stack (if ErrorEvent is supported)", function() { + if (!window.ErrorEvent) { + return this.skip(); + } + + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.isDefined(err.stack); + }); + + it("Should have type = 'Error' (if ErrorEvent is supported)", function() { + if (!window.ErrorEvent) { + return this.skip(); + } + + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (!t.isIE()) { + assert.equal(err.type, "Error"); + } + else { + // IE has ErrorEvent but doesn't implement the ErrorEvent constructor + assert.equal(err.type, "TypeError"); + } + }); + + it("Should have via = GLOBAL_EXCEPTION_HANDLER (if ErrorEvent is supported)", function() { + if (!window.ErrorEvent) { + return this.skip(); + } + + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.via, BOOMR.plugins.Errors.VIA_GLOBAL_EXCEPTION_HANDLER); + }); + + it("Should have columNumber to be a number if specified (if ErrorEvent is supported)", function() { + if (!window.ErrorEvent) { + return this.skip(); + } + + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (typeof err.columnNumber !== "undefined") { + assert.isTrue(err.columnNumber >= 0); + } + else { + this.skip(); + } + }); + + it("Should have lineNumber 1234 (if ErrorEvent is supported)", function() { + if (!window.ErrorEvent) { + return this.skip(); + } + + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.lineNumber) { + assert.closeTo(err.lineNumber, HEADER_LINES + 19, 5); + } + else { + this.skip(); + } + }); +}); diff --git a/tests/page-templates/14-errors/44-unhandled-rejection.html b/tests/page-templates/14-errors/44-unhandled-rejection.html new file mode 100644 index 000000000..12af2c4b7 --- /dev/null +++ b/tests/page-templates/14-errors/44-unhandled-rejection.html @@ -0,0 +1,46 @@ +<%= header %> +<%= boomerangScript %> + + + + +<%= footer %> diff --git a/tests/page-templates/14-errors/44-unhandled-rejection.js b/tests/page-templates/14-errors/44-unhandled-rejection.js new file mode 100644 index 000000000..da3709f05 --- /dev/null +++ b/tests/page-templates/14-errors/44-unhandled-rejection.js @@ -0,0 +1,173 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["p1", "p2", "p3"]); + +describe("e2e/14-errors/44-unhandled-rejection", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + var C = BOOMR.utils.Compression; + + it("Should have only sent one page load beacon", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 1); + }); + + it("Should have had a five errors (if PromiseRejectionEvent is supported)", function() { + if (!window.Promise || !window.PromiseRejectionEvent) { + this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isDefined(b.err); + assert.equal(BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err)).length, 5); + }); + + it("Should have had a zero errors (if PromiseRejectionEvent is not supported)", function() { + if (window.Promise && window.PromiseRejectionEvent) { + this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isUndefined(b.err); + }); + + if (window.Promise && window.PromiseRejectionEvent) { + var ERRORS = [ + { messageTest: function(msg) { + assert.equal(msg, "error string"); + } }, + + { func: "func2", line: 19, messageTest: function(msg) { + assert.equal(msg, "error object"); + } }, + + { messageTest: function(msg) { + assert.equal(msg, "reject string"); + } }, + + { line: 35, messageTest: function(msg) { + // Chrome, Firefox == a is not defined, Safari = Can't find variable, Edge = 'a' is not defined + assert.isTrue( + msg.indexOf("a is not defined") !== -1 || + msg.indexOf("Can't find variable: a") !== -1 || + msg.indexOf("'a' is undefined") !== -1 || + msg.indexOf("'a' is not defined") !== -1); + }}, + + { messageTest: function(msg) { + assert.equal(msg, "[object Object]"); + } } + ]; + + /* eslint-disable no-loop-func */ + for (var n = 0; n < 5; n++) { + (function(i) { + describe("Beacon 1, Error " + (i + 1), function() { + it("Should have count = 1", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[i]; + + assert.equal(err.count, 1); + }); + + it("Should have fileName of the page (if set)", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[i]; + + if (err.fileName) { + assert.include( + err.fileName, + window.location.pathname.substring(window.location.pathname.lastIndexOf("/") + 1)); + } + else { + return this.skip(); + } + }); + + it("Should have correct functionName (if set)", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[i]; + + if (err.functionName && ERRORS[i].func) { + assert.include(err.functionName, ERRORS[i].func); + } + else { + return this.skip(); + } + }); + + it("Should have correct message", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[i]; + + ERRORS[i].messageTest(err.message); + }); + + it("Should have source = APP", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[i]; + + assert.equal(err.source, BOOMR.plugins.Errors.SOURCE_APP); + }); + + it("Should have stack with the stack", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[i]; + + assert.isDefined(err.stack); + }); + + it("Should have not have BOOMR_plugins_errors_wrap on the stack", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[i]; + + assert.notInclude(err.stack, "BOOMR_plugins_errors_wrap"); + }); + + it("Should have correct type = 'ReferenceError' or 'Error'", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[i]; + + assert.isTrue(err.type === "ReferenceError" || err.type === "Error"); + }); + + it("Should have via = REJECTION", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[i]; + + assert.equal(err.via, BOOMR.plugins.Errors.VIA_REJECTION); + }); + + it("Should have columNumber to be a number (if set)", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[i]; + + if (typeof err.columnNumber !== "undefined") { + assert.isTrue(err.columnNumber >= 0); + } + else { + return this.skip(); + } + }); + + it("Should have correct lineNumber (if set)", function() { + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[i]; + + if (err.lineNumber) { + assert.closeTo(err.lineNumber, HEADER_LINES + ERRORS[i].line, 5); + } + else { + return this.skip(); + } + }); + }); + })(n); + } + /* eslint-enable no-loop-func */ + } +}); diff --git a/tests/page-templates/14-errors/45-reporting-api.html b/tests/page-templates/14-errors/45-reporting-api.html new file mode 100644 index 000000000..e932e11eb --- /dev/null +++ b/tests/page-templates/14-errors/45-reporting-api.html @@ -0,0 +1,27 @@ +<%= header %> +<%= boomerangScriptMin %> + + + +<%= footer %> diff --git a/tests/page-templates/14-errors/45-reporting-api.js b/tests/page-templates/14-errors/45-reporting-api.js new file mode 100644 index 000000000..3a1827a92 --- /dev/null +++ b/tests/page-templates/14-errors/45-reporting-api.js @@ -0,0 +1,176 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["errorFunction", "willReportDeprecation"]); + +describe("e2e/14-errors/45-reporting-api", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + var C = BOOMR.utils.Compression; + + it("Should have sent a single beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have put the err on the beacon", function() { + if (!window.willReportDeprecation) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isDefined(b.err); + }); + + it("Should have had a single error", function() { + if (!window.willReportDeprecation) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.equal(C.jsUrlDecompress(b.err).length, 1); + }); + + it("Should have count = 1", function() { + if (!window.willReportDeprecation) { + return this.skip(); + } + + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.count, 1); + }); + + it("Should have had a recent timestamp", function() { + if (!window.willReportDeprecation) { + return this.skip(); + } + + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.closeTo(err.timestamp, BOOMR.now(), 10000); + }); + + it("Should have fileName of the page (if set)", function() { + if (!window.willReportDeprecation) { + return this.skip(); + } + + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.fileName) { + assert.include(err.fileName, "45-reporting-api.html"); + } + else { + this.skip(); + } + }); + + it("Should have functionName of 'errorFunction'", function() { + if (!window.willReportDeprecation) { + return this.skip(); + } + + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.functionName) { + assert.equal(err.functionName, "errorFunction"); + } + else { + this.skip(); + } + }); + + it("Should have correct message", function() { + if (!window.willReportDeprecation) { + return this.skip(); + } + + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.include(err.message, "chrome.loadTimes()"); + }); + + it("Should have source = APP", function() { + if (!window.willReportDeprecation) { + return this.skip(); + } + + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.source, BOOMR.plugins.Errors.SOURCE_APP); + }); + + it("Should have stack with the stack", function() { + if (!window.willReportDeprecation) { + return this.skip(); + } + + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.isDefined(err.stack); + }); + + it("Should have type = 'Error'", function() { + if (!window.willReportDeprecation) { + return this.skip(); + } + + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.type, "Error"); + }); + + it("Should have via = REPORTING", function() { + if (!window.willReportDeprecation) { + return this.skip(); + } + + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + assert.equal(err.via, BOOMR.plugins.Errors.VIA_REPORTING_API); + }); + + it("Should have columNumber to be a number if specified", function() { + if (!window.willReportDeprecation) { + return this.skip(); + } + + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (typeof err.columnNumber !== "undefined") { + assert.isTrue(err.columnNumber >= 0); + } + else { + this.skip(); + } + }); + + it("Should have lineNumber ~ " + (HEADER_LINES + 12), function() { + if (!window.willReportDeprecation) { + return this.skip(); + } + + var b = tf.lastBeacon(); + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(b.err))[0]; + + if (err.lineNumber) { + assert.closeTo(err.lineNumber, HEADER_LINES + 12, 5); + } + else { + this.skip(); + } + }); +}); diff --git a/tests/page-templates/14-errors/46-error-during-onload.html b/tests/page-templates/14-errors/46-error-during-onload.html new file mode 100644 index 000000000..c11e1cd74 --- /dev/null +++ b/tests/page-templates/14-errors/46-error-during-onload.html @@ -0,0 +1,56 @@ +<%= header %> + +<%= boomerangScriptMin %> + + +<%= footer %> diff --git a/tests/page-templates/14-errors/46-error-during-onload.js b/tests/page-templates/14-errors/46-error-during-onload.js new file mode 100644 index 000000000..09bdf8cad --- /dev/null +++ b/tests/page-templates/14-errors/46-error-during-onload.js @@ -0,0 +1,33 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["errorFunction"]); + +describe("e2e/14-errors/46-error-during-onload", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + var C = BOOMR.utils.Compression; + + it("Should have sent a single beacon", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have put the err on the beacon", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b.err); + }); + + it("Should not have http.initiator type set to Error", function() { + var b = tf.lastBeacon(); + + assert.isUndefined(b["http.initiator"], "http.initiator should not be defined"); + }); + + it("Should have had a single error", function() { + var b = tf.lastBeacon(); + + assert.equal(C.jsUrlDecompress(b.err).length, 1); + }); +}); diff --git a/tests/page-templates/14-errors/47-after-unload.html b/tests/page-templates/14-errors/47-after-unload.html new file mode 100644 index 000000000..71c056664 --- /dev/null +++ b/tests/page-templates/14-errors/47-after-unload.html @@ -0,0 +1,59 @@ +<%= header %> + +<%= boomerangSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/14-errors/47-after-unload.js b/tests/page-templates/14-errors/47-after-unload.js new file mode 100644 index 000000000..5c4dbc761 --- /dev/null +++ b/tests/page-templates/14-errors/47-after-unload.js @@ -0,0 +1,18 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["origSetTimeout"]); + +describe("e2e/14-errors/47-after-unload", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a single beacon", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have not generated an error after Boomerang was unloaded", function() { + assert.isUndefined(window.onerrorHit); + }); +}); diff --git a/tests/page-templates/14-errors/48-autorun-false-multiple-then-load-no-send-during-onload.html b/tests/page-templates/14-errors/48-autorun-false-multiple-then-load-no-send-during-onload.html new file mode 100644 index 000000000..e988ddff5 --- /dev/null +++ b/tests/page-templates/14-errors/48-autorun-false-multiple-then-load-no-send-during-onload.html @@ -0,0 +1,30 @@ +<%= header %> +<%= boomerangScriptMin %> + + + + +<%= footer %> diff --git a/tests/page-templates/14-errors/48-autorun-false-multiple-then-load-no-send-during-onload.js b/tests/page-templates/14-errors/48-autorun-false-multiple-then-load-no-send-during-onload.js new file mode 100644 index 000000000..9bb9c5699 --- /dev/null +++ b/tests/page-templates/14-errors/48-autorun-false-multiple-then-load-no-send-during-onload.js @@ -0,0 +1,53 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert,describe,it */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["a", "b"]); + +describe("e2e/14-errors/48-autorun-false-multiple-then-load-no-send-during-onload", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + var C = BOOMR.utils.Compression; + + it("Should have sent one beacon", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 1); + }); + + it("Should have put err on the first beacon", function() { + var b = tf.beacons[0]; + + assert.isDefined(b.err); + }); + + it("Should have no http.initiator the first beacon", function() { + var b = tf.beacons[0]; + + assert.isUndefined(b["http.initiator"]); + }); + + it("Should have rt.start=navigation on the first beacon (if NavigationTiming is supported)", function() { + if (t.isNavigationTimingSupported()) { + assert.equal(tf.beacons[0]["rt.start"], "navigation"); + } + else { + assert.equal(tf.beacons[0]["rt.start"], "none"); + } + }); + + it("Should have put NavigationTiming metrics on the first beacon (if NavigationTiming is supported)", function() { + if (t.isNavigationTimingSupported()) { + assert.isDefined(tf.beacons[0].nt_nav_st); + assert.isDefined(tf.beacons[0].nt_load_st); + } + else { + return this.skip(); + } + }); + + it("Should have rt.sl = 1 on the first beacon", function() { + var b = tf.beacons[0]; + + assert.equal(b["rt.sl"], 1); + }); +}); diff --git a/tests/page-templates/14-errors/tests.json5 b/tests/page-templates/14-errors/tests.json5 new file mode 100644 index 000000000..cf5efee9c --- /dev/null +++ b/tests/page-templates/14-errors/tests.json5 @@ -0,0 +1,8 @@ +{ + all: { + requires: ["errors"] + }, + "08-network": { + requires: ["auto-xhr"] + } +} diff --git a/tests/page-templates/16-third-party-analytics/00-adobe-cookie-svi.html b/tests/page-templates/16-third-party-analytics/00-adobe-cookie-svi.html new file mode 100644 index 000000000..76c63342b --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/00-adobe-cookie-svi.html @@ -0,0 +1,21 @@ +<%= header %> +<%= boomerangSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/16-third-party-analytics/00-adobe-cookie-svi.js b/tests/page-templates/16-third-party-analytics/00-adobe-cookie-svi.js new file mode 100644 index 000000000..84e63ee06 --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/00-adobe-cookie-svi.js @@ -0,0 +1,36 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["_satellite"]); + +describe("e2e/16-third-party-analytics/00-adobe-cookie-svi", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have Adobe AID set (on a domain or in PhantomJS)", function() { + if (t.canSetCookies()) { + var b = tf.lastBeacon(); + + assert.equal(b["tp.aa.aid"], "2B8147DA850785C4-6000010E2006DC28"); + } + }); + + it("Should be missing Adobe AID (on localhost or an IP)", function() { + if (!t.canSetCookies()) { + var b = tf.lastBeacon(); + + assert.equal(b["tp.aa.aid"], undefined); + } + }); + + it("Should be missing Adobe MID", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.aa.mid"], undefined); + }); +}); diff --git a/tests/page-templates/16-third-party-analytics/01-adobe-cookie-svi-invalid.html b/tests/page-templates/16-third-party-analytics/01-adobe-cookie-svi-invalid.html new file mode 100644 index 000000000..f1740be3b --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/01-adobe-cookie-svi-invalid.html @@ -0,0 +1,21 @@ +<%= header %> +<%= boomerangSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/16-third-party-analytics/01-adobe-cookie-svi-invalid.js b/tests/page-templates/16-third-party-analytics/01-adobe-cookie-svi-invalid.js new file mode 100644 index 000000000..1daa40c24 --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/01-adobe-cookie-svi-invalid.js @@ -0,0 +1,26 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["_satellite"]); + +describe("e2e/16-third-party-analytics/01-adobe-cookie-svi-invalid", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should be missing Adobe AID", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.aa.aid"], undefined); + }); + + it("Should be missing Adobe MID", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.aa.mid"], undefined); + }); +}); diff --git a/tests/page-templates/16-third-party-analytics/03-adobe-cookie-sfid.html b/tests/page-templates/16-third-party-analytics/03-adobe-cookie-sfid.html new file mode 100644 index 000000000..cb63a01bf --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/03-adobe-cookie-sfid.html @@ -0,0 +1,21 @@ +<%= header %> +<%= boomerangSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/16-third-party-analytics/03-adobe-cookie-sfid.js b/tests/page-templates/16-third-party-analytics/03-adobe-cookie-sfid.js new file mode 100644 index 000000000..458b0c642 --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/03-adobe-cookie-sfid.js @@ -0,0 +1,36 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["_satellite"]); + +describe("e2e/16-third-party-analytics/03-adobe-cookie-sfid", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have Adobe AID set (on a domain or in PhantomJS)", function() { + if (t.canSetCookies()) { + var b = tf.lastBeacon(); + + assert.equal(b["tp.aa.aid"], "6B280792FE0CFE56-162DA99B1988A2F8"); + } + }); + + it("Should be missing Adobe AID (on localhost or an IP)", function() { + if (!t.canSetCookies()) { + var b = tf.lastBeacon(); + + assert.equal(b["tp.aa.aid"], undefined); + } + }); + + it("Should be missing Adobe MID", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.aa.mid"], undefined); + }); +}); diff --git a/tests/page-templates/16-third-party-analytics/04-adobe-cookie-svi-sfid.html b/tests/page-templates/16-third-party-analytics/04-adobe-cookie-svi-sfid.html new file mode 100644 index 000000000..96686181f --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/04-adobe-cookie-svi-sfid.html @@ -0,0 +1,22 @@ +<%= header %> +<%= boomerangSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/16-third-party-analytics/04-adobe-cookie-svi-sfid.js b/tests/page-templates/16-third-party-analytics/04-adobe-cookie-svi-sfid.js new file mode 100644 index 000000000..c065dfef5 --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/04-adobe-cookie-svi-sfid.js @@ -0,0 +1,36 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["_satellite"]); + +describe("e2e/16-third-party-analytics/04-adobe-cookie-svi-sfid", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have Adobe AID set from s_vi cookie (on a domain or in PhantomJS)", function() { + if (t.canSetCookies()) { + var b = tf.lastBeacon(); + + assert.equal(b["tp.aa.aid"], "2B8147DA850785C4-6000010E2006DC28"); + } + }); + + it("Should be missing Adobe AID (on localhost or an IP)", function() { + if (!t.canSetCookies()) { + var b = tf.lastBeacon(); + + assert.equal(b["tp.aa.aid"], undefined); + } + }); + + it("Should be missing Adobe MID", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.aa.mid"], undefined); + }); +}); diff --git a/tests/page-templates/16-third-party-analytics/05-adobe-cookie-amcv.html b/tests/page-templates/16-third-party-analytics/05-adobe-cookie-amcv.html new file mode 100644 index 000000000..3a9d2d319 --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/05-adobe-cookie-amcv.html @@ -0,0 +1,46 @@ +<%= header %> +<%= boomerangSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/16-third-party-analytics/05-adobe-cookie-amcv.js b/tests/page-templates/16-third-party-analytics/05-adobe-cookie-amcv.js new file mode 100644 index 000000000..151ec3867 --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/05-adobe-cookie-amcv.js @@ -0,0 +1,46 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["Visitor"]); + +describe("e2e/16-third-party-analytics/05-adobe-cookie-amcv", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have Adobe AID set (on a domain or in PhantomJS)", function() { + if (t.canSetCookies()) { + var b = tf.lastBeacon(); + + assert.equal(b["tp.aa.aid"], "AAAAAAAAAAAAAAAA-AAAAAAAAAAAAAAAA"); + } + }); + + it("Should be missing Adobe AID (on localhost or an IP)", function() { + if (!t.canSetCookies()) { + var b = tf.lastBeacon(); + + assert.equal(b["tp.aa.aid"], undefined); + } + }); + + it("Should have Adobe MID set (on a domain or in PhantomJS)", function() { + if (t.canSetCookies()) { + var b = tf.lastBeacon(); + + assert.equal(b["tp.aa.mid"], "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"); + } + }); + + it("Should be missing Adobe MID (on localhost or an IP)", function() { + if (!t.canSetCookies()) { + var b = tf.lastBeacon(); + + assert.equal(b["tp.aa.mid"], undefined); + } + }); +}); diff --git a/tests/page-templates/16-third-party-analytics/06-adobe-cookie-amcv.html b/tests/page-templates/16-third-party-analytics/06-adobe-cookie-amcv.html new file mode 100644 index 000000000..a0d43a9f9 --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/06-adobe-cookie-amcv.html @@ -0,0 +1,50 @@ +<%= header %> +<%= boomerangSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/16-third-party-analytics/06-adobe-cookie-amcv.js b/tests/page-templates/16-third-party-analytics/06-adobe-cookie-amcv.js new file mode 100644 index 000000000..ca5bfd91e --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/06-adobe-cookie-amcv.js @@ -0,0 +1,46 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["Visitor", "s"]); + +describe("e2e/16-third-party-analytics/06-adobe-cookie-amcv", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have Adobe AID set (on a domain or in PhantomJS)", function() { + if (t.canSetCookies()) { + var b = tf.lastBeacon(); + + assert.equal(b["tp.aa.aid"], "AAAAAAAAAAAAAAAA-AAAAAAAAAAAAAAAA"); + } + }); + + it("Should be missing Adobe AID (on localhost or an IP)", function() { + if (!t.canSetCookies()) { + var b = tf.lastBeacon(); + + assert.equal(b["tp.aa.aid"], undefined); + } + }); + + it("Should have Adobe MID set (on a domain or in PhantomJS)", function() { + if (t.canSetCookies()) { + var b = tf.lastBeacon(); + + assert.equal(b["tp.aa.mid"], "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"); + } + }); + + it("Should be missing Adobe MID (on localhost or an IP)", function() { + if (!t.canSetCookies()) { + var b = tf.lastBeacon(); + + assert.equal(b["tp.aa.mid"], undefined); + } + }); +}); diff --git a/tests/page-templates/16-third-party-analytics/07a-adobe-invalid-amcv.html b/tests/page-templates/16-third-party-analytics/07a-adobe-invalid-amcv.html new file mode 100644 index 000000000..62e3ae344 --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/07a-adobe-invalid-amcv.html @@ -0,0 +1,31 @@ +<%= header %> +<%= boomerangSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/16-third-party-analytics/07a-adobe-invalid-amcv.js b/tests/page-templates/16-third-party-analytics/07a-adobe-invalid-amcv.js new file mode 100644 index 000000000..99c749591 --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/07a-adobe-invalid-amcv.js @@ -0,0 +1,38 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["Visitor"]); + +describe("e2e/16-third-party-analytics/07a-adobe-invalid-amcv", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should be missing Adobe AID", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.aa.aid"], undefined); + }); + + it("Should be missing Adobe MID", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.aa.mid"], undefined); + }); + + it("Should not have an app error on the beacon", function() { + var b = tf.lastBeacon(); + + assert.equal(b.err, undefined); + }); + + it("Should not have a boomerang error on the beacon", function() { + var b = tf.lastBeacon(); + + assert.equal(b.errors, undefined); + }); +}); diff --git a/tests/page-templates/16-third-party-analytics/07b-adobe-invalid-amcv.html b/tests/page-templates/16-third-party-analytics/07b-adobe-invalid-amcv.html new file mode 100644 index 000000000..8e45f18fd --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/07b-adobe-invalid-amcv.html @@ -0,0 +1,46 @@ +<%= header %> +<%= boomerangSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/16-third-party-analytics/07b-adobe-invalid-amcv.js b/tests/page-templates/16-third-party-analytics/07b-adobe-invalid-amcv.js new file mode 100644 index 000000000..be7ad1e12 --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/07b-adobe-invalid-amcv.js @@ -0,0 +1,38 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["Visitor"]); + +describe("e2e/16-third-party-analytics/07b-adobe-invalid-amcv", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should be missing Adobe AID", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.aa.aid"], undefined); + }); + + it("Should be missing Adobe MID", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.aa.mid"], undefined); + }); + + it("Should not have an app error on the beacon", function() { + var b = tf.lastBeacon(); + + assert.equal(b.err, undefined); + }); + + it("Should not have a boomerang error on the beacon", function() { + var b = tf.lastBeacon(); + + assert.equal(b.errors, undefined); + }); +}); diff --git a/tests/page-templates/16-third-party-analytics/07c-adobe-invalid-amcv.html b/tests/page-templates/16-third-party-analytics/07c-adobe-invalid-amcv.html new file mode 100644 index 000000000..b2378b025 --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/07c-adobe-invalid-amcv.html @@ -0,0 +1,46 @@ +<%= header %> +<%= boomerangSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/16-third-party-analytics/07c-adobe-invalid-amcv.js b/tests/page-templates/16-third-party-analytics/07c-adobe-invalid-amcv.js new file mode 100644 index 000000000..5f49edf12 --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/07c-adobe-invalid-amcv.js @@ -0,0 +1,48 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["Visitor"]); + +describe("e2e/16-third-party-analytics/07c-adobe-invalid-amcv", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should be missing Adobe AID", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.aa.aid"], undefined); + }); + + it("Should have Adobe MID set (on a domain or in PhantomJS)", function() { + if (t.canSetCookies()) { + var b = tf.lastBeacon(); + + assert.equal(b["tp.aa.mid"], "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"); + } + }); + + it("Should be missing Adobe MID (on localhost or an IP)", function() { + if (!t.canSetCookies()) { + var b = tf.lastBeacon(); + + assert.equal(b["tp.aa.mid"], undefined); + } + }); + + it("Should not have an app error on the beacon", function() { + var b = tf.lastBeacon(); + + assert.equal(b.err, undefined); + }); + + it("Should not have a boomerang error on the beacon", function() { + var b = tf.lastBeacon(); + + assert.equal(b.errors, undefined); + }); +}); diff --git a/tests/page-templates/16-third-party-analytics/07d-adobe-invalid-amcv.html b/tests/page-templates/16-third-party-analytics/07d-adobe-invalid-amcv.html new file mode 100644 index 000000000..dd94fbffb --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/07d-adobe-invalid-amcv.html @@ -0,0 +1,21 @@ +<%= header %> +<%= boomerangSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/16-third-party-analytics/07d-adobe-invalid-amcv.js b/tests/page-templates/16-third-party-analytics/07d-adobe-invalid-amcv.js new file mode 100644 index 000000000..a4b85eafe --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/07d-adobe-invalid-amcv.js @@ -0,0 +1,38 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["s"]); + +describe("e2e/16-third-party-analytics/07d-adobe-invalid-amcv", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should be missing Adobe AID", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.aa.aid"], undefined); + }); + + it("Should be missing Adobe MID", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.aa.mid"], undefined); + }); + + it("Should not have an app error on the beacon", function() { + var b = tf.lastBeacon(); + + assert.equal(b.err, undefined); + }); + + it("Should not have a boomerang error on the beacon", function() { + var b = tf.lastBeacon(); + + assert.equal(b.errors, undefined); + }); +}); diff --git a/tests/page-templates/16-third-party-analytics/07e-adobe-invalid-amcv.html b/tests/page-templates/16-third-party-analytics/07e-adobe-invalid-amcv.html new file mode 100644 index 000000000..23ece9b8b --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/07e-adobe-invalid-amcv.html @@ -0,0 +1,50 @@ +<%= header %> +<%= boomerangSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/16-third-party-analytics/07e-adobe-invalid-amcv.js b/tests/page-templates/16-third-party-analytics/07e-adobe-invalid-amcv.js new file mode 100644 index 000000000..04e99eee0 --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/07e-adobe-invalid-amcv.js @@ -0,0 +1,38 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["Visitor", "s"]); + +describe("e2e/16-third-party-analytics/07e-adobe-invalid-amcv", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should be missing Adobe AID", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.aa.aid"], undefined); + }); + + it("Should be missing Adobe MID", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.aa.mid"], undefined); + }); + + it("Should not have an app error on the beacon", function() { + var b = tf.lastBeacon(); + + assert.equal(b.err, undefined); + }); + + it("Should not have a boomerang error on the beacon", function() { + var b = tf.lastBeacon(); + + assert.equal(b.errors, undefined); + }); +}); diff --git a/tests/page-templates/16-third-party-analytics/07f-adobe-invalid-amcv.html b/tests/page-templates/16-third-party-analytics/07f-adobe-invalid-amcv.html new file mode 100644 index 000000000..032b4bfc3 --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/07f-adobe-invalid-amcv.html @@ -0,0 +1,50 @@ +<%= header %> +<%= boomerangSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/16-third-party-analytics/07f-adobe-invalid-amcv.js b/tests/page-templates/16-third-party-analytics/07f-adobe-invalid-amcv.js new file mode 100644 index 000000000..f1a716fc8 --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/07f-adobe-invalid-amcv.js @@ -0,0 +1,48 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["Visitor", "s"]); + +describe("e2e/16-third-party-analytics/07f-adobe-invalid-amcv", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should be missing Adobe AID", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.aa.aid"], undefined); + }); + + it("Should have Adobe MID set (on a domain or in PhantomJS)", function() { + if (t.canSetCookies()) { + var b = tf.lastBeacon(); + + assert.equal(b["tp.aa.mid"], "MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"); + } + }); + + it("Should be missing Adobe MID (on localhost or an IP)", function() { + if (!t.canSetCookies()) { + var b = tf.lastBeacon(); + + assert.equal(b["tp.aa.mid"], undefined); + } + }); + + it("Should not have an app error on the beacon", function() { + var b = tf.lastBeacon(); + + assert.equal(b.err, undefined); + }); + + it("Should not have a boomerang error on the beacon", function() { + var b = tf.lastBeacon(); + + assert.equal(b.errors, undefined); + }); +}); diff --git a/tests/page-templates/16-third-party-analytics/08-adobe-campaign.html b/tests/page-templates/16-third-party-analytics/08-adobe-campaign.html new file mode 100644 index 000000000..3f18a64ea --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/08-adobe-campaign.html @@ -0,0 +1,21 @@ +<%= header %> +<%= boomerangSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/16-third-party-analytics/08-adobe-campaign.js b/tests/page-templates/16-third-party-analytics/08-adobe-campaign.js new file mode 100644 index 000000000..965af61e8 --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/08-adobe-campaign.js @@ -0,0 +1,20 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["s"]); + +describe("e2e/16-third-party-analytics/08-adobe-campaign", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have Adobe Campaign ID set", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.aa.campaign"], "campaignid"); + }); +}); diff --git a/tests/page-templates/16-third-party-analytics/08a-adobe-purchaseid.html b/tests/page-templates/16-third-party-analytics/08a-adobe-purchaseid.html new file mode 100644 index 000000000..940605110 --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/08a-adobe-purchaseid.html @@ -0,0 +1,21 @@ +<%= header %> +<%= boomerangSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/16-third-party-analytics/08a-adobe-purchaseid.js b/tests/page-templates/16-third-party-analytics/08a-adobe-purchaseid.js new file mode 100644 index 000000000..a446a2e5c --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/08a-adobe-purchaseid.js @@ -0,0 +1,20 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["s"]); + +describe("e2e/16-third-party-analytics/08a-adobe-purchaseid", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have Adobe Purchase ID set", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.aa.purchaseid"], "purchaseid"); + }); +}); diff --git a/tests/page-templates/16-third-party-analytics/09-adobe-drop-cid.html b/tests/page-templates/16-third-party-analytics/09-adobe-drop-cid.html new file mode 100644 index 000000000..d15756faf --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/09-adobe-drop-cid.html @@ -0,0 +1,50 @@ +<%= header %> +<%= boomerangSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/16-third-party-analytics/09-adobe-drop-cid.js b/tests/page-templates/16-third-party-analytics/09-adobe-drop-cid.js new file mode 100644 index 000000000..17374c120 --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/09-adobe-drop-cid.js @@ -0,0 +1,32 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["Visitor", "s"]); + +describe("e2e/16-third-party-analytics/09-adobe-drop-cid", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should not have Adobe AID set", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.aa.aid"], undefined); + }); + + it("Should not have Adobe MID set", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.aa.mid"], undefined); + }); + + it("Should have Adobe Campaign ID set", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.aa.campaign"], "campaignid"); + }); +}); diff --git a/tests/page-templates/16-third-party-analytics/10-google-clientid.html b/tests/page-templates/16-third-party-analytics/10-google-clientid.html new file mode 100644 index 000000000..026d889d1 --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/10-google-clientid.html @@ -0,0 +1,25 @@ +<%= header %> +<%= boomerangSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/16-third-party-analytics/10-google-clientid.js b/tests/page-templates/16-third-party-analytics/10-google-clientid.js new file mode 100644 index 000000000..db299ad9d --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/10-google-clientid.js @@ -0,0 +1,20 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["ga"]); + +describe("e2e/16-third-party-analytics/10-google-clientid", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have Google Analytics Client ID", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.ga.clientid"], "XXXXXXXXXX.YYYYYYYYYY"); + }); +}); diff --git a/tests/page-templates/16-third-party-analytics/11-google-invalid.html b/tests/page-templates/16-third-party-analytics/11-google-invalid.html new file mode 100644 index 000000000..56f02aebd --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/11-google-invalid.html @@ -0,0 +1,19 @@ +<%= header %> +<%= boomerangSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/16-third-party-analytics/11-google-invalid.js b/tests/page-templates/16-third-party-analytics/11-google-invalid.js new file mode 100644 index 000000000..a44d7bc96 --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/11-google-invalid.js @@ -0,0 +1,32 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["ga"]); + +describe("e2e/16-third-party-analytics/11-google-invalid", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should not have Google Analytics Client ID", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.ga.clientid"], undefined); + }); + + it("Should not have an app error on the beacon", function() { + var b = tf.lastBeacon(); + + assert.equal(b.err, undefined); + }); + + it("Should not have a boomerang error on the beacon", function() { + var b = tf.lastBeacon(); + + assert.equal(b.errors, undefined); + }); +}); diff --git a/tests/page-templates/16-third-party-analytics/12-google-campaign.html b/tests/page-templates/16-third-party-analytics/12-google-campaign.html new file mode 100644 index 000000000..f88ac1600 --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/12-google-campaign.html @@ -0,0 +1,21 @@ +<%= header %> +<%= boomerangSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/16-third-party-analytics/12-google-campaign.js b/tests/page-templates/16-third-party-analytics/12-google-campaign.js new file mode 100644 index 000000000..889ad4403 --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/12-google-campaign.js @@ -0,0 +1,41 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/16-third-party-analytics/12-google-campaign", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have Google campaign source set", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.ga.utm_source"], "source"); + }); + + it("Should have Google campaign medium set", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.ga.utm_medium"], "medium"); + }); + + it("Should have Google campaign term set", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.ga.utm_term"], "term"); + }); + + it("Should have Google campaign content set", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.ga.utm_content"], "content"); + }); + + it("Should have Google campaign campaign set", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.ga.utm_campaign"], "campaign"); + }); +}); diff --git a/tests/page-templates/16-third-party-analytics/13-google-drop-cid.html b/tests/page-templates/16-third-party-analytics/13-google-drop-cid.html new file mode 100644 index 000000000..f7b9af7d7 --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/13-google-drop-cid.html @@ -0,0 +1,30 @@ +<%= header %> +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/16-third-party-analytics/13-google-drop-cid.js b/tests/page-templates/16-third-party-analytics/13-google-drop-cid.js new file mode 100644 index 000000000..e77b01966 --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/13-google-drop-cid.js @@ -0,0 +1,50 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["ga"]); + +describe("e2e/16-third-party-analytics/13-google-drop-cid", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should not have Google Analytics Client ID", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.ga.clientid"], undefined); + }); + + it("Should have Google campaign source set", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.ga.utm_source"], "source"); + }); + + it("Should have Google campaign medium set", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.ga.utm_medium"], "medium"); + }); + + it("Should have Google campaign term set", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.ga.utm_term"], "term"); + }); + + it("Should have Google campaign content set", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.ga.utm_content"], "content"); + }); + + it("Should have Google campaign campaign set", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.ga.utm_campaign"], "campaign"); + }); +}); diff --git a/tests/page-templates/16-third-party-analytics/14-google-drop-params.html b/tests/page-templates/16-third-party-analytics/14-google-drop-params.html new file mode 100644 index 000000000..c771f8ca4 --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/14-google-drop-params.html @@ -0,0 +1,31 @@ +<%= header %> +<%= boomerangSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/16-third-party-analytics/14-google-drop-params.js b/tests/page-templates/16-third-party-analytics/14-google-drop-params.js new file mode 100644 index 000000000..6c55fa1a6 --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/14-google-drop-params.js @@ -0,0 +1,50 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["ga"]); + +describe("e2e/16-third-party-analytics/14-google-drop-params", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should not have Google Analytics Client ID", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.ga.clientid"], undefined); + }); + + it("Should have Google campaign source set", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.ga.utm_source"], "source"); + }); + + it("Should have Google campaign medium set", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.ga.utm_medium"], "medium"); + }); + + it("Should not have Google campaign term set", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.ga.utm_term"], undefined); + }); + + it("Should not have Google campaign content set", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.ga.utm_content"], undefined); + }); + + it("Should have Google campaign campaign set", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.ga.utm_campaign"], "campaign"); + }); +}); diff --git a/tests/page-templates/16-third-party-analytics/15-google-cookie-classic.html b/tests/page-templates/16-third-party-analytics/15-google-cookie-classic.html new file mode 100644 index 000000000..51288094d --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/15-google-cookie-classic.html @@ -0,0 +1,19 @@ +<%= header %> +<%= boomerangSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/16-third-party-analytics/15-google-cookie-classic.js b/tests/page-templates/16-third-party-analytics/15-google-cookie-classic.js new file mode 100644 index 000000000..9f117f55d --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/15-google-cookie-classic.js @@ -0,0 +1,17 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/16-third-party-analytics/15-google-cookie-classic", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have Google Analytics Client ID", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.ga.clientid"], "XXXXXXXXXX.YYYYYYYYYY"); + }); +}); diff --git a/tests/page-templates/16-third-party-analytics/16-google-cookie-universal.html b/tests/page-templates/16-third-party-analytics/16-google-cookie-universal.html new file mode 100644 index 000000000..e265e7a2b --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/16-google-cookie-universal.html @@ -0,0 +1,19 @@ +<%= header %> +<%= boomerangSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/16-third-party-analytics/16-google-cookie-universal.js b/tests/page-templates/16-third-party-analytics/16-google-cookie-universal.js new file mode 100644 index 000000000..425eb3d6b --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/16-google-cookie-universal.js @@ -0,0 +1,17 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/16-third-party-analytics/16-google-cookie-universal", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have Google Analytics Client ID", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.ga.clientid"], "XXXXXXXXXX.YYYYYYYYYY"); + }); +}); diff --git a/tests/page-templates/16-third-party-analytics/17-google-multitracker.html b/tests/page-templates/16-third-party-analytics/17-google-multitracker.html new file mode 100644 index 000000000..5f6823b3e --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/17-google-multitracker.html @@ -0,0 +1,36 @@ +<%= header %> +<%= boomerangSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/16-third-party-analytics/17-google-multitracker.js b/tests/page-templates/16-third-party-analytics/17-google-multitracker.js new file mode 100644 index 000000000..656704e74 --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/17-google-multitracker.js @@ -0,0 +1,20 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["FakeTracker", "ga"]); + +describe("e2e/16-third-party-analytics/17-google-multitracker", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have Google Analytics Client ID", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.ga.clientid"], "XXXXXXXXXX.YYYYYYYYYY"); + }); +}); diff --git a/tests/page-templates/16-third-party-analytics/18-google-getall-undef.html b/tests/page-templates/16-third-party-analytics/18-google-getall-undef.html new file mode 100644 index 000000000..508e3a36a --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/18-google-getall-undef.html @@ -0,0 +1,38 @@ +<%= header %> +<%= boomerangSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/16-third-party-analytics/18-google-getall-undef.js b/tests/page-templates/16-third-party-analytics/18-google-getall-undef.js new file mode 100644 index 000000000..daa40558e --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/18-google-getall-undef.js @@ -0,0 +1,20 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["FakeTracker", "ga"]); + +describe("e2e/16-third-party-analytics/18-google-getall-undef", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have Google Analytics Client ID", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.ga.clientid"], "XXXXXXXXXX.YYYYYYYYYY"); + }); +}); diff --git a/tests/page-templates/16-third-party-analytics/20-ibm-coreid.html b/tests/page-templates/16-third-party-analytics/20-ibm-coreid.html new file mode 100644 index 000000000..eb6f8cd40 --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/20-ibm-coreid.html @@ -0,0 +1,19 @@ +<%= header %> +<%= boomerangSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/16-third-party-analytics/20-ibm-coreid.js b/tests/page-templates/16-third-party-analytics/20-ibm-coreid.js new file mode 100644 index 000000000..b098f56dc --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/20-ibm-coreid.js @@ -0,0 +1,20 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["cmRetrieveUserID"]); + +describe("e2e/16-third-party-analytics/20-ibm-coreid", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have IBM Analytics Core ID", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.ia.coreid"], "80031460041999083951624"); + }); +}); diff --git a/tests/page-templates/16-third-party-analytics/21-ibm-invalid.html b/tests/page-templates/16-third-party-analytics/21-ibm-invalid.html new file mode 100644 index 000000000..d44df915e --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/21-ibm-invalid.html @@ -0,0 +1,19 @@ +<%= header %> +<%= boomerangSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/16-third-party-analytics/21-ibm-invalid.js b/tests/page-templates/16-third-party-analytics/21-ibm-invalid.js new file mode 100644 index 000000000..72467bf11 --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/21-ibm-invalid.js @@ -0,0 +1,32 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["cmRetrieveUserID"]); + +describe("e2e/16-third-party-analytics/21-ibm-invalid", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should not have IBM Analytics Core ID", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.cm.coreid"], undefined); + }); + + it("Should not have an app error on the beacon", function() { + var b = tf.lastBeacon(); + + assert.equal(b.err, undefined); + }); + + it("Should not have a boomerang error on the beacon", function() { + var b = tf.lastBeacon(); + + assert.equal(b.errors, undefined); + }); +}); diff --git a/tests/page-templates/16-third-party-analytics/22-ibm-campaign.html b/tests/page-templates/16-third-party-analytics/22-ibm-campaign.html new file mode 100644 index 000000000..90d42c28d --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/22-ibm-campaign.html @@ -0,0 +1,21 @@ +<%= header %> +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/16-third-party-analytics/22-ibm-campaign.js b/tests/page-templates/16-third-party-analytics/22-ibm-campaign.js new file mode 100644 index 000000000..51c7135ab --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/22-ibm-campaign.js @@ -0,0 +1,71 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/16-third-party-analytics/22-ibm-campaign", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have IBM campaign vendor set", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.ia.mmc_vendor"], "vendor"); + }); + + it("Should have IBM campaign category set", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.ia.mmc_category"], "category"); + }); + + it("Should have IBM campaign placement set", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.ia.mmc_placement"], "placement"); + }); + + it("Should have IBM campaign item set", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.ia.mmc_item"], "item"); + }); + + it("Should have IBM site promotion type set", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.ia.sp_type"], "fall"); + }); + + it("Should have IBM site promotion set", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.ia.sp_promotion"], "sale"); + }); + + it("Should have IBM site promotion link set", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.ia.sp_link"], "free shipping?cm_mmc=Organic_Social-_-Facebook-_-Placement-_-Item"); + }); + + it("Should have IBM real estate version set", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.ia.re_version"], "page A"); + }); + + it("Should have IBM real estate page area set", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.ia.re_pagearea"], "left navbar"); + }); + + it("Should have IBM real estate link set", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.ia.re_link"], "mens shirts"); + }); +}); diff --git a/tests/page-templates/16-third-party-analytics/23-ibm-drop-cid.html b/tests/page-templates/16-third-party-analytics/23-ibm-drop-cid.html new file mode 100644 index 000000000..255ddf7bf --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/23-ibm-drop-cid.html @@ -0,0 +1,25 @@ +<%= header %> +<%= boomerangSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/16-third-party-analytics/23-ibm-drop-cid.js b/tests/page-templates/16-third-party-analytics/23-ibm-drop-cid.js new file mode 100644 index 000000000..5d08c357c --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/23-ibm-drop-cid.js @@ -0,0 +1,80 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["cmRetrieveUserID"]); + +describe("e2e/16-third-party-analytics/23-ibm-drop-cid", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should not have IBM Analytics Core ID", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.ia.coreid"], undefined); + }); + + it("Should have IBM campaign vendor set", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.ia.mmc_vendor"], "vendor"); + }); + + it("Should have IBM campaign category set", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.ia.mmc_category"], "category"); + }); + + it("Should have IBM campaign placement set", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.ia.mmc_placement"], "placement"); + }); + + it("Should have IBM campaign item set", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.ia.mmc_item"], "item"); + }); + + it("Should have IBM site promotion type set", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.ia.sp_type"], "fall"); + }); + + it("Should have IBM site promotion set", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.ia.sp_promotion"], "sale"); + }); + + it("Should have IBM site promotion link set", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.ia.sp_link"], "free shipping?cm_mmc=Organic_Social-_-Facebook-_-Placement-_-Item"); + }); + + it("Should have IBM real estate version set", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.ia.re_version"], "page A"); + }); + + it("Should have IBM real estate page area set", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.ia.re_pagearea"], "left navbar"); + }); + + it("Should have IBM real estate link set", function() { + var b = tf.lastBeacon(); + + assert.equal(b["tp.ia.re_link"], "mens shirts"); + }); +}); diff --git a/tests/page-templates/16-third-party-analytics/24-spa-xhr.html b/tests/page-templates/16-third-party-analytics/24-spa-xhr.html new file mode 100644 index 000000000..4bd0ac786 --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/24-spa-xhr.html @@ -0,0 +1,38 @@ +<%= header %> +<%= boomerangDelayedSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/16-third-party-analytics/24-spa-xhr.js b/tests/page-templates/16-third-party-analytics/24-spa-xhr.js new file mode 100644 index 000000000..981eae126 --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/24-spa-xhr.js @@ -0,0 +1,78 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["BOOMR_script_delay"]); + +describe("e2e/16-third-party-analytics/24-spa-xhr", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent 2 beacons", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 2); + }); + + describe("Beacon 1 (spa_hard)", function() { + var i = 0; + + it("Should be a spa_hard beacon", function() { + var b = tf.beacons[i]; + + assert.equal(b["http.initiator"], "spa_hard"); + }); + + it("Should have Google campaign source set", function() { + var b = tf.beacons[i]; + + assert.equal(b["tp.ga.utm_source"], "source"); + }); + + it("Should have Google campaign medium set", function() { + var b = tf.beacons[i]; + + assert.equal(b["tp.ga.utm_medium"], "medium"); + }); + + it("Should have Google campaign term set", function() { + var b = tf.beacons[i]; + + assert.equal(b["tp.ga.utm_term"], "term"); + }); + + it("Should have Google campaign content set", function() { + var b = tf.beacons[i]; + + assert.equal(b["tp.ga.utm_content"], "content"); + }); + + it("Should have Google campaign campaign set", function() { + var b = tf.beacons[i]; + + assert.equal(b["tp.ga.utm_campaign"], "campaign"); + }); + }); + + describe("Beacon 2 (xhr)", function() { + var i = 1; + + it("Should be a xhr beacon", function() { + var b = tf.beacons[i]; + + assert.equal(b["http.initiator"], "xhr"); + }); + + it("Should not have any TPAnalytics params set", function() { + var k, + b = tf.beacons[i]; + + for (k in b) { + assert.isFalse(/^tp\./.test(b[k])); + } + }); + }); +}); diff --git a/tests/page-templates/16-third-party-analytics/tests.json5 b/tests/page-templates/16-third-party-analytics/tests.json5 new file mode 100644 index 000000000..94c4b8cff --- /dev/null +++ b/tests/page-templates/16-third-party-analytics/tests.json5 @@ -0,0 +1,5 @@ +{ + all: { + requires: ["third-party-analytics"] + } +} diff --git a/tests/page-templates/17-memory/00-dom-counts.html b/tests/page-templates/17-memory/00-dom-counts.html new file mode 100644 index 000000000..bcae2959d --- /dev/null +++ b/tests/page-templates/17-memory/00-dom-counts.html @@ -0,0 +1,21 @@ +<%= header %> +<%= boomerangSnippet %> + + + + + + + + + + + + + + +<%= footer %> diff --git a/tests/page-templates/17-memory/00-dom-counts.js b/tests/page-templates/17-memory/00-dom-counts.js new file mode 100644 index 000000000..ebeab1ce8 --- /dev/null +++ b/tests/page-templates/17-memory/00-dom-counts.js @@ -0,0 +1,200 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/17-memory/00-dom-counts", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a beacon", function() { + // ensure we fired a beacon ('beacon') + assert.isTrue(tf.fired_onbeacon); + }); + + it("Should have found 3 images", function() { + assert.equal(tf.lastBeacon()["dom.img"], 3); + }); + + it("Should have found 2 external images", function() { + assert.equal(tf.lastBeacon()["dom.img.ext"], 2); + }); + + it("Should have found 1 unique external images", function() { + assert.equal(tf.lastBeacon()["dom.img.uniq"], 1); + }); + + it("Should have found 12 scripts (when Snippet was in IFRAME mode)", function() { + if (!t.snippetWasLoadedIframe()) { + return this.skip(); + } + + assert.equal(tf.lastBeacon()["dom.script"], 12); + }); + + it("Should have found 13 scripts (when Snippet was in SCRIPT mode)", function() { + if (!t.snippetWasLoadedScript()) { + return this.skip(); + } + + assert.equal(tf.lastBeacon()["dom.script"], 13); + }); + + it("Should have found 13 scripts (when Snippet was in Preload mode)", function() { + if (!t.snippetWasLoadedPreload()) { + return this.skip(); + } + + assert.equal(tf.lastBeacon()["dom.script"], 13); + }); + + it("Should have found 7 external scripts (when Snippet was in IFRAME mode)", function() { + if (!t.snippetWasLoadedIframe()) { + return this.skip(); + } + + assert.equal(tf.lastBeacon()["dom.script.ext"], 7); + }); + + it("Should have found 8 external scripts (when Snippet was in SCRIPT mode)", function() { + if (!t.snippetWasLoadedScript()) { + return this.skip(); + } + + assert.equal(tf.lastBeacon()["dom.script.ext"], 8); + }); + + it("Should have found 8 external scripts (when Snippet was in Preload mode)", function() { + if (!t.snippetWasLoadedPreload()) { + return this.skip(); + } + + assert.equal(tf.lastBeacon()["dom.script.ext"], 8); + }); + + it("Should have found 6 unique external scripts (when Snippet was in IFRAME mode)", function() { + if (!t.snippetWasLoadedIframe()) { + return this.skip(); + } + + assert.equal(tf.lastBeacon()["dom.script.uniq"], 6); + }); + + it("Should have found 7 unique external scripts (when Snippet was in SCRIPT mode)", function() { + if (!t.snippetWasLoadedScript()) { + return this.skip(); + } + + assert.equal(tf.lastBeacon()["dom.script.uniq"], 7); + }); + + it("Should have found 7 unique external scripts (when Snippet was in Preload mode)", function() { + if (!t.snippetWasLoadedPreload()) { + return this.skip(); + } + + assert.equal(tf.lastBeacon()["dom.script.uniq"], 7); + }); + + it("Should have found 3 iframes (when Snippet was in IFRAME mode)", function() { + if (!t.snippetWasLoadedIframe()) { + return this.skip(); + } + + assert.equal(tf.lastBeacon()["dom.iframe"], 3); + }); + + it("Should have found 2 iframes (when Snippet was in SCRIPT mode)", function() { + if (!t.snippetWasLoadedScript()) { + return this.skip(); + } + + assert.equal(tf.lastBeacon()["dom.iframe"], 2); + }); + + it("Should have found 2 iframes (when Snippet was in Preload mode)", function() { + if (!t.snippetWasLoadedPreload()) { + return this.skip(); + } + + assert.equal(tf.lastBeacon()["dom.iframe"], 2); + }); + + it("Should have found 2 external iframes (when Snippet was in IFRAME mode)", function() { + if (!t.snippetWasLoadedIframe()) { + return this.skip(); + } + + assert.equal(tf.lastBeacon()["dom.iframe.ext"], 2); + }); + + it("Should have found 0 external iframes (when Snippet was in SCRIPT mode)", function() { + if (!t.snippetWasLoadedScript()) { + return this.skip(); + } + + assert.isUndefined(tf.lastBeacon()["dom.iframe.ext"]); + }); + + it("Should have found 0 external iframes (when Snippet was in Preload mode)", function() { + if (!t.snippetWasLoadedPreload()) { + return this.skip(); + } + + assert.isUndefined(tf.lastBeacon()["dom.iframe.ext"]); + }); + + it("Should have found 1 unique external iframes (when Snippet was in IFRAME mode)", function() { + if (!t.snippetWasLoadedIframe()) { + return this.skip(); + } + + assert.equal(tf.lastBeacon()["dom.iframe.uniq"], 1); + }); + + it("Should have found 1 unique external iframes (when Snippet was in SCRIPT mode)", function() { + if (!t.snippetWasLoadedScript()) { + return this.skip(); + } + + assert.equal(tf.lastBeacon()["dom.iframe.uniq"], 1); + }); + + it("Should have found 1 unique external iframes (when Snippet was in Preload mode)", function() { + if (!t.snippetWasLoadedPreload()) { + return this.skip(); + } + + assert.equal(tf.lastBeacon()["dom.iframe.uniq"], 1); + }); + + it("Should have found 3 links (when Snippet was in IFRAME mode)", function() { + if (!t.snippetWasLoadedIframe()) { + return this.skip(); + } + + assert.equal(tf.lastBeacon()["dom.link"], 3); + }); + + it("Should have found 3 links (when Snippet was in SCRIPT mode)", function() { + if (!t.snippetWasLoadedScript()) { + return this.skip(); + } + + assert.equal(tf.lastBeacon()["dom.link"], 3); + }); + + it("Should have found 4 links (when Snippet was in Preload mode)", function() { + if (!t.snippetWasLoadedPreload()) { + return this.skip(); + } + + assert.equal(tf.lastBeacon()["dom.link"], 4); + }); + + it("Should have found 2 stylessheets", function() { + assert.equal(tf.lastBeacon()["dom.link.css"], 2); + }); + + it("Should have found 1 unique stylesheets", function() { + assert.equal(tf.lastBeacon()["dom.link.css.uniq"], 1); + }); +}); diff --git a/tests/page-templates/17-memory/01-cookie-length.html b/tests/page-templates/17-memory/01-cookie-length.html new file mode 100644 index 000000000..4b9bd232e --- /dev/null +++ b/tests/page-templates/17-memory/01-cookie-length.html @@ -0,0 +1,14 @@ +<%= header %> +<%= boomerangSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/17-memory/01-cookie-length.js b/tests/page-templates/17-memory/01-cookie-length.js new file mode 100644 index 000000000..a1ddee1d7 --- /dev/null +++ b/tests/page-templates/17-memory/01-cookie-length.js @@ -0,0 +1,16 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/17-memory/01-cookie-length", function() { + var tf = BOOMR.plugins.TestFramework; + + it("Should have sent a beacon", function() { + // ensure we fired a beacon ('beacon') + assert.isTrue(tf.fired_onbeacon); + }); + + it("Should have cookie length over 37 (or 38 for HTTPS pages)", function() { + // Cookie length should include at least Foo and baz. Y will be included on HTTPS. RT might have data too. + assert.operator(parseInt(tf.lastBeacon()["dom.ck"], 10), ">=", "Foo=bar; baz=abcdefghijklmnopqrstuvwxyz; RT=\"\"".length); + }); +}); diff --git a/tests/page-templates/17-memory/02-localstorage.html b/tests/page-templates/17-memory/02-localstorage.html new file mode 100644 index 000000000..4d806ec82 --- /dev/null +++ b/tests/page-templates/17-memory/02-localstorage.html @@ -0,0 +1,27 @@ +<%= header %> +<%= boomerangSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/17-memory/02-localstorage.js b/tests/page-templates/17-memory/02-localstorage.js new file mode 100644 index 000000000..43c1038eb --- /dev/null +++ b/tests/page-templates/17-memory/02-localstorage.js @@ -0,0 +1,38 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["lst", "sst", "x", "y"]); + +describe("e2e/17-memory/02-localstorage", function() { + var tf = BOOMR.plugins.TestFramework; + + it("Should have sent a beacon", function() { + // ensure we fired a beacon ('beacon') + assert.isTrue(tf.fired_onbeacon); + }); + + it("Should have localstorage count of 2", function() { + if (window.localStorage) { + assert.equal(tf.lastBeacon()["mem.lsln"], 2); + } + }); + + it("Should have localstorage size of 48", function() { + if (window.localStorage) { + assert.equal(tf.lastBeacon()["mem.lssz"], 35 + 13); + } + }); + + it("Should have sessionstorage count of 3", function() { + if (window.sessionStorage) { + assert.equal(tf.lastBeacon()["mem.ssln"], 3); + } + }); + + it("Should have sessionstorage size of 41", function() { + if (window.sessionStorage) { + assert.equal(tf.lastBeacon()["mem.sssz"], 22 + 19); + } + }); +}); diff --git a/tests/page-templates/17-memory/03-hard-loop-arrayfilter-error-before-load.html b/tests/page-templates/17-memory/03-hard-loop-arrayfilter-error-before-load.html new file mode 100644 index 000000000..2430686e7 --- /dev/null +++ b/tests/page-templates/17-memory/03-hard-loop-arrayfilter-error-before-load.html @@ -0,0 +1,54 @@ +<%= header %> +<%= boomerangSnippet %> + + + + + + + + gg + + + + gg + + + + gg + + + + + + +<%= footer %> diff --git a/tests/page-templates/17-memory/03-hard-loop-arrayfilter-error-before-load.js b/tests/page-templates/17-memory/03-hard-loop-arrayfilter-error-before-load.js new file mode 100644 index 000000000..8f3bac6fc --- /dev/null +++ b/tests/page-templates/17-memory/03-hard-loop-arrayfilter-error-before-load.js @@ -0,0 +1,17 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/17-memory/03-hard-loop-arrayfilter-error-before-load", function() { + beforeEach(function() { + if (!BOOMR.plugins.Errors) { + return this.skip(); + } + }); + + var t = BOOMR_test; + + it("Should have sent 2 beacons", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 2); + }); +}); diff --git a/tests/page-templates/17-memory/05-device-memory.html b/tests/page-templates/17-memory/05-device-memory.html new file mode 100644 index 000000000..a2d9fed1d --- /dev/null +++ b/tests/page-templates/17-memory/05-device-memory.html @@ -0,0 +1,9 @@ +<%= header %> +<%= boomerangSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/17-memory/05-device-memory.js b/tests/page-templates/17-memory/05-device-memory.js new file mode 100644 index 000000000..f05b4edb3 --- /dev/null +++ b/tests/page-templates/17-memory/05-device-memory.js @@ -0,0 +1,22 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/17-memory/05-device-memory", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a beacon", function() { + // ensure we fired a beacon ('beacon') + assert.isTrue(tf.fired_onbeacon); + }); + + it("Should have added 'dev.mem' to the beacon", function() { + if (!t.isSecureConnection()) { + return this.skip(); + } + + var expected = window.navigator.deviceMemory; + + assert.equal(tf.lastBeacon()["dev.mem"], expected); + }); +}); diff --git a/tests/page-templates/17-memory/06-onunload.html b/tests/page-templates/17-memory/06-onunload.html new file mode 100644 index 000000000..e0a196e54 --- /dev/null +++ b/tests/page-templates/17-memory/06-onunload.html @@ -0,0 +1,18 @@ +<%= header %> +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/17-memory/06-onunload.js b/tests/page-templates/17-memory/06-onunload.js new file mode 100644 index 000000000..aa8bd1b1f --- /dev/null +++ b/tests/page-templates/17-memory/06-onunload.js @@ -0,0 +1,43 @@ +/* eslint-env mocha */ +/* global assert */ + +describe("e2e/17-memory/06-onunload", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + var UNLOAD_MEMORY_PARAMS = [ + "cpu.cnc", + "dom.ck", + "dom.doms", + "dom.iframe", + "dom.img", + "dom.link", + "dom.ln", + "dom.res", + "dom.script.ext", + "dom.script", + "dom.sz", + "mem.limit", + "mem.lsln", + "mem.lssz", + "mem.ssln", + "mem.sssz", + "mem.total", + "mem.used", + "scr.bpp", + "scr.dpx", + "scr.orn", + "scr.xy " + ]; + + it("Should have sent an unload beacon", function() { + assert.isDefined(tf.beacons[1]["rt.quit"]); + }); + + it("Should not have Memory data on the Unload beacon", function() { + for (var i = 0; i < UNLOAD_MEMORY_PARAMS.length; i++) { + assert.isUndefined(tf.beacons[1][UNLOAD_MEMORY_PARAMS[i]], + UNLOAD_MEMORY_PARAMS[i] + " should not be on the beacon"); + } + }); +}); diff --git a/tests/page-templates/17-memory/07-onnonpageload.html b/tests/page-templates/17-memory/07-onnonpageload.html new file mode 100644 index 000000000..a1f2ad254 --- /dev/null +++ b/tests/page-templates/17-memory/07-onnonpageload.html @@ -0,0 +1,18 @@ +<%= header %> +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/17-memory/07-onnonpageload.js b/tests/page-templates/17-memory/07-onnonpageload.js new file mode 100644 index 000000000..6522b8f88 --- /dev/null +++ b/tests/page-templates/17-memory/07-onnonpageload.js @@ -0,0 +1,21 @@ +/* eslint-env mocha */ +/* global assert */ + +describe("e2e/17-memory/07-onnonpageload", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent an unload beacon", function() { + assert.isDefined(tf.beacons[1]["rt.quit"]); + }); + + it("Should have DOM count data on unload", function() { + var b = tf.beacons[1]; + + assert.isDefined(b["dom.ln"]); + assert.isDefined(b["dom.img"]); + assert.isDefined(b["dom.script"]); + assert.isDefined(b["dom.iframe"]); + assert.isDefined(b["dom.link"]); + }); +}); diff --git a/tests/page-templates/17-memory/support/generic.html b/tests/page-templates/17-memory/support/generic.html new file mode 100644 index 000000000..5a8647259 --- /dev/null +++ b/tests/page-templates/17-memory/support/generic.html @@ -0,0 +1,38 @@ + + + + Generic Page w/ Boomerang + + + + + + + + + + + + + + +
    + + + diff --git a/tests/page-templates/17-memory/tests.json5 b/tests/page-templates/17-memory/tests.json5 new file mode 100644 index 000000000..b1ed7a37a --- /dev/null +++ b/tests/page-templates/17-memory/tests.json5 @@ -0,0 +1,11 @@ +{ + all: { + requires: ["memory"] + }, + "03-hard-loop-arrayfilter-error-before-load": { + requires: ["bw", "rt", "errors"] + }, + "04-hard-loop-arrayfilter-error-after-load": { + requires: ["bw", "rt", "errors"] + } +} diff --git a/tests/page-templates/18-usertiming/00-usertiming-none.html b/tests/page-templates/18-usertiming/00-usertiming-none.html new file mode 100644 index 000000000..a9bf3ebc2 --- /dev/null +++ b/tests/page-templates/18-usertiming/00-usertiming-none.html @@ -0,0 +1,15 @@ +<%= header %> + +<%= boomerangSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/18-usertiming/00-usertiming-none.js b/tests/page-templates/18-usertiming/00-usertiming-none.js new file mode 100644 index 000000000..cffed0b72 --- /dev/null +++ b/tests/page-templates/18-usertiming/00-usertiming-none.js @@ -0,0 +1,22 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["BOOMR_no_mark"]); + +describe("e2e/18-usertiming/00-usertiming-none", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should not have usertiming", function() { + if (t.isUserTimingSupported()) { + var b = tf.beacons[0]; + + assert.equal(b.usertiming, undefined); + } + }); +}); diff --git a/tests/page-templates/18-usertiming/01-usertiming-basic.html b/tests/page-templates/18-usertiming/01-usertiming-basic.html new file mode 100644 index 000000000..51c34a920 --- /dev/null +++ b/tests/page-templates/18-usertiming/01-usertiming-basic.html @@ -0,0 +1,27 @@ +<%= header %> + +<%= boomerangSnippet %> + + + + +<%= footer %> diff --git a/tests/page-templates/18-usertiming/01-usertiming-basic.js b/tests/page-templates/18-usertiming/01-usertiming-basic.js new file mode 100644 index 000000000..2572d1fb2 --- /dev/null +++ b/tests/page-templates/18-usertiming/01-usertiming-basic.js @@ -0,0 +1,33 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["BOOMR_no_mark", "UserTimingDecompression"]); + +describe("e2e/18-usertiming/01-usertiming-basic", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have usertiming (if UserTiming is supported)", function() { + if (t.isUserTimingSupported()) { + var b = tf.beacons[0]; + + assert.isString(b.usertiming); + var data = UserTimingDecompression.decompressUserTiming(b.usertiming); + var usertiming = {}; + + assert.equal(data.length, 3); + for (var i = 0; i < data.length; i++) { + usertiming[data[i].name] = data[i]; + } + + assert.isTrue("mark1" in usertiming); + assert.isTrue("mark2" in usertiming); + assert.isTrue("measure1" in usertiming); + } + }); +}); diff --git a/tests/page-templates/18-usertiming/02-usertiming-polyfill.html b/tests/page-templates/18-usertiming/02-usertiming-polyfill.html new file mode 100644 index 000000000..ed59eef3d --- /dev/null +++ b/tests/page-templates/18-usertiming/02-usertiming-polyfill.html @@ -0,0 +1,40 @@ +<%= header %> + +<%= boomerangSnippet %> + + + +
    +<%= footer %> diff --git a/tests/page-templates/18-usertiming/02-usertiming-polyfill.js b/tests/page-templates/18-usertiming/02-usertiming-polyfill.js new file mode 100644 index 000000000..6bb4266cc --- /dev/null +++ b/tests/page-templates/18-usertiming/02-usertiming-polyfill.js @@ -0,0 +1,34 @@ + +/* eslint-env mocha */ +/* global assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["BOOMR_no_mark", "UserTimingDecompression", "getEntriesByTypeCopy"]); + +describe("e2e/18-usertiming/02-usertiming-polyfill", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have usertiming (if UserTiming is supported)", function() { + if (t.isUserTimingSupported()) { + var b = tf.beacons[0]; + + assert.isString(b.usertiming); + var data = UserTimingDecompression.decompressUserTiming(b.usertiming); + var usertiming = {}; + + assert.equal(data.length, 3); + for (var i = 0; i < data.length; i++) { + usertiming[data[i].name] = data[i]; + } + + assert.isTrue("mark1" in usertiming); + assert.isTrue("mark2" in usertiming); + assert.isTrue("measure1" in usertiming); + } + }); +}); diff --git a/tests/page-templates/18-usertiming/03-usertiming-disabled.html b/tests/page-templates/18-usertiming/03-usertiming-disabled.html new file mode 100644 index 000000000..8602fd50e --- /dev/null +++ b/tests/page-templates/18-usertiming/03-usertiming-disabled.html @@ -0,0 +1,26 @@ +<%= header %> + +<%= boomerangSnippet %> + + + +<%= footer %> diff --git a/tests/page-templates/18-usertiming/03-usertiming-disabled.js b/tests/page-templates/18-usertiming/03-usertiming-disabled.js new file mode 100644 index 000000000..80d29bf59 --- /dev/null +++ b/tests/page-templates/18-usertiming/03-usertiming-disabled.js @@ -0,0 +1,22 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["BOOMR_no_mark"]); + +describe("e2e/18-usertiming/03-usertiming-disabled", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should not have usertiming", function() { + if (t.isUserTimingSupported()) { + var b = tf.beacons[0]; + + assert.equal(b.usertiming, undefined); + } + }); +}); diff --git a/tests/page-templates/18-usertiming/04-usertiming-second-beacon.html b/tests/page-templates/18-usertiming/04-usertiming-second-beacon.html new file mode 100644 index 000000000..1036d8da5 --- /dev/null +++ b/tests/page-templates/18-usertiming/04-usertiming-second-beacon.html @@ -0,0 +1,32 @@ +<%= header %> + +<%= boomerangSnippet %> + + + +<%= footer %> diff --git a/tests/page-templates/18-usertiming/04-usertiming-second-beacon.js b/tests/page-templates/18-usertiming/04-usertiming-second-beacon.js new file mode 100644 index 000000000..afcde9f4a --- /dev/null +++ b/tests/page-templates/18-usertiming/04-usertiming-second-beacon.js @@ -0,0 +1,60 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["BOOMR_no_mark", "UserTimingDecompression", "sendPostLoadMark"]); + +describe("e2e/18-usertiming/04-usertiming-second-beacon", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should receive user timing data for marks/measures set before load beacon only", function() { + if (t.isUserTimingSupported()) { + var b = tf.beacons[0]; + + assert.isString(b.usertiming); + + var data = UserTimingDecompression.decompressUserTiming(b.usertiming); + var usertiming = {}; + + for (var i = 0; i < data.length; i++) { + usertiming[data[i].name] = data[i]; + } + + assert.isTrue("pre-load-mark-start" in usertiming); + assert.isTrue("pre-load-mark-end" in usertiming); + assert.isTrue("pre-load-measure" in usertiming); + + assert.isFalse("post-load-mark-start" in usertiming); + assert.isFalse("post-load-mark-end" in usertiming); + assert.isFalse("post-load-measure" in usertiming); + } + }); + + it("Should receive user timing data for marks/measures set before post-load beacon only", function() { + if (t.isUserTimingSupported()) { + var b = tf.beacons[1]; + + assert.isString(b.usertiming); + + var data = UserTimingDecompression.decompressUserTiming(b.usertiming); + var usertiming = {}; + + for (var i = 0; i < data.length; i++) { + usertiming[data[i].name] = data[i]; + } + + assert.isTrue("post-load-mark-start" in usertiming); + assert.isTrue("post-load-mark-end" in usertiming); + assert.isTrue("post-load-measure" in usertiming); + + assert.isFalse("pre-load-mark-start" in usertiming); + assert.isFalse("pre-load-mark-end" in usertiming); + assert.isFalse("pre-load-measure" in usertiming); + } + }); +}); diff --git a/tests/page-templates/18-usertiming/05-usertiming-second-beacon-no-performance-now.html b/tests/page-templates/18-usertiming/05-usertiming-second-beacon-no-performance-now.html new file mode 100644 index 000000000..6dd4f3d26 --- /dev/null +++ b/tests/page-templates/18-usertiming/05-usertiming-second-beacon-no-performance-now.html @@ -0,0 +1,37 @@ +<%= header %> + +<%= boomerangSnippet %> + + + + +<%= footer %> diff --git a/tests/page-templates/18-usertiming/05-usertiming-second-beacon-no-performance-now.js b/tests/page-templates/18-usertiming/05-usertiming-second-beacon-no-performance-now.js new file mode 100644 index 000000000..1db5fed44 --- /dev/null +++ b/tests/page-templates/18-usertiming/05-usertiming-second-beacon-no-performance-now.js @@ -0,0 +1,60 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["BOOMR_no_mark", "UserTimingDecompression", "sendPostLoadMark"]); + +describe("e2e/18-usertiming/05-usertiming-second-beacon-no-performance-now", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should receive user timing data for marks/measures set before load beacon only", function() { + if (t.isUserTimingSupported()) { + var b = tf.beacons[0]; + + assert.isString(b.usertiming); + + var data = UserTimingDecompression.decompressUserTiming(b.usertiming); + var usertiming = {}; + + for (var i = 0; i < data.length; i++) { + usertiming[data[i].name] = data[i]; + } + + assert.isTrue("pre-load-mark-start" in usertiming); + assert.isTrue("pre-load-mark-end" in usertiming); + assert.isTrue("pre-load-measure" in usertiming); + + assert.isFalse("post-load-mark-start" in usertiming); + assert.isFalse("post-load-mark-end" in usertiming); + assert.isFalse("post-load-measure" in usertiming); + } + }); + + it("Should receive user timing data for marks/measures set before post-load beacon only", function() { + if (t.isUserTimingSupported()) { + var b = tf.beacons[1]; + + assert.isString(b.usertiming); + + var data = UserTimingDecompression.decompressUserTiming(b.usertiming); + var usertiming = {}; + + for (var i = 0; i < data.length; i++) { + usertiming[data[i].name] = data[i]; + } + + assert.isTrue("post-load-mark-start" in usertiming); + assert.isTrue("post-load-mark-end" in usertiming); + assert.isTrue("post-load-measure" in usertiming); + + assert.isFalse("pre-load-mark-start" in usertiming); + assert.isFalse("pre-load-mark-end" in usertiming); + assert.isFalse("pre-load-measure" in usertiming); + } + }); +}); diff --git a/tests/page-templates/18-usertiming/06-usertiming-no-compression.html b/tests/page-templates/18-usertiming/06-usertiming-no-compression.html new file mode 100644 index 000000000..be1c3818a --- /dev/null +++ b/tests/page-templates/18-usertiming/06-usertiming-no-compression.html @@ -0,0 +1,29 @@ +<%= header %> + +<%= boomerangScript %> + + + + +<%= footer %> diff --git a/tests/page-templates/18-usertiming/06-usertiming-no-compression.js b/tests/page-templates/18-usertiming/06-usertiming-no-compression.js new file mode 100644 index 000000000..a9d34ead2 --- /dev/null +++ b/tests/page-templates/18-usertiming/06-usertiming-no-compression.js @@ -0,0 +1,31 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["BOOMR_no_mark"]); + +describe("e2e/18-usertiming/06-usertiming-no-compression", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have usertiming (if UserTiming is supported)", function() { + var b, data, marks, measures; + + if (t.isUserTimingSupported()) { + b = tf.beacons[0]; + assert.isString(b.usertiming); + data = JSON.parse(b.usertiming); + assert.isTrue("mark" in data); + marks = data.mark; + assert.isTrue("mark1" in marks); + assert.isTrue("mark2" in marks); + assert.isTrue("measure" in data); + measures = data.measure; + assert.isTrue("measure1" in measures); + } + }); +}); diff --git a/tests/page-templates/18-usertiming/tests.json5 b/tests/page-templates/18-usertiming/tests.json5 new file mode 100644 index 000000000..1873a0cd3 --- /dev/null +++ b/tests/page-templates/18-usertiming/tests.json5 @@ -0,0 +1,5 @@ +{ + all: { + requires: ["usertiming"] + } +} diff --git a/tests/page-templates/19-navtiming/00-onload.html b/tests/page-templates/19-navtiming/00-onload.html new file mode 100644 index 000000000..6986941c6 --- /dev/null +++ b/tests/page-templates/19-navtiming/00-onload.html @@ -0,0 +1,15 @@ +<%= header %> + + + +<%= boomerangSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/19-navtiming/00-onload.js b/tests/page-templates/19-navtiming/00-onload.js new file mode 100644 index 000000000..891e5df34 --- /dev/null +++ b/tests/page-templates/19-navtiming/00-onload.js @@ -0,0 +1,252 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/19-navtiming/00-onload", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + var NT_PROPERTIES = [ + "nt_nav_st", + "nt_fet_st", + "nt_dns_st", + "nt_dns_end", + "nt_con_st", + "nt_con_end", + "nt_req_st", + "nt_res_st", + "nt_res_end", + "nt_domloading", + "nt_domint", + "nt_domcontloaded_st", + "nt_domcontloaded_end", + "nt_domcomp", + "nt_load_st", + "nt_load_end" + ]; + + var NT_PROPERTIES_OPTIONAL = [ + "nt_unload_st", + "nt_unload_end" + ]; + + it("Should have sent a beacon", function() { + // ensure we fired a beacon ('beacon') + assert.isTrue(tf.fired_onbeacon); + }); + + it("Should have fired 'beacon' with a beacon payload", function() { + // ensure the data was sent to 'beacon' + assert.isObject(tf.lastBeacon()); + }); + + it("Should have set basic beacon properties", function() { + assert.isString(tf.lastBeacon().v); + }); + + it("Should have set nt_* properties (if NavigationTiming is supported)", function() { + if (!t.isNavigationTimingSupported()) { + return this.skip(); + } + + for (var i = 0; i < NT_PROPERTIES.length; i++) { + assert.isNumber(tf.lastBeacon()[NT_PROPERTIES[i]], NT_PROPERTIES[i]); + } + + if (location.protocol === "https:") { + // SSL start might be 0 if the connection is re-used (so nt_ssl_st + // would be missing from the beacon) + var pt = window.performance.timing; + + if (pt.secureConnectionStart - pt.navigationStart >= 2) { + assert.isNumber(tf.lastBeacon().nt_ssl_st, "nt_ssl_st"); + } + } + }); + + it("Should have set set nt_* properties as Unix-epoch timestamps, not DOMHighResTimestamps (if NavigationTiming is supported)", function() { + if (!t.isNavigationTimingSupported()) { + return this.skip(); + } + + for (var i = 0; i < NT_PROPERTIES.length; i++) { + // Jan 1 2000 + assert.operator(parseInt(tf.lastBeacon()[NT_PROPERTIES[i]], 10), ">=", 946684800, NT_PROPERTIES[i]); + + // Jan 1 2050 + assert.operator(parseInt(tf.lastBeacon()[NT_PROPERTIES[i]], 10), "<=", 2524658358000, NT_PROPERTIES[i]); + + // make sure it's not decimal + assert.notInclude(tf.lastBeacon()[NT_PROPERTIES[i]], "."); + } + + for (var i = 0; i < NT_PROPERTIES_OPTIONAL.length; i++) { + if (typeof tf.lastBeacon()[NT_PROPERTIES_OPTIONAL[i]] !== "undefined") { + // Jan 1 2000 + assert.operator(parseInt(tf.lastBeacon()[NT_PROPERTIES_OPTIONAL[i]], 10), ">=", 946684800, NT_PROPERTIES_OPTIONAL[i]); + + // Jan 1 2050 + assert.operator(parseInt(tf.lastBeacon()[NT_PROPERTIES_OPTIONAL[i]], 10), "<=", 2524658358000, NT_PROPERTIES_OPTIONAL[i]); + + // make sure it's not decimal + assert.notInclude(tf.lastBeacon()[NT_PROPERTIES_OPTIONAL[i]], "."); + } + } + }); + + it("Should have set set nt_* properties as full numbers, not decimals (if NavigationTiming is supported)", function() { + if (!t.isNavigationTimingSupported()) { + return this.skip(); + } + + for (var i = 0; i < NT_PROPERTIES.length; i++) { + assert.notInclude(tf.lastBeacon()[NT_PROPERTIES[i]], ".", NT_PROPERTIES[i]); + } + + for (var i = 0; i < NT_PROPERTIES_OPTIONAL.length; i++) { + if (typeof tf.lastBeacon()[NT_PROPERTIES_OPTIONAL[i]] !== "undefined") { + assert.notInclude(tf.lastBeacon()[NT_PROPERTIES_OPTIONAL[i]], ".", NT_PROPERTIES_OPTIONAL[i]); + } + } + }); + + it("Should have set Chrome nt_spdy (if the browser is Chrome, and if NavigationTiming2 w/ nextHopProtocol isn't supported)", function() { + if (!t.isChromeLoadTimesSupported() || t.isNavigationTiming2WithNextHopProtocolSupported()) { + return this.skip(); + } + + assert.isNumber(tf.lastBeacon().nt_spdy, "nt_spdy"); + }); + + it("Should not have set Chrome nt_spdy (if the browser is Chrome, and if NavigationTiming2 w/ nextHopProtocol is supported)", function() { + if (!t.isChromeLoadTimesSupported() || !t.isNavigationTiming2WithNextHopProtocolSupported()) { + return this.skip(); + } + + assert.isUndefined(tf.lastBeacon().nt_spdy); + }); + + it("Should have set Chrome nt_cinf (if the browser is Chrome, and if NavigationTiming2 w/ nextHopProtocol isn't supported)", function() { + if (!t.isChromeLoadTimesSupported() || t.isNavigationTiming2WithNextHopProtocolSupported()) { + return this.skip(); + } + + assert.isDefined(tf.lastBeacon().nt_cinf, "nt_cinf"); + }); + + it("Should not have set Chrome nt_cinf (if the browser is Chrome, and if NavigationTiming2 w/ nextHopProtocol is supported)", function() { + if (!t.isChromeLoadTimesSupported() || !t.isNavigationTiming2WithNextHopProtocolSupported()) { + return this.skip(); + } + + assert.isUndefined(tf.lastBeacon().nt_cinf); + }); + + it("Should have set Chrome nt_first_paint via Chrome loadTimes (if the browser is Chrome, and PaintTiming is not supported)", function() { + if (!t.isChromeLoadTimesSupported() || t.isPaintTimingSupported()) { + return this.skip(); + } + + var pt; + + if (window.chrome && window.chrome.loadTimes) { + pt = window.chrome.loadTimes(); + } + + // validation of firstPaintTime + assert.isNumber(tf.lastBeacon().nt_first_paint, "nt_first_paint"); + assert.operator(parseInt(tf.lastBeacon().nt_first_paint, 10), ">=", parseInt(tf.lastBeacon().nt_nav_st, 10)); + assert.equal(tf.lastBeacon().nt_first_paint, Math.round(pt.firstPaintTime * 1000)); + }); + + it("Should have set nt_first_paint via msFirstPaint (if IE)", function() { + var p = BOOMR.getPerformance(); + + if (!p || !p.timing || !p.timing.msFirstPaint) { + // NT first paint not supported + return this.skip(); + } + + assert.isNumber(tf.lastBeacon().nt_first_paint, "nt_first_paint"); + assert.operator(parseInt(tf.lastBeacon().nt_first_paint, 10), ">=", parseInt(tf.lastBeacon().nt_nav_st, 10)); + assert.equal(tf.lastBeacon().nt_first_paint, p.timing.msFirstPaint); + }); + + it("Should have set nt_first_paint via PaintTiming (if PaintTiming is supported and happened by load)", function() { + if (!t.isPaintTimingSupported() || !BOOMR.plugins.PaintTiming) { + return this.skip(); + } + + var pt = BOOMR.utils.arrayFind(performance.getEntriesByType("paint"), function(entry) { + return entry.name === "first-paint"; + }); + + // skip if it happened after onload + if (!pt || pt.startTime > parseInt(tf.lastBeacon().t_done, 10)) { + return this.skip(); + } + + // validation of firstPaint + assert.isNumber(tf.lastBeacon().nt_first_paint, "nt_first_paint"); + assert.operator(parseInt(tf.lastBeacon().nt_first_paint, 10), ">=", parseInt(tf.lastBeacon().nt_nav_st, 10)); + assert.equal(tf.lastBeacon().nt_first_paint, Math.floor(pt.startTime + performance.timing.navigationStart)); + }); + + it("Should have set NT2 properties (if NavigationTiming2 is supported)", function() { + var pt, + p = BOOMR.getPerformance(); + + if (!p || typeof p.getEntriesByType !== "function") { + // NT2 not supported + return this.skip(); + } + + pt = p.getEntriesByType("navigation"); + + if (!pt || !pt.length) { + // NT2 not supported + return this.skip(); + } + + pt = pt[0]; + + if (pt.workerStart) { + assert.isNumber(tf.lastBeacon().nt_worker_start, "nt_worker_start"); + } + + if (pt.decodedBodySize || pt.transferSize) { + assert.isNumber(tf.lastBeacon().nt_enc_size, "nt_enc_size"); + assert.isNumber(tf.lastBeacon().nt_dec_size, "nt_dec_size"); + assert.isNumber(tf.lastBeacon().nt_trn_size, "nt_trn_size"); + } + + if (pt.nextHopProtocol) { + assert.isDefined(tf.lastBeacon().nt_protocol, "nt_protocol"); + assert.equal(tf.lastBeacon().nt_protocol, pt.nextHopProtocol, "nt_protocol"); + } + }); + + it("Should have set nt_nav_type property (if NavigationTiming is supported)", function() { + if (!t.isNavigationTimingSupported()) { + return this.skip(); + } + + assert.isNumber(tf.lastBeacon().nt_nav_type, "nt_nav_type"); + }); + + it("Should have set nt_red_cnt property to 0 (if NavigationTiming is supported)", function() { + if (!t.isNavigationTimingSupported()) { + return this.skip(); + } + + assert.equal(tf.lastBeacon().nt_red_cnt, 0); + }); + + it("Should not have set redirect timing properties (if NavigationTiming is supported)", function() { + if (!t.isNavigationTimingSupported()) { + return this.skip(); + } + + assert.isUndefined(tf.lastBeacon().nt_red_st, "nt_red_st"); + assert.isUndefined(tf.lastBeacon().nt_red_end, "nt_red_end"); + }); +}); diff --git a/tests/page-templates/19-navtiming/01-onload-busy.html b/tests/page-templates/19-navtiming/01-onload-busy.html new file mode 100644 index 000000000..09f83e7a6 --- /dev/null +++ b/tests/page-templates/19-navtiming/01-onload-busy.html @@ -0,0 +1,20 @@ +<%= header %> + +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/19-navtiming/01-onload-busy.js b/tests/page-templates/19-navtiming/01-onload-busy.js new file mode 100644 index 000000000..82a1b906f --- /dev/null +++ b/tests/page-templates/19-navtiming/01-onload-busy.js @@ -0,0 +1,51 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/19-navtiming/01-onload-busy", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have Front-End time (t_page) on the beacon (if NavigationTiming is supported)", function() { + if (!t.isNavigationTimingSupported()) { + return this.skip(); + } + + assert.isNumber(tf.lastBeacon().t_page); + }); + + it("Should have Front-End time (t_page) >= 1500ms (if NavigationTiming is supported)", function() { + if (!t.isNavigationTimingSupported()) { + return this.skip(); + } + + assert.operator(tf.lastBeacon().t_page, ">=", 1500); + }); + + it("Should have Back-End time (t_resp) on the beacon (if NavigationTiming is supported)", function() { + if (!t.isNavigationTimingSupported()) { + return this.skip(); + } + + assert.isNumber(tf.lastBeacon().t_resp); + }); + + it("Should have Back-End time + Front-End time equal Page Load time (t_resp + t_page = t_done) on the beacon (if NavigationTiming is supported)", function() { + if (!t.isNavigationTimingSupported()) { + return this.skip(); + } + + var fe = parseInt(tf.lastBeacon().t_page, 10); + var be = parseInt(tf.lastBeacon().t_resp, 10); + var tt = parseInt(tf.lastBeacon().t_done, 10); + + assert.equal(fe + be, tt); + }); + + it("Should have Page Load time (t_done) >= 1500ms (if NavigationTiming is supported)", function() { + if (!t.isNavigationTimingSupported()) { + return this.skip(); + } + + assert.operator(tf.lastBeacon().t_done, ">=", 1500); + }); +}); diff --git a/tests/page-templates/19-navtiming/02-front-end-back-end.html b/tests/page-templates/19-navtiming/02-front-end-back-end.html new file mode 100644 index 000000000..c701aa115 --- /dev/null +++ b/tests/page-templates/19-navtiming/02-front-end-back-end.html @@ -0,0 +1,9 @@ +<%= header %> +<%= boomerangSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/19-navtiming/02-front-end-back-end.js b/tests/page-templates/19-navtiming/02-front-end-back-end.js new file mode 100644 index 000000000..c2cdf8b73 --- /dev/null +++ b/tests/page-templates/19-navtiming/02-front-end-back-end.js @@ -0,0 +1,51 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/19-navtiming/02-front-end-back-end", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have Front-End time (t_page) on the beacon (if NavigationTiming is supported)", function() { + if (!t.isNavigationTimingSupported()) { + return this.skip(); + } + + assert.isNumber(tf.lastBeacon().t_page); + }); + + it("Should not have Front-End time (t_page) on the beacon (if NavigationTiming is not supported)", function() { + if (t.isNavigationTimingSupported()) { + return this.skip(); + } + + assert.isUndefined(tf.lastBeacon().t_page); + }); + + it("Should have Back-End time (t_resp) on the beacon (if NavigationTiming is supported)", function() { + if (!t.isNavigationTimingSupported()) { + return this.skip(); + } + + assert.isNumber(tf.lastBeacon().t_resp); + }); + + it("Should not have Back-End time (t_resp) on the beacon (if NavigationTiming is not supported)", function() { + if (t.isNavigationTimingSupported()) { + return this.skip(); + } + + assert.isUndefined(tf.lastBeacon().t_resp); + }); + + it("Should have Back-End time + Front-End time equal Page Load time (t_resp + t_page = t_done) on the beacon (if NavigationTiming is supported)", function() { + if (!t.isNavigationTimingSupported()) { + return this.skip(); + } + + var fe = parseInt(tf.lastBeacon().t_page, 10); + var be = parseInt(tf.lastBeacon().t_resp, 10); + var tt = parseInt(tf.lastBeacon().t_done, 10); + + assert.equal(fe + be, tt); + }); +}); diff --git a/tests/page-templates/19-navtiming/03-onload-busy-loadedlate.html b/tests/page-templates/19-navtiming/03-onload-busy-loadedlate.html new file mode 100644 index 000000000..398996bc8 --- /dev/null +++ b/tests/page-templates/19-navtiming/03-onload-busy-loadedlate.html @@ -0,0 +1,20 @@ +<%= header %> + + + +<%= boomerangDelayedSnippet %> +<%= footer %> diff --git a/tests/page-templates/19-navtiming/03-onload-busy-loadedlate.js b/tests/page-templates/19-navtiming/03-onload-busy-loadedlate.js new file mode 100644 index 000000000..eb371a007 --- /dev/null +++ b/tests/page-templates/19-navtiming/03-onload-busy-loadedlate.js @@ -0,0 +1,54 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["BOOMR_script_delay"]); + +describe("e2e/19-navtiming/03-onload-busy-loadedlate", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have Front-End time (t_page) on the beacon (if NavigationTiming is supported)", function() { + if (!t.isNavigationTimingSupported()) { + return this.skip(); + } + + assert.isNumber(tf.lastBeacon().t_page); + }); + + it("Should have Front-End time (t_page) >= 1500ms (if NavigationTiming is supported)", function() { + if (!t.isNavigationTimingSupported()) { + return this.skip(); + } + + assert.operator(tf.lastBeacon().t_page, ">=", 1500); + }); + + it("Should have Back-End time (t_resp) on the beacon (if NavigationTiming is supported)", function() { + if (!t.isNavigationTimingSupported()) { + return this.skip(); + } + + assert.isNumber(tf.lastBeacon().t_resp); + }); + + it("Should have Back-End time + Front-End time equal Page Load time (t_resp + t_page = t_done) on the beacon (if NavigationTiming is supported)", function() { + if (!t.isNavigationTimingSupported()) { + return this.skip(); + } + + var fe = parseInt(tf.lastBeacon().t_page, 10); + var be = parseInt(tf.lastBeacon().t_resp, 10); + var tt = parseInt(tf.lastBeacon().t_done, 10); + + assert.equal(fe + be, tt); + }); + + it("Should have Page Load time (t_done) >= 1500ms (if NavigationTiming is supported)", function() { + if (!t.isNavigationTimingSupported()) { + return this.skip(); + } + + assert.operator(tf.lastBeacon().t_done, ">=", 1500); + }); +}); diff --git a/tests/page-templates/19-navtiming/04-nexthopprotocol-blank.html b/tests/page-templates/19-navtiming/04-nexthopprotocol-blank.html new file mode 100644 index 000000000..9f26c8e4a --- /dev/null +++ b/tests/page-templates/19-navtiming/04-nexthopprotocol-blank.html @@ -0,0 +1,50 @@ +<%= header %> + + +<%= boomerangScript %> + + +<%= footer %> \ No newline at end of file diff --git a/tests/page-templates/19-navtiming/04-nexthopprotocol-blank.js b/tests/page-templates/19-navtiming/04-nexthopprotocol-blank.js new file mode 100644 index 000000000..78d420f5e --- /dev/null +++ b/tests/page-templates/19-navtiming/04-nexthopprotocol-blank.js @@ -0,0 +1,41 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["loadTimesCalled"]); + +describe("e2e/19-navtiming/04-nexthopprotocol-blank", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a beacon", function() { + // ensure we fired a beacon ('beacon') + assert.isTrue(tf.fired_onbeacon); + }); + + it("Should have fired 'beacon' with a beacon payload", function() { + // ensure the data was sent to 'beacon' + assert.isObject(tf.lastBeacon()); + }); + + it("Should have set basic beacon properties", function() { + assert.isString(tf.lastBeacon().v); + }); + + it("Should not have set nt_protocol", function() { + if (!t.isNavigationTimingSupported()) { + return this.skip(); + } + + assert.isUndefined(tf.lastBeacon().nt_protocol, "nt_protocol"); + }); + + it("Should not have set chromeTimes if nt_protocol is not set", function() { + if (!t.isNavigationTimingSupported()) { + return this.skip(); + } + + assert.isFalse(loadTimesCalled, "chromeTimes"); + }); +}); + diff --git a/tests/page-templates/19-navtiming/04-no-resend-after-complete.html b/tests/page-templates/19-navtiming/04-no-resend-after-complete.html new file mode 100644 index 000000000..04de452a7 --- /dev/null +++ b/tests/page-templates/19-navtiming/04-no-resend-after-complete.html @@ -0,0 +1,29 @@ +<%= header %> +<%= boomerangScript %> + + + + +<%= footer %> diff --git a/tests/page-templates/19-navtiming/04-no-resend-after-complete.js b/tests/page-templates/19-navtiming/04-no-resend-after-complete.js new file mode 100644 index 000000000..40213d5a6 --- /dev/null +++ b/tests/page-templates/19-navtiming/04-no-resend-after-complete.js @@ -0,0 +1,19 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/19-navtiming/04-no-resend-after-complete", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should only send navigation timing information on second beacon (if NavigationTiming is supported)", function() { + var b1 = tf.beacons[0]; + + assert.isFalse("nt_load_end" in b1); + var b2 = tf.beacons[1]; + + assert.isTrue("nt_load_end" in b2); + var b3 = tf.beacons[2]; + + assert.isFalse("nt_load_end" in b3); + }); +}); diff --git a/tests/page-templates/19-navtiming/05-no-paint-yet.html b/tests/page-templates/19-navtiming/05-no-paint-yet.html new file mode 100644 index 000000000..e74abafd5 --- /dev/null +++ b/tests/page-templates/19-navtiming/05-no-paint-yet.html @@ -0,0 +1,41 @@ +<%= header %> +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/19-navtiming/05-no-paint-yet.js b/tests/page-templates/19-navtiming/05-no-paint-yet.js new file mode 100644 index 000000000..76024bbbd --- /dev/null +++ b/tests/page-templates/19-navtiming/05-no-paint-yet.js @@ -0,0 +1,19 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["loadTimesCalled"]); + +describe("e2e/19-navtiming/05-no-paint-yet", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a beacon", function() { + // ensure we fired a beacon ('beacon') + assert.isTrue(tf.fired_onbeacon); + }); + + it("Should not have called chrome.loadTimes() even if no paint has happened yet", function() { + assert.isFalse(window.loadTimesCalled, "chrome.loadTimes() should not be called"); + }); +}); diff --git a/tests/page-templates/19-navtiming/06-prerendered.html b/tests/page-templates/19-navtiming/06-prerendered.html new file mode 100644 index 000000000..8c0d4c251 --- /dev/null +++ b/tests/page-templates/19-navtiming/06-prerendered.html @@ -0,0 +1,43 @@ +<%= header %> +

    06-prerendered

    + + + +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/19-navtiming/06-prerendered.js b/tests/page-templates/19-navtiming/06-prerendered.js new file mode 100644 index 000000000..d7616b039 --- /dev/null +++ b/tests/page-templates/19-navtiming/06-prerendered.js @@ -0,0 +1,27 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/19-navtiming/06-prerendered", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a beacon", function() { + // ensure we fired a beacon ('beacon') + assert.isTrue(tf.fired_onbeacon); + }); + + it("Should not have sent a beacon while prerendering", function() { + assert.isUndefined(BOOMR_test.sentWhilePrerendering); + }); + + it("Should have set nt_act_st on the beacon", function() { + if (!t.isNavigationTiming2Supported() || !t.isPrerenderingSupported()) { + return this.skip(); + } + + var navSt = parseInt(tf.lastBeacon().nt_nav_st, 10); + var actSt = parseInt(tf.lastBeacon().nt_act_st, 10); + + assert.equal(actSt - navSt, BOOMR_test.fakeActivationStartOffset); + }); +}); diff --git a/tests/page-templates/19-navtiming/tests.json5 b/tests/page-templates/19-navtiming/tests.json5 new file mode 100644 index 000000000..5fa5b7ef2 --- /dev/null +++ b/tests/page-templates/19-navtiming/tests.json5 @@ -0,0 +1,5 @@ +{ + all: { + requires: ["navtiming"] + } +} diff --git a/tests/page-templates/20-painttiming/00-onload.html b/tests/page-templates/20-painttiming/00-onload.html new file mode 100644 index 000000000..b0eac6985 --- /dev/null +++ b/tests/page-templates/20-painttiming/00-onload.html @@ -0,0 +1,14 @@ +<%= header %> + + +<%= boomerangSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/20-painttiming/00-onload.js b/tests/page-templates/20-painttiming/00-onload.js new file mode 100644 index 000000000..51a76f623 --- /dev/null +++ b/tests/page-templates/20-painttiming/00-onload.js @@ -0,0 +1,60 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/20-painttiming/00-onload", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a beacon", function() { + // ensure we fired a beacon ('beacon') + assert.isTrue(tf.fired_onbeacon); + }); + + it("Should have set pt.fp (if PaintTiming is supported and happened by load)", function() { + if (!t.isPaintTimingSupported()) { + return this.skip(); + } + + var pt = BOOMR.utils.arrayFind(performance.getEntriesByType("paint"), function(entry) { + return entry.name === "first-paint"; + }); + + if (!pt || pt.startTime > parseInt(tf.lastBeacon().t_done, 10)) { + // might happen if there haven't been any paints by beacon, like if it + // loaded in the background + return this.skip(); + } + + // validation of First Paint + assert.isNumber(tf.lastBeacon()["pt.fp"]); + assert.operator(parseInt(tf.lastBeacon()["pt.fp"], 10), ">=", 0); + assert.equal(tf.lastBeacon()["pt.fp"], Math.floor(pt.startTime)); + }); + + it("Should have set pt.fcp (if PaintTiming is supported and happened by load)", function() { + if (!t.isPaintTimingSupported()) { + return this.skip(); + } + + var pt = BOOMR.utils.arrayFind(performance.getEntriesByType("paint"), function(entry) { + return entry.name === "first-contentful-paint"; + }); + + if (!pt || pt.startTime > parseInt(tf.lastBeacon().t_done, 10)) { + // might happen if there haven't been any paints by beacon, like if it + // loaded in the background + return this.skip(); + } + + // validation of First Contentful Paint + assert.isNumber(tf.lastBeacon()["pt.fcp"]); + assert.operator(parseInt(tf.lastBeacon()["pt.fcp"], 10), ">=", 0); + + if (tf.lastBeacon()["pt.fp"]) { + // FF has fcp but no fp + assert.operator(parseInt(tf.lastBeacon()["pt.fcp"], 10), ">=", parseInt(tf.lastBeacon()["pt.fp"], 10)); + } + + assert.equal(tf.lastBeacon()["pt.fcp"], Math.floor(pt.startTime)); + }); +}); diff --git a/tests/page-templates/20-painttiming/01-hidden-onload.html b/tests/page-templates/20-painttiming/01-hidden-onload.html new file mode 100644 index 000000000..0f38f2bf1 --- /dev/null +++ b/tests/page-templates/20-painttiming/01-hidden-onload.html @@ -0,0 +1,20 @@ +<%= header %> + + +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/20-painttiming/01-hidden-onload.js b/tests/page-templates/20-painttiming/01-hidden-onload.js new file mode 100644 index 000000000..efc39ba28 --- /dev/null +++ b/tests/page-templates/20-painttiming/01-hidden-onload.js @@ -0,0 +1,28 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/20-painttiming/01-hidden-onload", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a beacon", function() { + // ensure we fired a beacon ('beacon') + assert.isTrue(tf.fired_onbeacon); + }); + + it("Should not have set pt.fp", function() { + assert.isUndefined(tf.lastBeacon()["pt.fp"]); + }); + + it("Should not have set pt.fcp", function() { + assert.isUndefined(tf.lastBeacon()["pt.fcp"]); + }); + + it("Should have set pt.hid (if PaintTiming is supported)", function() { + if (!t.isPaintTimingSupported()) { + return this.skip(); + } + + assert.equal(tf.lastBeacon()["pt.hid"], 1); + }); +}); diff --git a/tests/page-templates/20-painttiming/02-lcp.html b/tests/page-templates/20-painttiming/02-lcp.html new file mode 100644 index 000000000..833e1f1a8 --- /dev/null +++ b/tests/page-templates/20-painttiming/02-lcp.html @@ -0,0 +1,17 @@ +<%= header %> + + +<%= boomerangSnippet %> + + + + + +<%= footer %> diff --git a/tests/page-templates/20-painttiming/02-lcp.js b/tests/page-templates/20-painttiming/02-lcp.js new file mode 100644 index 000000000..0c530824d --- /dev/null +++ b/tests/page-templates/20-painttiming/02-lcp.js @@ -0,0 +1,173 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/20-painttiming/02-lcp", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a beacon", function() { + // ensure we fired a beacon ('beacon') + assert.isTrue(tf.fired_onbeacon); + }); + + it("Should have set pt.lcp (if LargestContentfulPaint is supported and happened by load)", function(done) { + var observerWait, + that = this; + + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + if (typeof tf.lastBeacon()["pt.fp"] === "undefined") { + // No paint + return this.skip(); + } + + var observer = new window.PerformanceObserver(function(list) { + clearTimeout(observerWait); + + var entries = list.getEntries(); + + if (entries.length === 0) { + return that.skip(); + } + + var lcp = entries[entries.length - 1]; + var lcpTime = lcp.renderTime || lcp.loadTime; + + // validation of First Paint + assert.isNumber(tf.lastBeacon()["pt.lcp"]); + assert.operator(parseInt(tf.lastBeacon()["pt.lcp"], 10), ">=", 0); + assert.equal(tf.lastBeacon()["pt.lcp"], Math.floor(lcpTime)); + + observer.disconnect(); + + done(); + }); + + observer.observe({ type: "largest-contentful-paint", buffered: true }); + + // wait for the LCP observer to fire, if not, skip the test + observerWait = setTimeout(function() { + // no LCP before load + return this.skip(); + }.bind(this), 1000); + }); + + it("Should have exposed LCP metric (if LargestContentfulPaint is supported and happened by load)", function() { + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + if (typeof tf.lastBeacon()["pt.fp"] === "undefined") { + // No paint + return this.skip(); + } + + assert.equal(tf.lastBeacon()["pt.lcp"], BOOMR.plugins.PaintTiming.metrics.lcp()); + }); + + it("Should have exposed LCP metric src (pt.lcp.src) (if LargestContentfulPaint is supported and happened by load)", function() { + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + if (typeof tf.lastBeacon()["pt.fp"] === "undefined") { + // No paint + return this.skip(); + } + + assert.equal(tf.lastBeacon()["pt.lcp"], BOOMR.plugins.PaintTiming.metrics.lcp()); + + assert.isString(tf.lastBeacon()["pt.lcp.src"]); + + assert.equal(tf.lastBeacon()["pt.lcp.src"], BOOMR.plugins.PaintTiming.metrics.lcpSrc()); + assert.include(tf.lastBeacon()["pt.lcp.src"], "delay?delay=2000&file=/assets/img.jpg&id=2000"); + }); + + it("Should have exposed LCP metric element (pt.lcp.el) (if LargestContentfulPaint is supported and happened by load)", function() { + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + if (typeof tf.lastBeacon()["pt.fp"] === "undefined") { + // No paint + return this.skip(); + } + + assert.isString(tf.lastBeacon()["pt.lcp.el"]); + + assert.equal(tf.lastBeacon()["pt.lcp.el"], BOOMR.plugins.PaintTiming.metrics.lcpEl()); + assert.equal(tf.lastBeacon()["pt.lcp.el"], "IMG"); + }); + + it("Should not have exposed LCP metric ID (pt.lcp.id) (if LargestContentfulPaint is supported and happened by load)", function() { + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + if (typeof tf.lastBeacon()["pt.fp"] === "undefined") { + // No paint + return this.skip(); + } + + assert.notProperty(tf.lastBeacon(), "pt.lcp.id"); + }); + + it("Should have exposed LCP metric Pseudo-CSS Selector (pt.lcp.e) (if LargestContentfulPaint is supported and happened by load)", function() { + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + if (typeof tf.lastBeacon()["pt.fp"] === "undefined") { + // No paint + return this.skip(); + } + + assert.isString(tf.lastBeacon()["pt.lcp.e"]); + + assert.equal(tf.lastBeacon()["pt.lcp.e"], BOOMR.plugins.PaintTiming.metrics.lcpE()); + assert.equal(tf.lastBeacon()["pt.lcp.e"], "img"); + }); + + it("Should not have exposed LCP metric src-set (pt.lcp.srcset) (if LargestContentfulPaint is supported and happened by load)", function() { + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + if (typeof tf.lastBeacon()["pt.fp"] === "undefined") { + // No paint + return this.skip(); + } + + assert.notProperty(tf.lastBeacon(), "pt.lcp.srcset"); + }); + + it("Should not have exposed LCP metric sizes (pt.lcp.sizes) (if LargestContentfulPaint is supported and happened by load)", function() { + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + if (typeof tf.lastBeacon()["pt.fp"] === "undefined") { + // No paint + return this.skip(); + } + + assert.notProperty(tf.lastBeacon(), "pt.lcp.sizes"); + }); + + it("Should not have exposed LCP metric size (pt.lcp.s) (if LargestContentfulPaint is supported and happened by load)", function() { + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + if (typeof tf.lastBeacon()["pt.fp"] === "undefined") { + // No paint + return this.skip(); + } + + assert.isTrue(Number.isInteger(tf.lastBeacon()["pt.lcp.s"])); + + assert.equal(tf.lastBeacon()["pt.lcp.s"], BOOMR.plugins.PaintTiming.metrics.lcpS()); + }); +}); diff --git a/tests/page-templates/20-painttiming/03-lcp-src.html b/tests/page-templates/20-painttiming/03-lcp-src.html new file mode 100644 index 000000000..07f887668 --- /dev/null +++ b/tests/page-templates/20-painttiming/03-lcp-src.html @@ -0,0 +1,29 @@ +<%= header %> + + +<%= boomerangSnippet %> + +
    + +
    + + + + +<%= footer %> diff --git a/tests/page-templates/20-painttiming/03-lcp-src.js b/tests/page-templates/20-painttiming/03-lcp-src.js new file mode 100644 index 000000000..f836f45d2 --- /dev/null +++ b/tests/page-templates/20-painttiming/03-lcp-src.js @@ -0,0 +1,155 @@ +describe("e2e/20-painttiming/03-lcp-src", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent two beacons", function(done) { + t.ensureBeaconCount(done, 2); + }); + + describe("Beacon 1", function() { + it("Should have exposed LCP metric src (pt.lcp.src) (if LargestContentfulPaint is supported and happened by load)", function() { + var b = tf.beacons[0]; + + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + if (typeof b["pt.fp"] === "undefined") { + // No paint + return this.skip(); + } + + assert.equal(b["pt.lcp"], BOOMR.plugins.PaintTiming.metrics.lcp()); + + assert.isString(b["pt.lcp.src"]); + + assert.equal(b["pt.lcp.src"], BOOMR.plugins.PaintTiming.metrics.lcpSrc()); + assert.include(b["pt.lcp.src"], "delay?delay=2000&file=/assets/img.jpg&id=2000"); + }); + + it("Should have exposed LCP metric element (pt.lcp.el) (if LargestContentfulPaint is supported and happened by load)", function() { + var b = tf.beacons[0]; + + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + if (typeof b["pt.fp"] === "undefined") { + // No paint + return this.skip(); + } + + assert.isString(b["pt.lcp.el"]); + + assert.equal(b["pt.lcp.el"], BOOMR.plugins.PaintTiming.metrics.lcpEl()); + assert.equal(b["pt.lcp.el"], "IMG"); + }); + + it("Should have exposed LCP metric ID (pt.lcp.id) (if LargestContentfulPaint is supported and happened by load)", function() { + var b = tf.beacons[0]; + + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + if (typeof b["pt.fp"] === "undefined") { + // No paint + return this.skip(); + } + + assert.isString(b["pt.lcp.id"]); + + assert.equal(b["pt.lcp.id"], BOOMR.plugins.PaintTiming.metrics.lcpId()); + assert.equal(b["pt.lcp.id"], "lcp-id"); + }); + + it("Should have exposed LCP metric Pseudo-CSS Selector (pt.lcp.e) (if LargestContentfulPaint is supported and happened by load)", function() { + var b = tf.beacons[0]; + + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + if (typeof b["pt.fp"] === "undefined") { + // No paint + return this.skip(); + } + + assert.isString(b["pt.lcp.e"]); + + assert.equal(b["pt.lcp.e"], BOOMR.plugins.PaintTiming.metrics.lcpE()); + assert.equal(b["pt.lcp.e"], "img#lcp-id"); + }); + + it("Should have exposed LCP metric src-set (pt.lcp.srcset) (if LargestContentfulPaint is supported and happened by load)", function() { + var b = tf.beacons[0]; + + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + if (typeof b["pt.fp"] === "undefined") { + // No paint + return this.skip(); + } + + assert.isString(b["pt.lcp.srcset"]); + + assert.equal(b["pt.lcp.srcset"], BOOMR.plugins.PaintTiming.metrics.lcpSrcset()); + assert.equal(b["pt.lcp.srcset"], "/delay?delay=2000&file=/assets/img.jpg&id=2000 1w, /delay?delay=2000&file=/assets/img.jpg&id=2000 9999w"); + }); + + it("Should have exposed LCP metric sizes (pt.lcp.sizes) (if LargestContentfulPaint is supported and happened by load)", function() { + var b = tf.beacons[0]; + + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + if (typeof b["pt.fp"] === "undefined") { + // No paint + return this.skip(); + } + + assert.isString(b["pt.lcp.sizes"]); + + assert.equal(b["pt.lcp.sizes"], BOOMR.plugins.PaintTiming.metrics.lcpSizes()); + assert.equal(b["pt.lcp.sizes"], "(min-width: 1px) 2000px, 9999px"); + }); + + it("Should have exposed LCP metric size (pt.lcp.s) (if LargestContentfulPaint is supported and happened by load)", function() { + var b = tf.beacons[0]; + + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + if (typeof b["pt.fp"] === "undefined") { + // No paint + return this.skip(); + } + + assert.isTrue(Number.isInteger(b["pt.lcp.s"])); + + assert.equal(b["pt.lcp.s"], BOOMR.plugins.PaintTiming.metrics.lcpS()); + }); + }); + + describe("Beacon 2", function() { + it("Should not have exposed LCP attributes", function() { + var b = tf.beacons[1]; + + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + assert.isUndefined(b["pt.lcp.src"]); + assert.isUndefined(b["pt.lcp.el"]); + assert.isUndefined(b["pt.lcp.id"]); + assert.isUndefined(b["pt.lcp.e"]); + assert.isUndefined(b["pt.lcp.srcset"]); + assert.isUndefined(b["pt.lcp.sizes"]); + assert.isUndefined(b["pt.lcp.s"]); + }); + }); +}); diff --git a/tests/page-templates/20-painttiming/04-lcp-error.html b/tests/page-templates/20-painttiming/04-lcp-error.html new file mode 100644 index 000000000..fd83e477c --- /dev/null +++ b/tests/page-templates/20-painttiming/04-lcp-error.html @@ -0,0 +1,26 @@ +<%= header %> + + +<%= boomerangScript %> + + + + + +<%= footer %> diff --git a/tests/page-templates/20-painttiming/04-lcp-error.js b/tests/page-templates/20-painttiming/04-lcp-error.js new file mode 100644 index 000000000..f43424283 --- /dev/null +++ b/tests/page-templates/20-painttiming/04-lcp-error.js @@ -0,0 +1,201 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/20-painttiming/04-lcp-error", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + var C = BOOMR.utils.Compression; + + it("Should have sent two beacons", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 2); + }); + + describe("No LCP data on Error Beacon", function() { + it("Should have error data set on first beacon", function(){ + assert.notEqual(typeof tf.beacons[0].err, "undefined"); + }); + + it("Should not have LCP data on the error beacon", function() { + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + assert.equal(typeof tf.beacons[0]["pt.lcp"], "undefined"); + }); + + it("Should be a test error on the error beacon", function() { + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(tf.beacons[0].err))[0]; + + // message defined in html file + assert.equal(err.message, "ERROR!"); + }); + }); + + describe("LCP data on Pageload Beacon", function(){ + it("Should have only sent error information on the error beacon", function() { + assert.equal(typeof tf.lastBeacon().err, "undefined"); + }); + + it("Should have set pt.lcp (if LargestContentfulPaint is supported and happened by load)", function(done) { + var observerWait, + that = this; + + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + if (typeof tf.lastBeacon()["pt.fp"] === "undefined") { + // No paint + return this.skip(); + } + + var observer = new window.PerformanceObserver(function(list) { + clearTimeout(observerWait); + + var entries = list.getEntries(); + + if (entries.length === 0) { + return that.skip(); + } + + var lcp = entries[entries.length - 1]; + var lcpTime = lcp.renderTime || lcp.loadTime; + + // validation of First Paint + assert.isNumber(tf.lastBeacon()["pt.lcp"]); + assert.operator(parseInt(tf.lastBeacon()["pt.lcp"], 10), ">=", 0); + assert.equal(tf.lastBeacon()["pt.lcp"], Math.floor(lcpTime)); + + observer.disconnect(); + + done(); + }); + + observer.observe({ type: "largest-contentful-paint", buffered: true }); + + // wait for the LCP observer to fire, if not, skip the test + observerWait = setTimeout(function() { + // no LCP before load + return this.skip(); + }.bind(this), 3000); + }); + + it("Should have exposed LCP metric (if LargestContentfulPaint is supported and happened by load)", function() { + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + if (typeof tf.lastBeacon()["pt.fp"] === "undefined") { + // No paint + return this.skip(); + } + + assert.equal(tf.lastBeacon()["pt.lcp"], BOOMR.plugins.PaintTiming.metrics.lcp()); + }); + + it("Should have exposed LCP metric src (pt.lcp.src) (if LargestContentfulPaint is supported and happened by load)", function() { + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + if (typeof tf.lastBeacon()["pt.fp"] === "undefined") { + // No paint + return this.skip(); + } + + assert.equal(tf.lastBeacon()["pt.lcp"], BOOMR.plugins.PaintTiming.metrics.lcp()); + + assert.isString(tf.lastBeacon()["pt.lcp.src"]); + + assert.equal(tf.lastBeacon()["pt.lcp.src"], BOOMR.plugins.PaintTiming.metrics.lcpSrc()); + assert.include(tf.lastBeacon()["pt.lcp.src"], "delay?delay=2000&file=/assets/img.jpg&id=2000"); + }); + + it("Should have exposed LCP metric element (pt.lcp.el) (if LargestContentfulPaint is supported and happened by load)", function() { + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + if (typeof tf.lastBeacon()["pt.fp"] === "undefined") { + // No paint + return this.skip(); + } + + assert.isString(tf.lastBeacon()["pt.lcp.el"]); + + assert.equal(tf.lastBeacon()["pt.lcp.el"], BOOMR.plugins.PaintTiming.metrics.lcpEl()); + assert.equal(tf.lastBeacon()["pt.lcp.el"], "IMG"); + }); + + it("Should not have exposed LCP metric ID (pt.lcp.id) (if LargestContentfulPaint is supported and happened by load)", function() { + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + if (typeof tf.lastBeacon()["pt.fp"] === "undefined") { + // No paint + return this.skip(); + } + + assert.notProperty(tf.lastBeacon(), "pt.lcp.id"); + }); + + it("Should have exposed LCP metric Pseudo-CSS Selector (pt.lcp.e) (if LargestContentfulPaint is supported and happened by load)", function() { + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + if (typeof tf.lastBeacon()["pt.fp"] === "undefined") { + // No paint + return this.skip(); + } + + assert.isString(tf.lastBeacon()["pt.lcp.e"]); + + assert.equal(tf.lastBeacon()["pt.lcp.e"], BOOMR.plugins.PaintTiming.metrics.lcpE()); + assert.equal(tf.lastBeacon()["pt.lcp.e"], "img"); + }); + + it("Should not have exposed LCP metric src-set (pt.lcp.srcset) (if LargestContentfulPaint is supported and happened by load)", function() { + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + if (typeof tf.lastBeacon()["pt.fp"] === "undefined") { + // No paint + return this.skip(); + } + + assert.notProperty(tf.lastBeacon(), "pt.lcp.srcset"); + }); + + it("Should not have exposed LCP metric sizes (pt.lcp.sizes) (if LargestContentfulPaint is supported and happened by load)", function() { + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + if (typeof tf.lastBeacon()["pt.fp"] === "undefined") { + // No paint + return this.skip(); + } + + assert.notProperty(tf.lastBeacon(), "pt.lcp.sizes"); + }); + + it("Should have exposed LCP metric size (pt.lcp.s) (if LargestContentfulPaint is supported and happened by load)", function() { + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + if (typeof tf.lastBeacon()["pt.fp"] === "undefined") { + // No paint + return this.skip(); + } + + assert.isTrue(Number.isInteger(tf.lastBeacon()["pt.lcp.s"])); + + assert.equal(tf.lastBeacon()["pt.lcp.s"], BOOMR.plugins.PaintTiming.metrics.lcpS()); + }); + }); +}); diff --git a/tests/page-templates/20-painttiming/05-lcp-early.html b/tests/page-templates/20-painttiming/05-lcp-early.html new file mode 100644 index 000000000..cb118623d --- /dev/null +++ b/tests/page-templates/20-painttiming/05-lcp-early.html @@ -0,0 +1,66 @@ +<%= header %> + + + +<%= boomerangScript %> + + + + + +<%= footer %> diff --git a/tests/page-templates/20-painttiming/05-lcp-early.js b/tests/page-templates/20-painttiming/05-lcp-early.js new file mode 100644 index 000000000..ee675579c --- /dev/null +++ b/tests/page-templates/20-painttiming/05-lcp-early.js @@ -0,0 +1,122 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["LargestContentfulPaint", "POs", "curLoadTime", "generateLCP"]); + +describe("e2e/20-painttiming/05-lcp-early", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + var C = BOOMR.utils.Compression; + + it("Should have sent two beacons", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 2); + }); + + describe("Beacon 1: Early Beacon", function() { + it("Should have sent an early beacon", function(){ + assert.operator(parseInt(tf.beacons[0].early, 10), ">", 0); + }); + + it("Should have LCP data on the early beacon", function() { + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + var earlyBeacon = tf.beacons[0]; + + assert.isNumber(earlyBeacon["pt.lcp"]); + assert.operator(parseInt(tf.lastBeacon()["pt.lcp"], 10), ">", 0); + }); + }); + + describe("Beacon 2: Pageload Beacon", function(){ + it("Should have set pt.lcp (if LargestContentfulPaint is supported and happened by load)", function() { + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + assert.isNumber(tf.lastBeacon()["pt.lcp"]); + assert.operator(parseInt(tf.lastBeacon()["pt.lcp"], 10), ">=", 0); + assert.equal(tf.lastBeacon()["pt.lcp"], 220); + }); + + it("Should have exposed LCP metric (if LargestContentfulPaint is supported and happened by load)", function() { + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + assert.equal(tf.lastBeacon()["pt.lcp"], BOOMR.plugins.PaintTiming.metrics.lcp()); + }); + + it("Should have exposed LCP metric src (pt.lcp.src) (if LargestContentfulPaint is supported and happened by load)", function() { + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + assert.equal(tf.lastBeacon()["pt.lcp"], BOOMR.plugins.PaintTiming.metrics.lcp()); + + assert.isString(tf.lastBeacon()["pt.lcp.src"]); + + assert.equal(tf.lastBeacon()["pt.lcp.src"], BOOMR.plugins.PaintTiming.metrics.lcpSrc()); + assert.include(tf.lastBeacon()["pt.lcp.src"], "delay?delay=2000&file=/assets/img.jpg&id=2000"); + }); + + it("Should have exposed LCP metric element (pt.lcp.el) (if LargestContentfulPaint is supported and happened by load)", function() { + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + assert.isString(tf.lastBeacon()["pt.lcp.el"]); + + assert.equal(tf.lastBeacon()["pt.lcp.el"], BOOMR.plugins.PaintTiming.metrics.lcpEl()); + assert.equal(tf.lastBeacon()["pt.lcp.el"], "IMG"); + }); + + it("Should have exposed LCP metric ID (pt.lcp.id) (if LargestContentfulPaint is supported and happened by load)", function() { + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + assert.equal(tf.lastBeacon()["pt.lcp.id"], "img_id2"); + }); + + it("Should have exposed LCP metric Pseudo-CSS Selector (pt.lcp.e) (if LargestContentfulPaint is supported and happened by load)", function() { + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + assert.isString(tf.lastBeacon()["pt.lcp.e"]); + + assert.equal(tf.lastBeacon()["pt.lcp.e"], BOOMR.plugins.PaintTiming.metrics.lcpE()); + assert.equal(tf.lastBeacon()["pt.lcp.e"], "img#img_id2"); + }); + + it("Should not have exposed LCP metric src-set (pt.lcp.srcset) (if LargestContentfulPaint is supported and happened by load)", function() { + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + assert.notProperty(tf.lastBeacon(), "pt.lcp.srcset"); + }); + + it("Should not have exposed LCP metric sizes (pt.lcp.sizes) (if LargestContentfulPaint is supported and happened by load)", function() { + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + assert.notProperty(tf.lastBeacon(), "pt.lcp.sizes"); + }); + + it("Should have exposed LCP metric size (pt.lcp.s) (if LargestContentfulPaint is supported and happened by load)", function() { + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + assert.isTrue(Number.isInteger(tf.lastBeacon()["pt.lcp.s"])); + + assert.equal(tf.lastBeacon()["pt.lcp.s"], BOOMR.plugins.PaintTiming.metrics.lcpS()); + }); + }); +}); diff --git a/tests/page-templates/20-painttiming/06-lcp-error-after-pageload.html b/tests/page-templates/20-painttiming/06-lcp-error-after-pageload.html new file mode 100644 index 000000000..a7602eeb6 --- /dev/null +++ b/tests/page-templates/20-painttiming/06-lcp-error-after-pageload.html @@ -0,0 +1,30 @@ +<%= header %> + + +<%= boomerangScript %> + + + + +<%= footer %> diff --git a/tests/page-templates/20-painttiming/06-lcp-error-after-pageload.js b/tests/page-templates/20-painttiming/06-lcp-error-after-pageload.js new file mode 100644 index 000000000..a3295449e --- /dev/null +++ b/tests/page-templates/20-painttiming/06-lcp-error-after-pageload.js @@ -0,0 +1,204 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["errorFunction"]); + +describe("e2e/20-painttiming/06-lcp-error-after-pageload", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + var C = BOOMR.utils.Compression; + + it("Should have sent two beacons", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 2); + }); + + describe("No LCP data on Error Beacon", function() { + it("Should have error data set on second beacon", function(){ + assert.notEqual(typeof tf.lastBeacon().err, "undefined"); + }); + + it("Should not have LCP data on the error beacon", function() { + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + assert.equal(typeof tf.lastBeacon()["pt.lcp"], "undefined"); + }); + + it("Should be a test error on the error beacon", function() { + var err = BOOMR.plugins.Errors.decompressErrors(C.jsUrlDecompress(tf.lastBeacon().err))[0]; + + // message defined in html file + assert.equal(err.message, "ERROR!"); + }); + + it("Should have only sent error information on the error beacon", function() { + assert.equal(typeof tf.beacons[0].err, "undefined"); + }); + }); + + describe("LCP data on Pageload Beacon", function(){ + it("Should have set pt.lcp (if LargestContentfulPaint is supported and happened by load)", function(done) { + var observerWait, + that = this; + + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + if (typeof tf.beacons[0]["pt.fp"] === "undefined") { + // No paint + return this.skip(); + } + + var observer = new window.PerformanceObserver(function(list) { + clearTimeout(observerWait); + + var entries = list.getEntries(); + + if (entries.length === 0) { + return that.skip(); + } + + var lcp = entries[entries.length - 1]; + var lcpTime = lcp.renderTime || lcp.loadTime; + + // validation of First Paint + assert.isNumber(tf.beacons[0]["pt.lcp"]); + assert.operator(parseInt(tf.beacons[0]["pt.lcp"], 10), ">=", 0); + assert.equal(tf.beacons[0]["pt.lcp"], Math.floor(lcpTime)); + + observer.disconnect(); + + done(); + }); + + observer.observe({ type: "largest-contentful-paint", buffered: true }); + + // wait for the LCP observer to fire, if not, skip the test + observerWait = setTimeout(function() { + // no LCP before load + return this.skip(); + }.bind(this), 3000); + }); + + it("Should have exposed LCP metric (if LargestContentfulPaint is supported and happened by load)", function() { + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + if (typeof tf.beacons[0]["pt.fp"] === "undefined") { + // No paint + return this.skip(); + } + + assert.equal(tf.beacons[0]["pt.lcp"], BOOMR.plugins.PaintTiming.metrics.lcp()); + }); + + it("Should have exposed LCP metric src (pt.lcp.src) (if LargestContentfulPaint is supported and happened by load)", function() { + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + if (typeof tf.beacons[0]["pt.fp"] === "undefined") { + // No paint + return this.skip(); + } + + assert.equal(tf.beacons[0]["pt.lcp"], BOOMR.plugins.PaintTiming.metrics.lcp()); + + assert.isString(tf.beacons[0]["pt.lcp.src"]); + + assert.equal(tf.beacons[0]["pt.lcp.src"], BOOMR.plugins.PaintTiming.metrics.lcpSrc()); + assert.include(tf.beacons[0]["pt.lcp.src"], "delay?delay=2000&file=/assets/img.jpg&id=2000"); + }); + + it("Should have exposed LCP metric element (pt.lcp.el) (if LargestContentfulPaint is supported and happened by load)", function() { + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + if (typeof tf.beacons[0]["pt.fp"] === "undefined") { + // No paint + return this.skip(); + } + + assert.isString(tf.beacons[0]["pt.lcp.el"]); + + assert.equal(tf.beacons[0]["pt.lcp.el"], BOOMR.plugins.PaintTiming.metrics.lcpEl()); + assert.equal(tf.beacons[0]["pt.lcp.el"], "IMG"); + }); + + it("Should not have exposed LCP metric ID (pt.lcp.id) (if LargestContentfulPaint is supported and happened by load)", function() { + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + if (typeof tf.beacons[0]["pt.fp"] === "undefined") { + // No paint + return this.skip(); + } + + assert.notProperty(tf.beacons[0], "pt.lcp.id"); + }); + + it("Should have exposed LCP metric Pseudo-CSS Selector (pt.lcp.e) (if LargestContentfulPaint is supported and happened by load)", function() { + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + if (typeof tf.beacons[0]["pt.fp"] === "undefined") { + // No paint + return this.skip(); + } + + assert.isString(tf.beacons[0]["pt.lcp.e"]); + + assert.equal(tf.beacons[0]["pt.lcp.e"], BOOMR.plugins.PaintTiming.metrics.lcpE()); + assert.equal(tf.beacons[0]["pt.lcp.e"], "img"); + }); + + it("Should not have exposed LCP metric src-set (pt.lcp.srcset) (if LargestContentfulPaint is supported and happened by load)", function() { + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + if (typeof tf.beacons[0]["pt.fp"] === "undefined") { + // No paint + return this.skip(); + } + + assert.notProperty(tf.beacons[0], "pt.lcp.srcset"); + }); + + it("Should not have exposed LCP metric sizes (pt.lcp.sizes) (if LargestContentfulPaint is supported and happened by load)", function() { + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + if (typeof tf.beacons[0]["pt.fp"] === "undefined") { + // No paint + return this.skip(); + } + + assert.notProperty(tf.beacons[0], "pt.lcp.sizes"); + }); + + it("Should not have exposed LCP metric size (pt.lcp.s) (if LargestContentfulPaint is supported and happened by load)", function() { + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + if (typeof tf.beacons[0]["pt.fp"] === "undefined") { + // No paint + return this.skip(); + } + + assert.isTrue(Number.isInteger(tf.beacons[0]["pt.lcp.s"])); + + assert.equal(tf.beacons[0]["pt.lcp.s"], BOOMR.plugins.PaintTiming.metrics.lcpS()); + }); + }); +}); diff --git a/tests/page-templates/20-painttiming/07-lcp-spa-soft.html b/tests/page-templates/20-painttiming/07-lcp-spa-soft.html new file mode 100644 index 000000000..e5042f49d --- /dev/null +++ b/tests/page-templates/20-painttiming/07-lcp-spa-soft.html @@ -0,0 +1,41 @@ +<%= header %> + + + + +<%= boomerangScript %> + + + + + +<%= footer %> diff --git a/tests/page-templates/20-painttiming/07-lcp-spa-soft.js b/tests/page-templates/20-painttiming/07-lcp-spa-soft.js new file mode 100644 index 000000000..6ee1edc40 --- /dev/null +++ b/tests/page-templates/20-painttiming/07-lcp-spa-soft.js @@ -0,0 +1,41 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/20-painttiming/07-lcp-spa-soft", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + var C = BOOMR.utils.Compression; + + it("Should have sent three beacons", function(done) { + this.timeout(10000); + + t.ensureBeaconCount(done, 3); + }); + + describe("Beacon 1 (Page Load)", function(){ + it("Should have set pt.lcp (if LargestContentfulPaint is supported and happened by load)", function() { + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + if (typeof tf.beacons[0]["pt.fp"] === "undefined") { + // No paint + return this.skip(); + } + + assert.isDefined(tf.beacons[0]["pt.lcp"]); + }); + }); + + describe("Beacon 2 (SPA Soft #1)", function(){ + it("Should not have set pt.lcp", function() { + assert.isUndefined(tf.beacons[1]["pt.lcp"]); + }); + }); + + describe("Beacon 3 (SPA Soft #2)", function(){ + it("Should not have set pt.lcp", function() { + assert.isUndefined(tf.beacons[2]["pt.lcp"]); + }); + }); +}); diff --git a/tests/page-templates/20-painttiming/08-lcp-spa-soft-no-hard-lcp.html b/tests/page-templates/20-painttiming/08-lcp-spa-soft-no-hard-lcp.html new file mode 100644 index 000000000..8be2ef8e4 --- /dev/null +++ b/tests/page-templates/20-painttiming/08-lcp-spa-soft-no-hard-lcp.html @@ -0,0 +1,40 @@ +<%= header %> + + + +<%= boomerangScript %> + + + + + +<%= footer %> diff --git a/tests/page-templates/20-painttiming/08-lcp-spa-soft-no-hard-lcp.js b/tests/page-templates/20-painttiming/08-lcp-spa-soft-no-hard-lcp.js new file mode 100644 index 000000000..8812fc6bf --- /dev/null +++ b/tests/page-templates/20-painttiming/08-lcp-spa-soft-no-hard-lcp.js @@ -0,0 +1,32 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/20-painttiming/08-lcp-spa-soft-no-hard-lcp", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + var C = BOOMR.utils.Compression; + + it("Should have sent three beacons", function(done) { + this.timeout(10000); + + t.ensureBeaconCount(done, 3); + }); + + describe("Beacon 1 (Page Load)", function(){ + it("Should not have set pt.lcp", function() { + assert.isUndefined(tf.beacons[0]["pt.lcp"]); + }); + }); + + describe("Beacon 2 (SPA Soft #1)", function(){ + it("Should not have set pt.lcp", function() { + assert.isUndefined(tf.beacons[1]["pt.lcp"]); + }); + }); + + describe("Beacon 3 (SPA Soft #2)", function(){ + it("Should not have set pt.lcp", function() { + assert.isUndefined(tf.beacons[2]["pt.lcp"]); + }); + }); +}); diff --git a/tests/page-templates/20-painttiming/09-lcp-custom-element.html b/tests/page-templates/20-painttiming/09-lcp-custom-element.html new file mode 100644 index 000000000..512131742 --- /dev/null +++ b/tests/page-templates/20-painttiming/09-lcp-custom-element.html @@ -0,0 +1,28 @@ +<%= header %> +<%= boomerangSnippet %> + + + + + + + + + +<%= footer %> diff --git a/tests/page-templates/20-painttiming/09-lcp-custom-element.js b/tests/page-templates/20-painttiming/09-lcp-custom-element.js new file mode 100644 index 000000000..b82eb535b --- /dev/null +++ b/tests/page-templates/20-painttiming/09-lcp-custom-element.js @@ -0,0 +1,119 @@ +describe("e2e/20-painttiming/09-lcp-custom-element", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent two beacons", function(done) { + t.ensureBeaconCount(done, 2); + }); + + describe("Beacon 1", function() { + it("Should have exposed LCP metric src (pt.lcp.src) (if LargestContentfulPaint is supported and happened by load)", function() { + var b = tf.beacons[0]; + + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + if (typeof b["pt.fp"] === "undefined") { + // No paint + return this.skip(); + } + + assert.equal(b["pt.lcp"], BOOMR.plugins.PaintTiming.metrics.lcp()); + + assert.isString(b["pt.lcp.src"]); + + assert.equal(b["pt.lcp.src"], BOOMR.plugins.PaintTiming.metrics.lcpSrc()); + assert.include(b["pt.lcp.src"], "delay?delay=0&file=/assets/img.jpg&id=1"); + }); + + it("Should have exposed LCP metric element (pt.lcp.el) (if LargestContentfulPaint is supported and happened by load)", function() { + var b = tf.beacons[0]; + + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + if (typeof b["pt.fp"] === "undefined") { + // No paint + return this.skip(); + } + + assert.isString(b["pt.lcp.el"]); + + assert.equal(b["pt.lcp.el"], BOOMR.plugins.PaintTiming.metrics.lcpEl()); + assert.equal(b["pt.lcp.el"], "CUSTOM-ELEMENT"); + }); + + it("Should have exposed LCP metric ID (pt.lcp.id) (if LargestContentfulPaint is supported and happened by load)", function() { + var b = tf.beacons[0]; + + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + if (typeof b["pt.fp"] === "undefined") { + // No paint + return this.skip(); + } + + assert.isString(b["pt.lcp.id"]); + + assert.equal(b["pt.lcp.id"], BOOMR.plugins.PaintTiming.metrics.lcpId()); + assert.equal(b["pt.lcp.id"], "lcp-id"); + }); + + it("Should have exposed LCP metric Pseudo-CSS Selector (pt.lcp.e) (if LargestContentfulPaint is supported and happened by load)", function() { + var b = tf.beacons[0]; + + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + if (typeof b["pt.fp"] === "undefined") { + // No paint + return this.skip(); + } + + assert.isString(b["pt.lcp.e"]); + + assert.equal(b["pt.lcp.e"], BOOMR.plugins.PaintTiming.metrics.lcpE()); + assert.equal(b["pt.lcp.e"], "custom-element#lcp-id"); + }); + + it("Should have exposed LCP metric size (pt.lcp.s) (if LargestContentfulPaint is supported and happened by load)", function() { + var b = tf.beacons[0]; + + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + if (typeof b["pt.fp"] === "undefined") { + // No paint + return this.skip(); + } + + assert.isTrue(Number.isInteger(b["pt.lcp.s"])); + + assert.equal(b["pt.lcp.s"], BOOMR.plugins.PaintTiming.metrics.lcpS()); + }); + }); + + describe("Beacon 2", function() { + it("Should not have exposed LCP attributes", function() { + var b = tf.beacons[1]; + + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + assert.isUndefined(b["pt.lcp.src"]); + assert.isUndefined(b["pt.lcp.el"]); + assert.isUndefined(b["pt.lcp.id"]); + assert.isUndefined(b["pt.lcp.e"]); + assert.isUndefined(b["pt.lcp.srcset"]); + assert.isUndefined(b["pt.lcp.sizes"]); + assert.isUndefined(b["pt.lcp.s"]); + }); + }); +}); diff --git a/tests/page-templates/20-painttiming/09-prerendered.html b/tests/page-templates/20-painttiming/09-prerendered.html new file mode 100644 index 000000000..eecd37ee8 --- /dev/null +++ b/tests/page-templates/20-painttiming/09-prerendered.html @@ -0,0 +1,100 @@ +<%= header %> + +<%= boomerangScript %> + + + + + + +<%= footer %> diff --git a/tests/page-templates/20-painttiming/09-prerendered.js b/tests/page-templates/20-painttiming/09-prerendered.js new file mode 100644 index 000000000..9580e6322 --- /dev/null +++ b/tests/page-templates/20-painttiming/09-prerendered.js @@ -0,0 +1,50 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["LargestContentfulPaint", "generateLCP"]); + +describe("e2e/20-painttiming/09-prerendered", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a beacon", function() { + // ensure we fired a beacon ('beacon') + assert.isTrue(tf.fired_onbeacon); + }); + + it("Should have set pt.fp on the beacon and offset by the activationStart", function() { + if (!t.isPaintTimingSupported() || !t.isPrerenderingSupported()) { + return this.skip(); + } + + var fp = parseInt(tf.lastBeacon()["pt.fp"], 10); + + assert.operator(fp, "<", BOOMR_test.ACT_ST_OFFSET); + assert.operator(fp, ">=", 0); + }); + + it("Should have set pt.fcp on the beacon and offset by the activationStart", function() { + if (!t.isPaintTimingSupported() || !t.isPrerenderingSupported()) { + return this.skip(); + } + + var fcp = parseInt(tf.lastBeacon()["pt.fcp"], 10); + + assert.operator(fcp, "<", BOOMR_test.ACT_ST_OFFSET); + assert.operator(fcp, ">=", 0); + }); + + it("Should have set pt.lcp on the beacon and offset by the activationStart", function() { + if (!t.isPaintTimingSupported() || + !t.isLargestContentfulPaintSupported() || + !t.isPrerenderingSupported()) { + return this.skip(); + } + + var lcp = parseInt(tf.lastBeacon()["pt.lcp"], 10); + + assert.operator(lcp, "<", BOOMR_test.ACT_ST_OFFSET); + assert.operator(lcp, ">=", 0); + }); +}); diff --git a/tests/page-templates/20-painttiming/10-lcp-background-src.html b/tests/page-templates/20-painttiming/10-lcp-background-src.html new file mode 100644 index 000000000..14b154eaf --- /dev/null +++ b/tests/page-templates/20-painttiming/10-lcp-background-src.html @@ -0,0 +1,22 @@ +<%= header %> +<%= boomerangSnippet %> + + + + + + +<%= footer %> diff --git a/tests/page-templates/20-painttiming/10-lcp-background-src.js b/tests/page-templates/20-painttiming/10-lcp-background-src.js new file mode 100644 index 000000000..4141d3a12 --- /dev/null +++ b/tests/page-templates/20-painttiming/10-lcp-background-src.js @@ -0,0 +1,119 @@ +describe("e2e/20-painttiming/10-lcp-background-src", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent two beacons", function(done) { + t.ensureBeaconCount(done, 2); + }); + + describe("Beacon 1", function() { + it("Should have exposed LCP metric src (pt.lcp.src) (if LargestContentfulPaint is supported and happened by load)", function() { + var b = tf.beacons[0]; + + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + if (typeof b["pt.fp"] === "undefined") { + // No paint + return this.skip(); + } + + assert.equal(b["pt.lcp"], BOOMR.plugins.PaintTiming.metrics.lcp()); + + assert.isString(b["pt.lcp.src"]); + + assert.equal(b["pt.lcp.src"], BOOMR.plugins.PaintTiming.metrics.lcpSrc()); + assert.include(b["pt.lcp.src"], "delay?delay=2000&file=/assets/img.jpg&id=0"); + }); + + it("Should have exposed LCP metric element (pt.lcp.el) (if LargestContentfulPaint is supported and happened by load)", function() { + var b = tf.beacons[0]; + + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + if (typeof b["pt.fp"] === "undefined") { + // No paint + return this.skip(); + } + + assert.isString(b["pt.lcp.el"]); + + assert.equal(b["pt.lcp.el"], BOOMR.plugins.PaintTiming.metrics.lcpEl()); + assert.equal(b["pt.lcp.el"], "IMG"); + }); + + it("Should have exposed LCP metric ID (pt.lcp.id) (if LargestContentfulPaint is supported and happened by load)", function() { + var b = tf.beacons[0]; + + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + if (typeof b["pt.fp"] === "undefined") { + // No paint + return this.skip(); + } + + assert.isString(b["pt.lcp.id"]); + + assert.equal(b["pt.lcp.id"], BOOMR.plugins.PaintTiming.metrics.lcpId()); + assert.equal(b["pt.lcp.id"], "lcp-id"); + }); + + it("Should have exposed LCP metric Pseudo-CSS Selector (pt.lcp.e) (if LargestContentfulPaint is supported and happened by load)", function() { + var b = tf.beacons[0]; + + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + if (typeof b["pt.fp"] === "undefined") { + // No paint + return this.skip(); + } + + assert.isString(b["pt.lcp.e"]); + + assert.equal(b["pt.lcp.e"], BOOMR.plugins.PaintTiming.metrics.lcpE()); + assert.equal(b["pt.lcp.e"], "img#lcp-id"); + }); + + it("Should have exposed LCP metric size (pt.lcp.s) (if LargestContentfulPaint is supported and happened by load)", function() { + var b = tf.beacons[0]; + + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + if (typeof b["pt.fp"] === "undefined") { + // No paint + return this.skip(); + } + + assert.isTrue(Number.isInteger(b["pt.lcp.s"])); + + assert.equal(b["pt.lcp.s"], BOOMR.plugins.PaintTiming.metrics.lcpS()); + }); + }); + + describe("Beacon 2", function() { + it("Should not have exposed LCP attributes", function() { + var b = tf.beacons[1]; + + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + assert.isUndefined(b["pt.lcp.src"]); + assert.isUndefined(b["pt.lcp.el"]); + assert.isUndefined(b["pt.lcp.id"]); + assert.isUndefined(b["pt.lcp.e"]); + assert.isUndefined(b["pt.lcp.srcset"]); + assert.isUndefined(b["pt.lcp.sizes"]); + assert.isUndefined(b["pt.lcp.s"]); + }); + }); +}); diff --git a/tests/page-templates/20-painttiming/11-lcp-src-datauri.html b/tests/page-templates/20-painttiming/11-lcp-src-datauri.html new file mode 100644 index 000000000..6763d1fac --- /dev/null +++ b/tests/page-templates/20-painttiming/11-lcp-src-datauri.html @@ -0,0 +1,25 @@ +<%= header %> +<%= boomerangSnippet %> + + + + + + + +<%= footer %> diff --git a/tests/page-templates/20-painttiming/11-lcp-src-datauri.js b/tests/page-templates/20-painttiming/11-lcp-src-datauri.js new file mode 100644 index 000000000..1a6f037f2 --- /dev/null +++ b/tests/page-templates/20-painttiming/11-lcp-src-datauri.js @@ -0,0 +1,119 @@ +describe("e2e/20-painttiming/11-lcp-src-datauri", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent two beacons", function(done) { + t.ensureBeaconCount(done, 2); + }); + + describe("Beacon 1", function() { + it("Should have exposed LCP metric src (pt.lcp.src) (if LargestContentfulPaint is supported and happened by load)", function() { + var b = tf.beacons[0]; + + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + if (typeof b["pt.fp"] === "undefined") { + // No paint + return this.skip(); + } + + assert.equal(b["pt.lcp"], BOOMR.plugins.PaintTiming.metrics.lcp()); + + assert.isString(b["pt.lcp.src"]); + + assert.equal(b["pt.lcp.src"], BOOMR.plugins.PaintTiming.metrics.lcpSrc()); + assert.include(b["pt.lcp.src"], "data:image/png"); + }); + + it("Should have exposed LCP metric element (pt.lcp.el) (if LargestContentfulPaint is supported and happened by load)", function() { + var b = tf.beacons[0]; + + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + if (typeof b["pt.fp"] === "undefined") { + // No paint + return this.skip(); + } + + assert.isString(b["pt.lcp.el"]); + + assert.equal(b["pt.lcp.el"], BOOMR.plugins.PaintTiming.metrics.lcpEl()); + assert.equal(b["pt.lcp.el"], "IMG"); + }); + + it("Should have exposed LCP metric ID (pt.lcp.id) (if LargestContentfulPaint is supported and happened by load)", function() { + var b = tf.beacons[0]; + + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + if (typeof b["pt.fp"] === "undefined") { + // No paint + return this.skip(); + } + + assert.isString(b["pt.lcp.id"]); + + assert.equal(b["pt.lcp.id"], BOOMR.plugins.PaintTiming.metrics.lcpId()); + assert.equal(b["pt.lcp.id"], "lcp-id"); + }); + + it("Should have exposed LCP metric Pseudo-CSS Selector (pt.lcp.e) (if LargestContentfulPaint is supported and happened by load)", function() { + var b = tf.beacons[0]; + + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + if (typeof b["pt.fp"] === "undefined") { + // No paint + return this.skip(); + } + + assert.isString(b["pt.lcp.e"]); + + assert.equal(b["pt.lcp.e"], BOOMR.plugins.PaintTiming.metrics.lcpE()); + assert.equal(b["pt.lcp.e"], "img#lcp-id"); + }); + + it("Should have exposed LCP metric size (pt.lcp.s) (if LargestContentfulPaint is supported and happened by load)", function() { + var b = tf.beacons[0]; + + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + if (typeof b["pt.fp"] === "undefined") { + // No paint + return this.skip(); + } + + assert.isTrue(Number.isInteger(b["pt.lcp.s"])); + + assert.equal(b["pt.lcp.s"], BOOMR.plugins.PaintTiming.metrics.lcpS()); + }); + }); + + describe("Beacon 2", function() { + it("Should not have exposed LCP attributes", function() { + var b = tf.beacons[1]; + + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + assert.isUndefined(b["pt.lcp.src"]); + assert.isUndefined(b["pt.lcp.el"]); + assert.isUndefined(b["pt.lcp.id"]); + assert.isUndefined(b["pt.lcp.e"]); + assert.isUndefined(b["pt.lcp.srcset"]); + assert.isUndefined(b["pt.lcp.sizes"]); + assert.isUndefined(b["pt.lcp.s"]); + }); + }); +}); diff --git a/tests/page-templates/20-painttiming/support/custom-element.js b/tests/page-templates/20-painttiming/support/custom-element.js new file mode 100644 index 000000000..13623107b --- /dev/null +++ b/tests/page-templates/20-painttiming/support/custom-element.js @@ -0,0 +1,8 @@ +/* eslint-env es6 */ +class CustomElement extends HTMLElement { + connectedCallback() { + this.style.backgroundImage = 'url("' + this.getAttribute("data-src-base") + '")'; + } +} + +window.customElements.define("custom-element", CustomElement); diff --git a/tests/page-templates/20-painttiming/tests.json5 b/tests/page-templates/20-painttiming/tests.json5 new file mode 100644 index 000000000..127b2b265 --- /dev/null +++ b/tests/page-templates/20-painttiming/tests.json5 @@ -0,0 +1,20 @@ +{ + all: { + requires: ["painttiming"] + }, + "04-lcp-error": { + requires: ["errors"] + }, + "05-lcp-early": { + requires: ["early"] + }, + "06-lcp-error-after-pageload": { + requires: ["errors"] + }, + "07-lcp-spa-soft": { + requires: ["spa"] + }, + "08-lcp-spa-soft-no-hard-lcp": { + requires: ["spa"] + } +} diff --git a/tests/page-templates/21-continuity/00-fps.html b/tests/page-templates/21-continuity/00-fps.html new file mode 100644 index 000000000..b4576458e --- /dev/null +++ b/tests/page-templates/21-continuity/00-fps.html @@ -0,0 +1,23 @@ +<%= header %> +<%= continuitySnippet %> +<%= boomerangScript %> + + + +<%= footer %> diff --git a/tests/page-templates/21-continuity/00-fps.js b/tests/page-templates/21-continuity/00-fps.js new file mode 100644 index 000000000..38a6331fd --- /dev/null +++ b/tests/page-templates/21-continuity/00-fps.js @@ -0,0 +1,83 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/21-continuity/00-fps", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a single beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have set the FPS (c.f) (if rAF is supported)", function() { + if (!window.requestAnimationFrame) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isDefined(b["c.f"]); + assert.operator(parseInt(b["c.f"], 10), ">", 0); + assert.operator(parseInt(b["c.f"], 10), "<=", 60); + }); + + it("Should have set the FPS start time (c.f.s) (if rAF is supported)", function() { + if (!window.requestAnimationFrame) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isDefined(b["c.f.s"]); + assert.operator(parseInt(b["c.f.s"], 36), ">", 0); + assert.operator(parseInt(b["c.f.s"], 36), ">=", performance.timing.navigationStart); + assert.operator(parseInt(b["c.f.s"], 36), "<=", BOOMR.now()); + }); + + it("Should have sent the FPS duration (c.f.d) (if rAF is supported)", function() { + if (!window.requestAnimationFrame) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isDefined(b["c.f.d"]); + assert.operator(parseInt(b["c.f.d"], 10), ">", 0); + + // might fire a bit after onload + assert.operator(parseInt(b["c.f.d"], 10), "<=", parseInt(b.t_done, 10) + 500); + }); + + it("Should have sent the FPS long frames (c.f.l) (if rAF is supported)", function() { + if (!window.requestAnimationFrame) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isDefined(b["c.f.l"]); + assert.operator(parseInt(b["c.f.l"], 10), ">", 0); + }); + + it("Should have set the minimum FPS (c.f.m) (if rAF is supported)", function() { + if (!window.requestAnimationFrame) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isDefined(b["c.f.m"]); + assert.operator(parseInt(b["c.f.m"], 10), ">", 0); + }); + + it("Should have the FPS timeline (c.t.fps) (if rAF is supported)", function() { + if (!window.requestAnimationFrame) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isDefined(b["c.t.fps"]); + assert.operator(b["c.t.fps"].length, ">=", 1); + }); +}); diff --git a/tests/page-templates/21-continuity/01-fps-delayed.html b/tests/page-templates/21-continuity/01-fps-delayed.html new file mode 100644 index 000000000..d62ab0172 --- /dev/null +++ b/tests/page-templates/21-continuity/01-fps-delayed.html @@ -0,0 +1,28 @@ +<%= header %> + +<%= continuitySnippet %> + + + +<%= boomerangDelayedSnippet %> + +<%= footer %> diff --git a/tests/page-templates/21-continuity/01-fps-delayed.js b/tests/page-templates/21-continuity/01-fps-delayed.js new file mode 100644 index 000000000..b8d6f147d --- /dev/null +++ b/tests/page-templates/21-continuity/01-fps-delayed.js @@ -0,0 +1,86 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["BOOMR_script_delay"]); + +describe("e2e/21-continuity/01-fps-delayed", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a single beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have set the FPS (c.f) (if rAF is supported)", function() { + if (!window.requestAnimationFrame) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isDefined(b["c.f"]); + assert.operator(parseInt(b["c.f"], 10), ">", 0); + assert.operator(parseInt(b["c.f"], 10), "<=", 60); + }); + + it("Should have set the FPS start time (c.f.s) (if rAF is supported)", function() { + if (!window.requestAnimationFrame) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isDefined(b["c.f.s"]); + assert.operator(parseInt(b["c.f.s"], 36), ">", 0); + assert.operator(parseInt(b["c.f.s"], 36), ">=", performance.timing.navigationStart); + assert.operator(parseInt(b["c.f.s"], 36), "<=", BOOMR.now()); + }); + + it("Should have sent the FPS duration (c.f.d) (if rAF is supported)", function() { + if (!window.requestAnimationFrame) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isDefined(b["c.f.d"]); + assert.operator(parseInt(b["c.f.d"], 10), ">", 0); + + // might fire a bit after onload + assert.operator(parseInt(b["c.f.d"], 10), "<=", parseInt(b.t_done, 10) + 500); + }); + + it("Should have sent the FPS long frames (c.f.l) (if rAF is supported)", function() { + if (!window.requestAnimationFrame) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isDefined(b["c.f.l"]); + assert.operator(parseInt(b["c.f.l"], 10), ">", 0); + }); + + it("Should have set the minimum FPS (c.f.m) (if rAF is supported)", function() { + if (!window.requestAnimationFrame) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isDefined(b["c.f.m"]); + assert.operator(parseInt(b["c.f.m"], 10), ">", 0); + }); + + it("Should have the FPS timeline (c.t.fps) (if rAF is supported)", function() { + if (!window.requestAnimationFrame) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isDefined(b["c.t.fps"]); + assert.operator(b["c.t.fps"].length, ">=", 1); + }); +}); diff --git a/tests/page-templates/21-continuity/02-fps-after-load.html b/tests/page-templates/21-continuity/02-fps-after-load.html new file mode 100644 index 000000000..4e27aa454 --- /dev/null +++ b/tests/page-templates/21-continuity/02-fps-after-load.html @@ -0,0 +1,25 @@ +<%= header %> +<%= continuitySnippet %> +<%= boomerangScript %> + + + +<%= footer %> diff --git a/tests/page-templates/21-continuity/02-fps-after-load.js b/tests/page-templates/21-continuity/02-fps-after-load.js new file mode 100644 index 000000000..03694a210 --- /dev/null +++ b/tests/page-templates/21-continuity/02-fps-after-load.js @@ -0,0 +1,92 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/21-continuity/02-fps-after-load", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent two beacons", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 2); + }); + + it("Should have set the FPS (c.f) (if rAF is supported)", function() { + if (!window.requestAnimationFrame) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isDefined(b["c.f"]); + assert.operator(parseInt(b["c.f"], 10), ">", 0); + assert.operator(parseInt(b["c.f"], 10), "<=", 60); + }); + + it("Should have set the FPS start time (c.f.s) (if rAF is supported)", function() { + if (!window.requestAnimationFrame) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isDefined(b["c.f.s"]); + assert.operator(parseInt(b["c.f.s"], 36), ">", 0); + assert.operator(parseInt(b["c.f.s"], 36), ">=", performance.timing.navigationStart); + assert.operator(parseInt(b["c.f.s"], 36), "<=", BOOMR.now()); + }); + + it("Should have set the FPS start time (c.f.s) different from the first beacon (if rAF is supported)", function() { + if (!window.requestAnimationFrame) { + return this.skip(); + } + + assert.notEqual(tf.beacons[0]["c.f.s"], tf.beacons[1]["c.f.s"]); + }); + + it("Should have sent the FPS duration (c.f.d) (if rAF is supported)", function() { + if (!window.requestAnimationFrame) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isDefined(b["c.f.d"]); + assert.operator(parseInt(b["c.f.d"], 10), ">", 0); + + // close to t_done + assert.closeTo(parseInt(b["c.f.d"], 10), b.t_done + 100 /* setTimeout delay */, 150); + }); + + it("Should have sent the FPS long frames (c.f.l) (if rAF is supported)", function() { + if (!window.requestAnimationFrame) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isDefined(b["c.f.l"]); + assert.operator(parseInt(b["c.f.l"], 10), ">", 0); + }); + + it("Should have set the minimum FPS (c.f.m) (if rAF is supported)", function() { + if (!window.requestAnimationFrame) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isDefined(b["c.f.m"]); + assert.operator(parseInt(b["c.f.m"], 10), ">", 0); + }); + + it("Should have the FPS timeline (c.t.fps) (if rAF is supported)", function() { + if (!window.requestAnimationFrame) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isDefined(b["c.t.fps"]); + assert.operator(b["c.t.fps"].length, ">=", 1); + }); +}); diff --git a/tests/page-templates/21-continuity/03-fps-after-load-disabled.html b/tests/page-templates/21-continuity/03-fps-after-load-disabled.html new file mode 100644 index 000000000..81812a629 --- /dev/null +++ b/tests/page-templates/21-continuity/03-fps-after-load-disabled.html @@ -0,0 +1,25 @@ +<%= header %> +<%= continuitySnippet %> +<%= boomerangScript %> + + + +<%= footer %> diff --git a/tests/page-templates/21-continuity/03-fps-after-load-disabled.js b/tests/page-templates/21-continuity/03-fps-after-load-disabled.js new file mode 100644 index 000000000..ee3726458 --- /dev/null +++ b/tests/page-templates/21-continuity/03-fps-after-load-disabled.js @@ -0,0 +1,72 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/21-continuity/03-fps-after-load-disabled", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent two beacons", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 2); + }); + + it("Should not have set the FPS (c.f) (if rAF is supported)", function() { + if (!window.requestAnimationFrame) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isUndefined(b["c.f"]); + }); + + it("Should not have set the FPS start time (c.f.s) (if rAF is supported)", function() { + if (!window.requestAnimationFrame) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isUndefined(b["c.f.s"]); + }); + + it("Should not have sent the FPS duration (c.f.d) (if rAF is supported)", function() { + if (!window.requestAnimationFrame) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isUndefined(b["c.f.d"]); + }); + + it("Should not have sent the FPS long frames (c.f.l) (if rAF is supported)", function() { + if (!window.requestAnimationFrame) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isUndefined(b["c.f.l"]); + }); + + it("Should not have set the minimum FPS (c.f.m) (if rAF is supported)", function() { + if (!window.requestAnimationFrame) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isUndefined(b["c.f.m"]); + }); + + it("Should not have the FPS timeline (c.t.fps) (if rAF is supported)", function() { + if (!window.requestAnimationFrame) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isUndefined(b["c.t.fps"]); + }); +}); diff --git a/tests/page-templates/21-continuity/04-epoch.html b/tests/page-templates/21-continuity/04-epoch.html new file mode 100644 index 000000000..74c009e86 --- /dev/null +++ b/tests/page-templates/21-continuity/04-epoch.html @@ -0,0 +1,12 @@ +<%= header %> +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/21-continuity/04-epoch.js b/tests/page-templates/21-continuity/04-epoch.js new file mode 100644 index 000000000..1527fd877 --- /dev/null +++ b/tests/page-templates/21-continuity/04-epoch.js @@ -0,0 +1,35 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/21-continuity/04-epoch", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a single beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have set the epoch (c.e) to navigationStart (if NavigationTiming is supported)", function() { + if (!t.isNavigationTimingSupported()) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isDefined(b["c.e"]); + + assert.equal(parseInt(b["c.e"], 36), BOOMR.plugins.RT.navigationStart()); + }); + + it("Should have set the epoch (c.e) to something close to now (if NavigationTiming is not supported)", function() { + if (t.isNavigationTimingSupported()) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isDefined(b["c.e"]); + + assert.closeTo(parseInt(b["c.e"], 36), BOOMR.now(), 3000); + }); +}); diff --git a/tests/page-templates/21-continuity/05-longtasks.html b/tests/page-templates/21-continuity/05-longtasks.html new file mode 100644 index 000000000..8ef0eafde --- /dev/null +++ b/tests/page-templates/21-continuity/05-longtasks.html @@ -0,0 +1,20 @@ +<%= header %> +<%= boomerangScript %> + + + + +<%= footer %> diff --git a/tests/page-templates/21-continuity/05-longtasks.js b/tests/page-templates/21-continuity/05-longtasks.js new file mode 100644 index 000000000..d02dd5950 --- /dev/null +++ b/tests/page-templates/21-continuity/05-longtasks.js @@ -0,0 +1,183 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/21-continuity/05-longtasks", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + // finds LongTasks that we know we triggered (since we caused a 1500ms loop) + function findMyLongTasks(b, type) { + return BOOMR.utils.Compression.jsUrlDecompress(b["c.lt"]).filter(function(o) { + return parseInt(o.d, 36) >= 1000 && o.n === type; + })[0]; + } + + it("Should have sent a single beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have set the LongTask count (c.lt.n) of at least 2 (if LongTasks are supported)", function() { + if (!t.isLongTasksSupported()) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isDefined(b["c.lt.n"]); + + // we caused at least 2 + assert.operator(parseInt(b["c.lt.n"], 10), ">=", 2); + }); + + it("Should have set the LongTask time (c.lt.tt) (if LongTasks are supported)", function() { + if (!t.isLongTasksSupported()) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isDefined(b["c.lt.tt"]); + + // we caused 3000ms + assert.operator(parseInt(b["c.lt.tt"], 10), ">=", 2900); + + // should be less less than 10 seconds + assert.operator(parseInt(b["c.lt.tt"], 10), "<=", 10000); + }); + + it("Should have set the LongTask data (c.lt) start time for the 'self' task (if LongTasks are supported)", function() { + if (!t.isLongTasksSupported()) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + var ltData = findMyLongTasks(b, 1); + + assert.operator(parseInt(ltData.s, 36), ">", 0); + assert.operator(parseInt(ltData.s, 36), "<=", 5000); + }); + + it("Should have set the LongTask data (c.lt) duration for the 'self' task (if LongTasks are supported)", function() { + if (!t.isLongTasksSupported()) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + var ltData = findMyLongTasks(b, 1); + + assert.operator(parseInt(ltData.d, 36), ">=", 1400); + assert.operator(parseInt(ltData.d, 36), "<=", 3000); + }); + + it("Should have set the LongTask data (c.lt) type for the 'self' task (if LongTasks are supported)", function() { + if (!t.isLongTasksSupported()) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + var ltData = findMyLongTasks(b, 1); + + assert.equal(ltData.n, "1"); + }); + + it("Should have set the LongTask data (c.lt) start time for the 'same-origin-descendant' task (if LongTasks are supported)", function() { + if (!t.isLongTasksSupported()) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + var ltData = findMyLongTasks(b, 3); + + assert.operator(parseInt(ltData.s, 36), ">", 0); + assert.operator(parseInt(ltData.s, 36), "<=", 10000); + }); + + it("Should have set the LongTask data (c.lt) duration for the 'same-origin-descendant' task (if LongTasks are supported)", function() { + if (!t.isLongTasksSupported()) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + var ltData = findMyLongTasks(b, 3); + + assert.operator(parseInt(ltData.d, 36), ">=", 1400); + assert.operator(parseInt(ltData.d, 36), "<=", 3000); + }); + + it("Should have set the LongTask data (c.lt) type for the 'same-origin-descendant' task (if LongTasks are supported)", function() { + if (!t.isLongTasksSupported()) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + var ltData = findMyLongTasks(b, 3); + + assert.equal(ltData.n, "3"); + }); + + it("Should have set the LongTask data (c.lt) attribution culprit attribution for the 'same-origin-descendant' task (if LongTasks are supported)", function() { + if (!t.isLongTasksSupported()) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + var ltData = findMyLongTasks(b, 3); + + // "script" or "unknown" (depending on the Chrome version) + assert.isTrue(ltData.a[0].a === 1 || ltData.a[0].a === 0); + }); + + it("Should have set the LongTask data (c.lt) attribution culprit name for the 'same-origin-descendant' task (if LongTasks are supported)", function() { + if (!t.isLongTasksSupported()) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + var ltData = findMyLongTasks(b, 3); + + assert.equal(ltData.a[0].t, "1"); + }); + + it("Should have set the LongTask data (c.lt) attribution container id for the 'same-origin-descendant' task (if LongTasks are supported)", function() { + if (!t.isLongTasksSupported()) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + var ltData = findMyLongTasks(b, 3); + + assert.equal(ltData.a[0].i, "longtaskframeid"); + }); + + it("Should have set the LongTask data (c.lt) attribution container name for the 'same-origin-descendant' task (if LongTasks are supported)", function() { + if (!t.isLongTasksSupported()) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + var ltData = findMyLongTasks(b, 3); + + assert.equal(ltData.a[0].n, "longtaskframename"); + }); + + it("Should have the LongTask timeline (c.t.longtask) (if LongTasks are supported)", function() { + if (!t.isLongTasksSupported()) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isDefined(b["c.t.fps"]); + assert.operator(b["c.t.fps"].length, ">=", 1); + }); +}); diff --git a/tests/page-templates/21-continuity/06-longtasks-after-load.html b/tests/page-templates/21-continuity/06-longtasks-after-load.html new file mode 100644 index 000000000..1c97024c2 --- /dev/null +++ b/tests/page-templates/21-continuity/06-longtasks-after-load.html @@ -0,0 +1,23 @@ +<%= header %> +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/21-continuity/06-longtasks-after-load.js b/tests/page-templates/21-continuity/06-longtasks-after-load.js new file mode 100644 index 000000000..f3b595f99 --- /dev/null +++ b/tests/page-templates/21-continuity/06-longtasks-after-load.js @@ -0,0 +1,95 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/21-continuity/06-longtasks-after-load", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent 2 beacons", function(done) { + t.ensureBeaconCount(done, 2); + }); + + it("Should have set the LongTask count (c.lt.n) (if LongTasks are supported)", function() { + if (!t.isLongTasksSupported()) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isDefined(b["c.lt.n"]); + assert.operator(parseInt(b["c.lt.n"], 10), ">=", 1); + }); + + it("Should have set the LongTask time (c.lt.tt) (if LongTasks are supported)", function() { + if (!t.isLongTasksSupported()) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isDefined(b["c.lt.tt"]); + assert.operator(parseInt(b["c.lt.tt"], 10), ">", 1500); + assert.operator(parseInt(b["c.lt.tt"], 10), "<=", 3000); + }); + + it("Should have set the LongTask data (c.lt) start time for the first task (if LongTasks are supported)", function() { + if (!t.isLongTasksSupported()) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + var ltData = BOOMR.utils.Compression.jsUrlDecompress(b["c.lt"])[0]; + + assert.operator(parseInt(ltData.s, 36), ">=", + performance.timing.loadEventStart - performance.timing.navigationStart); + }); + + it("Should have set the LongTask data (c.lt) duration for the first task (if LongTasks are supported)", function() { + if (!t.isLongTasksSupported()) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + var ltData = BOOMR.utils.Compression.jsUrlDecompress(b["c.lt"])[0]; + + assert.operator(parseInt(ltData.d, 36), ">=", 1500); + assert.operator(parseInt(ltData.d, 36), "<=", 3000); + }); + + it("Should have set the LongTask data (c.lt) type for the first task (if LongTasks are supported)", function() { + if (!t.isLongTasksSupported()) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + var ltData = BOOMR.utils.Compression.jsUrlDecompress(b["c.lt"])[0]; + + assert.equal(ltData.n, "1"); + }); + + it("Should have the LongTask timeline (c.t.longtask) (if LongTasks are supported)", function() { + if (!t.isLongTasksSupported()) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isDefined(b["c.t.fps"]); + assert.operator(b["c.t.fps"].length, ">=", 1); + }); + + it("Should have last Continuity beacon time (c.lb) on the second beacon but not on the first", function() { + var b1 = tf.beacons[0]; + var b2 = tf.beacons[1]; + + // first beacon + assert.isUndefined(b1["c.lb"]); + + // second beacon + assert.isDefined(b2["c.lb"]); + assert.closeTo(parseInt(b2["c.lb"], 36), BOOMR.now(), 10000); + }); +}); diff --git a/tests/page-templates/21-continuity/07-click.html b/tests/page-templates/21-continuity/07-click.html new file mode 100644 index 000000000..29b31b133 --- /dev/null +++ b/tests/page-templates/21-continuity/07-click.html @@ -0,0 +1,54 @@ +<%= header %> +<%= boomerangScript %> + + + + + + + +<%= footer %> diff --git a/tests/page-templates/21-continuity/07-click.js b/tests/page-templates/21-continuity/07-click.js new file mode 100644 index 000000000..77a4727e0 --- /dev/null +++ b/tests/page-templates/21-continuity/07-click.js @@ -0,0 +1,115 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["ev1", "ev2"]); + +describe("e2e/21-continuity/07-click", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a single beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent the click count (c.c) (if creating Mouse Events is supported)", function() { + if (window.cannotCreateMouseEvent) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isDefined(b["c.c"]); + assert.equal(parseInt(b["c.c"], 10), 10); + }); + + it("Should have sent the rage click count (c.c.r) (if creating Mouse Events is supported)", function() { + if (window.cannotCreateMouseEvent) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isDefined(b["c.c.r"]); + assert.equal(parseInt(b["c.c.r"], 10), 2); + }); + + it("Should have sent the log (c.l) (if creating Mouse Events is supported)", function() { + if (window.cannotCreateMouseEvent) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isDefined(b["c.l"]); + }); + + it("Should have logged the correct events (c.l) (if creating Mouse Events is supported)", function() { + if (window.cannotCreateMouseEvent) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + var clickLogs = BOOMR.plugins.Continuity.decompressLog(b["c.l"]).filter(function(obj) { + // LOG_TYPE_CLICK === 1 + return obj.type === 1; + }); + + assert.equal(clickLogs.length, 10); + + for (var i = 0; i < clickLogs.length; i++) { + // should all be x,y === 100 or 200 + var x = parseInt(clickLogs[i].x, 36); + var y = parseInt(clickLogs[i].y, 36); + + assert.isTrue(x === 100 || x === 200); + assert.isTrue(y === 100 || y === 200); + } + }); + + it("Should have the click timeline (c.t.click) (if creating Mouse Events is supported)", function() { + if (window.cannotCreateMouseEvent) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isDefined(b["c.t.click"]); + assert.operator(b["c.t.click"].length, ">=", 1); + }); + + it("Should have the interaction timeline (c.t.inter) (if creating Mouse Events is supported)", function() { + if (window.cannotCreateMouseEvent) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isDefined(b["c.t.inter"]); + assert.operator(b["c.t.inter"].length, ">=", 1); + }); + + it("Should have the Time to First Interaction (c.ttfi) (if creating Mouse Events is supported)", function() { + if (window.cannotCreateMouseEvent) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isDefined(b["c.ttfi"]); + assert.operator(parseInt(b["c.ttfi"], 10), ">=", 1); + assert.equal(parseFloat(b["c.ttfi"]), parseInt(b["c.ttfi"], 10)); + }); + + it("Should have First Input Delay (c.fid) (if creating Mouse Events is supported)", function() { + if (window.cannotCreateMouseEvent) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isDefined(b["c.fid"]); + assert.operator(parseInt(b["c.fid"], 10), ">=", 0); + }); +}); diff --git a/tests/page-templates/21-continuity/08-click-after-load.html b/tests/page-templates/21-continuity/08-click-after-load.html new file mode 100644 index 000000000..265d2f582 --- /dev/null +++ b/tests/page-templates/21-continuity/08-click-after-load.html @@ -0,0 +1,78 @@ +<%= header %> +<%= boomerangScript %> + + + + + + + +<%= footer %> diff --git a/tests/page-templates/21-continuity/08-click-after-load.js b/tests/page-templates/21-continuity/08-click-after-load.js new file mode 100644 index 000000000..874006132 --- /dev/null +++ b/tests/page-templates/21-continuity/08-click-after-load.js @@ -0,0 +1,177 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["ev1", "ev2"]); + +describe("e2e/21-continuity/08-click-after-load", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent three beacons (if creating Mouse Events is supported)", function(done) { + if (!window.cannotCreateMouseEvent) { + this.timeout(10000); + t.ensureBeaconCount(done, 3); + } + else { + this.timeout(10000); + t.ensureBeaconCount(done, 1); + } + }); + + it("Should have sent the click count (c.c) on the second beacon (if creating Mouse Events is supported)", function() { + if (window.cannotCreateMouseEvent) { + return this.skip(); + } + + var b = tf.beacons[1]; + + assert.isDefined(b["c.c"]); + assert.equal(parseInt(b["c.c"], 10), 10); + }); + + it("Should have sent the rage click count (c.c.r) on the second beacon (if creating Mouse Events is supported)", function() { + if (window.cannotCreateMouseEvent) { + return this.skip(); + } + + var b = tf.beacons[1]; + + assert.isDefined(b["c.c.r"]); + assert.equal(parseInt(b["c.c.r"], 10), 2); + }); + + it("Should have sent the log (c.l) on the second beacon (if creating Mouse Events is supported)", function() { + if (window.cannotCreateMouseEvent) { + return this.skip(); + } + + var b = tf.beacons[1]; + + assert.isDefined(b["c.l"]); + }); + + it("Should have logged the correct events (c.l) on the second beacon (if creating Mouse Events is supported)", function() { + if (window.cannotCreateMouseEvent) { + return this.skip(); + } + + var b = tf.beacons[1]; + + var clickLogs = BOOMR.plugins.Continuity.decompressLog(b["c.l"]).filter(function(obj) { + // LOG_TYPE_CLICK === 1 + return obj.type === 1; + }); + + assert.equal(clickLogs.length, 10); + + for (var i = 0; i < clickLogs.length; i++) { + // should all be x,y === 100 or 200 + var x = parseInt(clickLogs[i].x, 36); + var y = parseInt(clickLogs[i].y, 36); + + assert.isTrue(x === 100 || x === 200); + assert.isTrue(y === 100 || y === 200); + } + }); + + it("Should have the click timeline (c.t.click) on the second beacon (if creating Mouse Events is supported)", function() { + if (window.cannotCreateMouseEvent) { + return this.skip(); + } + + var b = tf.beacons[1]; + + assert.isDefined(b["c.t.click"]); + assert.operator(b["c.t.click"].length, ">=", 1); + }); + + it("Should have the interaction timeline (c.t.inter) on the second beacon (if creating Mouse Events is supported)", function() { + if (window.cannotCreateMouseEvent) { + return this.skip(); + } + + var b = tf.beacons[1]; + + assert.isDefined(b["c.t.inter"]); + assert.operator(b["c.t.inter"].length, ">=", 1); + }); + + it("Should have the Time to First Interaction (c.ttfi) on the first beacon (if creating Mouse Events is supported)", function() { + if (window.cannotCreateMouseEvent) { + return this.skip(); + } + + var b = tf.beacons[0]; + + assert.isDefined(b["c.ttfi"]); + assert.operator(parseInt(b["c.ttfi"], 10), ">=", 1); + }); + + it("Should not have the Time to First Interaction (c.ttfi) on the second beacon (if creating Mouse Events is supported)", function() { + if (window.cannotCreateMouseEvent) { + return this.skip(); + } + + var b = tf.beacons[1]; + + assert.isUndefined(b["c.ttfi"]); + }); + + it("Should not have Time to First Interaction (c.ttfi) on the third beacon (if creating Mouse Events is supported)", function() { + if (window.cannotCreateMouseEvent) { + return this.skip(); + } + + var b = tf.beacons[2]; + + assert.isUndefined(b["c.ttfi"]); + }); + + it("Should have last Continuity beacon time (c.lb) on the second beacon but not on the first", function() { + if (window.cannotCreateMouseEvent) { + return this.skip(); + } + + var b1 = tf.beacons[0]; + var b2 = tf.beacons[1]; + + // first beacon + assert.isUndefined(b1["c.lb"]); + + // second beacon + assert.isDefined(b2["c.lb"]); + assert.closeTo(parseInt(b2["c.lb"], 36), BOOMR.now(), 10000); + }); + + it("Should have First Input Delay (c.fid) on the first beacon (if creating Mouse Events is supported)", function() { + if (window.cannotCreateMouseEvent) { + return this.skip(); + } + + var b = tf.beacons[0]; + + assert.isDefined(b["c.fid"]); + assert.operator(parseInt(b["c.fid"], 10), ">=", 0); + }); + + it("Should not have First Input Delay (c.fid) on the second beacon (if creating Mouse Events is supported)", function() { + if (window.cannotCreateMouseEvent) { + return this.skip(); + } + + var b = tf.beacons[1]; + + assert.isUndefined(b["c.fid"]); + }); + + it("Should not have First Input Delay (c.fid) on the third beacon (if creating Mouse Events is supported)", function() { + if (window.cannotCreateMouseEvent) { + return this.skip(); + } + + var b = tf.beacons[2]; + + assert.isUndefined(b["c.fid"]); + }); +}); diff --git a/tests/page-templates/21-continuity/09-key.html b/tests/page-templates/21-continuity/09-key.html new file mode 100644 index 000000000..5579b3d02 --- /dev/null +++ b/tests/page-templates/21-continuity/09-key.html @@ -0,0 +1,22 @@ +<%= header %> +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/21-continuity/09-key.js b/tests/page-templates/21-continuity/09-key.js new file mode 100644 index 000000000..50bba45f4 --- /dev/null +++ b/tests/page-templates/21-continuity/09-key.js @@ -0,0 +1,73 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["ev"]); + +describe("e2e/21-continuity/09-key", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a single beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent the key count (c.k)", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["c.k"]); + assert.equal(parseInt(b["c.k"], 10), 3); + }); + + it("Should have sent the esc key count (c.k.e)", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["c.k.e"]); + assert.equal(parseInt(b["c.k.e"], 10), 3); + }); + + it("Should have sent the log (c.l)", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["c.l"]); + }); + + it("Should have logged the correct events (c.l)", function() { + var b = tf.lastBeacon(); + + var keyLogs = BOOMR.plugins.Continuity.decompressLog(b["c.l"]).filter(function(obj) { + // LOG_TYPE_KEY === 3 + return obj.type === 3; + }); + + assert.equal(keyLogs.length, 3); + }); + + it("Should have the key timeline (c.t.key)", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["c.t.key"]); + assert.operator(b["c.t.key"].length, ">=", 1); + }); + + it("Should have the interaction timeline (c.t.inter)", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["c.t.inter"]); + assert.operator(b["c.t.inter"].length, ">=", 1); + }); + + it("Should have the Time to First Interaction (c.ttfi)", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["c.ttfi"]); + assert.operator(parseInt(b["c.ttfi"], 10), ">=", 1); + }); + + it("Should have First Input Delay (c.fid)", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["c.fid"]); + assert.operator(parseInt(b["c.fid"], 10), ">=", 0); + }); +}); diff --git a/tests/page-templates/21-continuity/10-key-after-load.html b/tests/page-templates/21-continuity/10-key-after-load.html new file mode 100644 index 000000000..37be659ad --- /dev/null +++ b/tests/page-templates/21-continuity/10-key-after-load.html @@ -0,0 +1,37 @@ +<%= header %> +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/21-continuity/10-key-after-load.js b/tests/page-templates/21-continuity/10-key-after-load.js new file mode 100644 index 000000000..85d1bbf2c --- /dev/null +++ b/tests/page-templates/21-continuity/10-key-after-load.js @@ -0,0 +1,97 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["ev"]); + +describe("e2e/21-continuity/10-key-after-load", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a single beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent the key count (c.k)", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["c.k"]); + assert.equal(parseInt(b["c.k"], 10), 5); + }); + + it("Should have sent the esc key count (c.k.e)", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["c.k.e"]); + assert.equal(parseInt(b["c.k.e"], 10), 5); + }); + + it("Should have sent the log (c.l)", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["c.l"]); + }); + + it("Should have logged the correct events (c.l)", function() { + var b = tf.lastBeacon(); + + var keyLogs = BOOMR.plugins.Continuity.decompressLog(b["c.l"]).filter(function(obj) { + // LOG_TYPE_KEY === 3 + return obj.type === 3; + }); + + assert.equal(keyLogs.length, 5); + }); + + it("Should have the key timeline (c.t.key)", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["c.t.key"]); + assert.operator(b["c.t.key"].length, ">=", 1); + }); + + it("Should have the interaction timeline (c.t.inter)", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["c.t.inter"]); + assert.operator(b["c.t.inter"].length, ">=", 1); + }); + + it("Should have the Time to First Interaction on the first beacon (c.ttfi)", function() { + var b = tf.beacons[0]; + + assert.isDefined(b["c.ttfi"]); + assert.operator(parseInt(b["c.ttfi"], 10), ">=", 1); + }); + + it("Should not have the Time to First Interaction on the second beacon (c.ttfi)", function() { + var b = tf.lastBeacon(); + + assert.isUndefined(b["c.ttfi"]); + }); + + it("Should have last Continuity beacon time (c.lb) on the second beacon but not on the first", function() { + var b1 = tf.beacons[0]; + var b2 = tf.beacons[1]; + + // first beacon + assert.isUndefined(b1["c.lb"]); + + // second beacon + assert.isDefined(b2["c.lb"]); + assert.closeTo(parseInt(b2["c.lb"], 36), BOOMR.now(), 10000); + }); + + it("Should have First Input Delay (c.fid) on the first beacon", function() { + var b = tf.beacons[0]; + + assert.isDefined(b["c.fid"]); + assert.operator(parseInt(b["c.fid"], 10), ">=", 0); + }); + + it("Should not have First Input Delay (c.fid) on the second beacon", function() { + var b = tf.lastBeacon(); + + assert.isUndefined(b["c.fid"]); + }); +}); diff --git a/tests/page-templates/21-continuity/11-mouse.html b/tests/page-templates/21-continuity/11-mouse.html new file mode 100644 index 000000000..27ff92c1a --- /dev/null +++ b/tests/page-templates/21-continuity/11-mouse.html @@ -0,0 +1,29 @@ +<%= header %> +<%= boomerangScript %> + + + +<%= footer %> diff --git a/tests/page-templates/21-continuity/11-mouse.js b/tests/page-templates/21-continuity/11-mouse.js new file mode 100644 index 000000000..8af52a704 --- /dev/null +++ b/tests/page-templates/21-continuity/11-mouse.js @@ -0,0 +1,68 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["mouseMove"]); + +describe("e2e/21-continuity/11-mouse", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a single beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent the mouse percent (c.m.p)", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["c.m.p"]); + assert.operator(parseInt(b["c.m.p"], 10), ">=", 200); + }); + + it("Should have sent the mouse pixels (c.m.n)", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["c.m.n"]); + assert.operator(parseInt(b["c.m.n"], 10), ">=", 100); + }); + + it("Should have sent the log (c.l)", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["c.l"]); + }); + + it("Should have logged the correct events (c.l)", function() { + var b = tf.lastBeacon(); + + var mouseLogs = BOOMR.plugins.Continuity.decompressLog(b["c.l"]).filter(function(obj) { + // LOG_TYPE_MOUSE === 2 + return obj.type === 2; + }); + + assert.operator(mouseLogs.length, ">=", 1); + + for (var i = 0; i < mouseLogs.length; i++) { + // should all be x,y === 100 or 200 + var x = parseInt(mouseLogs[i].x, 36); + var y = parseInt(mouseLogs[i].y, 36); + + assert.operator(x, ">=", 100); + assert.operator(y, ">=", 100); + } + }); + + it("Should have the mouse timeline (c.t.mouse)", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["c.t.mouse"]); + assert.operator(b["c.t.mouse"].length, ">=", 1); + }); + + it("Should have the mouse percent timeline (c.t.mousepct)", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["c.t.mousepct"]); + assert.operator(b["c.t.mousepct"].length, ">=", 1); + }); +}); diff --git a/tests/page-templates/21-continuity/12-mouse-after-load.html b/tests/page-templates/21-continuity/12-mouse-after-load.html new file mode 100644 index 000000000..7068d1781 --- /dev/null +++ b/tests/page-templates/21-continuity/12-mouse-after-load.html @@ -0,0 +1,45 @@ +<%= header %> +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/21-continuity/12-mouse-after-load.js b/tests/page-templates/21-continuity/12-mouse-after-load.js new file mode 100644 index 000000000..008e2d76d --- /dev/null +++ b/tests/page-templates/21-continuity/12-mouse-after-load.js @@ -0,0 +1,80 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["mouseMove"]); + +describe("e2e/21-continuity/12-mouse-after-load", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a single beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent the mouse percent (c.m.p)", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["c.m.p"]); + assert.operator(parseInt(b["c.m.p"], 10), ">=", 300); + }); + + it("Should have sent the mouse pixels (c.m.n)", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["c.m.n"]); + assert.operator(parseInt(b["c.m.n"], 10), ">=", 100); + }); + + it("Should have sent the log (c.l)", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["c.l"]); + }); + + it("Should have logged the correct events (c.l)", function() { + var b = tf.lastBeacon(); + + var mouseLogs = BOOMR.plugins.Continuity.decompressLog(b["c.l"]).filter(function(obj) { + // LOG_TYPE_MOUSE === 2 + return obj.type === 2; + }); + + assert.operator(mouseLogs.length, ">=", 1); + + for (var i = 0; i < mouseLogs.length; i++) { + // should all be x,y === 100 or 200 + var x = parseInt(mouseLogs[i].x, 36); + var y = parseInt(mouseLogs[i].y, 36); + + assert.operator(x, ">=", 100); + assert.operator(y, ">=", 100); + } + }); + + it("Should have the mouse timeline (c.t.mouse)", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["c.t.mouse"]); + assert.operator(b["c.t.mouse"].length, ">=", 1); + }); + + it("Should have the mouse percent timeline (c.t.mousepct)", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["c.t.mousepct"]); + assert.operator(b["c.t.mousepct"].length, ">=", 1); + }); + + it("Should have last Continuity beacon time (c.lb) on the second beacon but not on the first", function() { + var b1 = tf.beacons[0]; + var b2 = tf.beacons[1]; + + // first beacon + assert.isUndefined(b1["c.lb"]); + + // second beacon + assert.isDefined(b2["c.lb"]); + assert.closeTo(parseInt(b2["c.lb"], 36), BOOMR.now(), 10000); + }); +}); diff --git a/tests/page-templates/21-continuity/13-interaction.html b/tests/page-templates/21-continuity/13-interaction.html new file mode 100644 index 000000000..9194c9b7e --- /dev/null +++ b/tests/page-templates/21-continuity/13-interaction.html @@ -0,0 +1,38 @@ +<%= header %> +<%= boomerangScript %> +

    13-interaction loading...

    + + + +<%= footer %> diff --git a/tests/page-templates/21-continuity/13-interaction.js b/tests/page-templates/21-continuity/13-interaction.js new file mode 100644 index 000000000..613d7ce96 --- /dev/null +++ b/tests/page-templates/21-continuity/13-interaction.js @@ -0,0 +1,64 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["ev", "ttfi"]); + +describe("e2e/21-continuity/13-interaction", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a single beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent the Time to First Interaction (c.ttfi)", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["c.ttfi"]); + var ttfi = parseInt(b["c.ttfi"], 10); + + if (t.isNavigationTimingSupported()) { + var st = parseInt(b["rt.tstart"], 10); + + assert.closeTo(st + ttfi, window.ttfi, 20); + } + else { + assert.operator(ttfi, ">=", 0); + } + }); + + it("Should have the interaction timeline (c.t.inter)", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["c.t.inter"]); + assert.operator(b["c.t.inter"].length, ">=", 1); + }); + + it("Could have the interaction delay timeline (c.t.interdly)", function() { + var b = tf.lastBeacon(); + + if (b["c.i.a"]) { + assert.isDefined(b["c.t.interdly"]); + assert.operator(b["c.t.interdly"].length, ">=", 1); + } + }); + + it("Could have interaction delay metrics (c.i.a, c.i.dc and c.i.dt)", function() { + var b = tf.lastBeacon(); + + if (!b["c.i.a"]) { + assert.operator(parseInt(b["c.i.a"], 10), ">=", 1); + assert.operator(parseInt(b["c.i.dc"], 10), ">=", 1); + assert.operator(parseInt(b["c.i.dc"], 10), "<=", 10); + assert.operator(parseInt(b["c.i.dt"], 10), ">=", 1); + } + }); + + it("Should have First Input Delay (c.fid)", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["c.fid"]); + assert.operator(parseInt(b["c.fid"], 10), ">=", 19); // we had a 20ms busy wait, allow for some fuzzing + }); +}); diff --git a/tests/page-templates/21-continuity/14-tti.html b/tests/page-templates/21-continuity/14-tti.html new file mode 100644 index 000000000..1ccfe1130 --- /dev/null +++ b/tests/page-templates/21-continuity/14-tti.html @@ -0,0 +1,17 @@ +<%= header %> +<%= continuitySnippet %> +<%= boomerangScript %> + +

    14-tti

    + + + + +<%= footer %> diff --git a/tests/page-templates/21-continuity/14-tti.js b/tests/page-templates/21-continuity/14-tti.js new file mode 100644 index 000000000..1b9296149 --- /dev/null +++ b/tests/page-templates/21-continuity/14-tti.js @@ -0,0 +1,61 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/21-continuity/14-tti", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a single beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have set the Visually Ready to First Paint or domContentLoadedEventEnd (c.tti.vr)", function() { + var b = tf.lastBeacon(); + + var vr = t.getFirstOrContentfulPaint(); + var p = window.performance; + + // domContentLoadedEventEnd + if (p && p.timing && p.timing.domContentLoadedEventEnd) { + vr = Math.max(vr, p.timing.domContentLoadedEventEnd); + } + + if (vr) { + var st = parseInt(b["rt.tstart"], 10); + + assert.isDefined(b["c.tti.vr"]); + assert.closeTo(parseInt(b["c.tti.vr"], 10), vr - st, 20); + } + }); + + it("Should have set the Time to Interactive (c.tti)", function() { + var b = tf.lastBeacon(); + + if (t.isNavigationTimingSupported()) { + var p = window.performance; + + assert.isDefined(b["c.tti"]); + + var tti = parseInt(b["c.tti"], 10); + + assert.operator(tti, ">=", p.timing.domContentLoadedEventEnd - p.timing.navigationStart); + assert.operator(tti, "<=", p.timing.loadEventStart - p.timing.navigationStart); + } + }); + + it("Should have set the Time to Interactive Method (c.tti.m)", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["c.tti.m"]); + + if (window.PerformanceLongTaskTiming) { + assert.equal(b["c.tti.m"], "lt"); + } + else if (window.requestAnimationFrame) { + assert.equal(b["c.tti.m"], "raf"); + } + else { + assert.equal(b["c.tti.m"], "b"); + } + }); +}); diff --git a/tests/page-templates/21-continuity/15-tti-multiple.html b/tests/page-templates/21-continuity/15-tti-multiple.html new file mode 100644 index 000000000..6bded017b --- /dev/null +++ b/tests/page-templates/21-continuity/15-tti-multiple.html @@ -0,0 +1,36 @@ +<%= header %> +<%= continuitySnippet %> +<%= boomerangScript %> + +

    15-tti-multiple

    + + + + +<%= footer %> diff --git a/tests/page-templates/21-continuity/15-tti-multiple.js b/tests/page-templates/21-continuity/15-tti-multiple.js new file mode 100644 index 000000000..100beea20 --- /dev/null +++ b/tests/page-templates/21-continuity/15-tti-multiple.js @@ -0,0 +1,65 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["workDone"]); + +describe("e2e/21-continuity/15-tti-multiple", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a single beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have set the Visually Ready to First Paint or domContentLoadedEventEnd (c.tti.vr)", function() { + var b = tf.lastBeacon(); + + var vr = t.getFirstOrContentfulPaint(); + var p = window.performance; + + // domContentLoadedEventEnd + if (p && p.timing && p.timing.domContentLoadedEventEnd) { + vr = Math.max(vr, p.timing.domContentLoadedEventEnd); + } + + if (vr) { + var st = parseInt(b["rt.tstart"], 10); + + assert.isDefined(b["c.tti.vr"]); + assert.closeTo(parseInt(b["c.tti.vr"], 10), vr - st, 20); + } + }); + + it("Should have set the Time to Interactive (c.tti)", function() { + var b = tf.lastBeacon(); + + if (t.isFirefox()) { + // TTI isn't as reliable on Firefox because it can't use LongTasks or Page Busy monitoring + return this.skip(); + } + + if (t.isNavigationTimingSupported()) { + var workDoneTs = window.workDone - performance.timing.navigationStart; + + assert.isDefined(b["c.tti"]); + assert.closeTo(parseInt(b["c.tti"], 10), workDoneTs, 200); + } + }); + + it("Should have set the Time to Interactive Method (c.tti.m)", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["c.tti.m"]); + + if (window.PerformanceLongTaskTiming) { + assert.equal(b["c.tti.m"], "lt"); + } + else if (window.requestAnimationFrame) { + assert.equal(b["c.tti.m"], "raf"); + } + else { + assert.equal(b["c.tti.m"], "b"); + } + }); +}); diff --git a/tests/page-templates/21-continuity/16-tti-framework-ready.html b/tests/page-templates/21-continuity/16-tti-framework-ready.html new file mode 100644 index 000000000..4476e5fed --- /dev/null +++ b/tests/page-templates/21-continuity/16-tti-framework-ready.html @@ -0,0 +1,32 @@ +<%= header %> +<%= continuitySnippet %> +<%= boomerangScript %> + +

    16-tti-framework-ready

    + + + + +<%= footer %> diff --git a/tests/page-templates/21-continuity/16-tti-framework-ready.js b/tests/page-templates/21-continuity/16-tti-framework-ready.js new file mode 100644 index 000000000..266edc525 --- /dev/null +++ b/tests/page-templates/21-continuity/16-tti-framework-ready.js @@ -0,0 +1,49 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["visuallyReady", "timeToInteractive"]); + +describe("e2e/21-continuity/16-tti-framework-ready", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a single beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have set the Visually Ready to when .frameworkReady() was called (c.tti.vr) (if NavigationTiming is supported)", function() { + if (t.isNavigationTimingSupported()) { + var b = tf.lastBeacon(); + + var vr = window.visuallyReady; + var st = parseInt(b["rt.tstart"], 10); + + assert.isDefined(b["c.tti.vr"]); + assert.closeTo(parseInt(b["c.tti.vr"], 10), vr - st, 20); + } + }); + + it("Should have set the Time to Interactive (c.tti) (if NavigationTiming is supported)", function() { + if (t.isNavigationTimingSupported()) { + var b = tf.lastBeacon(); + + var workDoneTs = window.timeToInteractive - performance.timing.navigationStart; + + assert.isDefined(b["c.tti"]); + assert.closeTo(parseInt(b["c.tti"], 10), workDoneTs, 200); + } + }); + + it("Should have set the Framework Ready (c.tti.fr) (if NavigationTiming is supported)", function() { + if (t.isNavigationTimingSupported()) { + var b = tf.lastBeacon(); + + var vr = window.visuallyReady; + var st = parseInt(b["rt.tstart"], 10); + + assert.isDefined(b["c.tti.fr"]); + assert.closeTo(parseInt(b["c.tti.fr"], 10), vr - st, 20); + } + }); +}); diff --git a/tests/page-templates/21-continuity/17-tti-hero-images.html b/tests/page-templates/21-continuity/17-tti-hero-images.html new file mode 100644 index 000000000..d2c7d9a3d --- /dev/null +++ b/tests/page-templates/21-continuity/17-tti-hero-images.html @@ -0,0 +1,31 @@ +<%= header %> +<%= continuitySnippet %> +<%= boomerangScript %> + +

    17-tti-hero-images

    + + + + + + + +<%= footer %> diff --git a/tests/page-templates/21-continuity/17-tti-hero-images.js b/tests/page-templates/21-continuity/17-tti-hero-images.js new file mode 100644 index 000000000..1509b4c1f --- /dev/null +++ b/tests/page-templates/21-continuity/17-tti-hero-images.js @@ -0,0 +1,51 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["lastHeroImageLoad", "heroImagesLoaded", "timeToInteractive"]); + +describe("e2e/21-continuity/17-tti-hero-images", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a single beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have set the Visually Ready to when the last Hero Image was loaded (c.tti.vr) (if ResourceTiming is supported)", function() { + if (t.isResourceTimingSupported()) { + var b = tf.lastBeacon(); + + var vr = window.heroImagesLoaded; + + var st = parseInt(b["rt.tstart"], 10); + + assert.isDefined(b["c.tti.vr"]); + assert.closeTo(parseInt(b["c.tti.vr"], 10), vr - st, 50); + } + }); + + it("Should have set the Time to Interactive (c.tti)", function() { + var b = tf.lastBeacon(); + + if (t.isNavigationTimingSupported()) { + var workDoneTs = window.timeToInteractive - performance.timing.navigationStart; + + assert.isDefined(b["c.tti"]); + assert.closeTo(parseInt(b["c.tti"], 10), workDoneTs, 200); + } + }); + + it("Should have set the Time to Hero Images (c.tti.hi)", function() { + if (t.isResourceTimingSupported()) { + var b = tf.lastBeacon(); + + var vr = window.heroImagesLoaded; + + var st = parseInt(b["rt.tstart"], 10); + + assert.isDefined(b["c.tti.hi"]); + assert.closeTo(parseInt(b["c.tti.hi"], 10), vr - st, 50); + } + }); +}); diff --git a/tests/page-templates/21-continuity/18-tti-wait-after-onload.html b/tests/page-templates/21-continuity/18-tti-wait-after-onload.html new file mode 100644 index 000000000..c24d0cf17 --- /dev/null +++ b/tests/page-templates/21-continuity/18-tti-wait-after-onload.html @@ -0,0 +1,35 @@ +<%= header %> +<%= continuitySnippet %> +<%= boomerangScript %> + +

    18-tti-wait-after-onload

    + + +<%= footer %> diff --git a/tests/page-templates/21-continuity/18-tti-wait-after-onload.js b/tests/page-templates/21-continuity/18-tti-wait-after-onload.js new file mode 100644 index 000000000..6b81a0330 --- /dev/null +++ b/tests/page-templates/21-continuity/18-tti-wait-after-onload.js @@ -0,0 +1,49 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["called", "workDone"]); + +describe("e2e/21-continuity/18-tti-wait-after-onload", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a single beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have set the Visually Ready to First Paint or domContentLoadedEventEnd (c.tti.vr)", function() { + var b = tf.lastBeacon(); + + var vr = t.getFirstOrContentfulPaint(); + var p = window.performance; + + // domContentLoadedEventEnd + if (p && p.timing && p.timing.domContentLoadedEventEnd) { + vr = Math.max(vr, p.timing.domContentLoadedEventEnd); + } + + if (vr) { + var st = parseInt(b["rt.tstart"], 10); + + assert.isDefined(b["c.tti.vr"]); + assert.closeTo(parseInt(b["c.tti.vr"], 10), vr - st, 20); + } + }); + + it("Should have set the Time to Interactive (c.tti)", function() { + var b = tf.lastBeacon(); + + if (t.isFirefox()) { + // TTI isn't as reliable on Firefox because it can't use LongTasks or Page Busy monitoring + return this.skip(); + } + + if (t.isNavigationTimingSupported()) { + var workDoneTs = window.workDone - performance.timing.navigationStart; + + assert.isDefined(b["c.tti"]); + assert.operator(parseInt(b["c.tti"], 10), ">=", workDoneTs - 150); + } + }); +}); diff --git a/tests/page-templates/21-continuity/19-scroll.html b/tests/page-templates/21-continuity/19-scroll.html new file mode 100644 index 000000000..96c680743 --- /dev/null +++ b/tests/page-templates/21-continuity/19-scroll.html @@ -0,0 +1,39 @@ +<%= header %> +<%= boomerangScript %> + + + +<%= footer %> diff --git a/tests/page-templates/21-continuity/19-scroll.js b/tests/page-templates/21-continuity/19-scroll.js new file mode 100644 index 000000000..4f88413ee --- /dev/null +++ b/tests/page-templates/21-continuity/19-scroll.js @@ -0,0 +1,96 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["onLoad"]); + +describe("e2e/21-continuity/19-scroll", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a single beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent the scroll count (c.s)", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["c.s"]); + assert.equal(parseInt(b["c.s"], 10), 2); + }); + + it("Should have sent the scroll Y (pixels) (c.s.y)", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["c.s.y"]); + assert.operator(parseInt(b["c.s.y"], 10), ">", 0); + assert.operator(parseInt(b["c.s.y"], 10), "<=", 20000); + assert.closeTo(parseInt(b["c.s.y"], 10), 18000, 2000); + }); + + it("Should have sent the scroll percentage (c.s.p)", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["c.s.p"]); + assert.closeTo(parseInt(b["c.s.p"], 10), 200, 5); + }); + + it("Should have sent the distinct scrolls (c.s.d)", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["c.s.d"]); + assert.equal(parseInt(b["c.s.d"], 10), 1); + }); + + it("Should have sent the log (c.l)", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["c.l"]); + }); + + it("Should have logged the correct events (c.l)", function() { + var b = tf.lastBeacon(); + + var scrollLogs = BOOMR.plugins.Continuity.decompressLog(b["c.l"]).filter(function(obj) { + // LOG_TYPE_SCROLL === 0 + return obj.type === 0; + }); + + assert.equal(scrollLogs.length, 2); + + assert.operator(parseInt(scrollLogs[0].y, 36), ">=", 100); + assert.equal(parseInt(scrollLogs[1].y, 36), 0); + }); + + it("Should have the scroll timeline (c.t.scroll)", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["c.t.scroll"]); + assert.operator(b["c.t.scroll"].length, ">=", 1); + }); + + it("Should have the scroll percent timeline (c.t.scrollpct)", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["c.t.scrollpct"]); + assert.operator(b["c.t.scrollpct"].length, ">=", 1); + }); + + it("Should not have the interaction timeline (c.t.inter)", function() { + var b = tf.lastBeacon(); + + assert.isUndefined(b["c.t.inter"]); + }); + + it("Should not have the Time to First Interaction (c.ttfi)", function() { + var b = tf.lastBeacon(); + + assert.isUndefined(b["c.ttfi"]); + }); + + it("Should not have First Input Delay (c.fid)", function() { + var b = tf.lastBeacon(); + + assert.isUndefined(b["c.fid"]); + }); +}); diff --git a/tests/page-templates/21-continuity/20-scroll-after-load.html b/tests/page-templates/21-continuity/20-scroll-after-load.html new file mode 100644 index 000000000..eb631dbaf --- /dev/null +++ b/tests/page-templates/21-continuity/20-scroll-after-load.html @@ -0,0 +1,39 @@ +<%= header %> +<%= boomerangScript %> + + + +<%= footer %> diff --git a/tests/page-templates/21-continuity/20-scroll-after-load.js b/tests/page-templates/21-continuity/20-scroll-after-load.js new file mode 100644 index 000000000..456274ae8 --- /dev/null +++ b/tests/page-templates/21-continuity/20-scroll-after-load.js @@ -0,0 +1,145 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/21-continuity/20-scroll-after-load", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent two beacons", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 2); + }); + + it("Should have not sent the scroll count (c.s) on the first beacon", function() { + var b = tf.beacons[0]; + + assert.isUndefined(b["c.s"]); + }); + + it("Should have not sent the scroll Y (pixels) (c.s.y) on the first beacon", function() { + var b = tf.beacons[0]; + + assert.isUndefined(b["c.s.y"]); + }); + + it("Should have not sent the scroll percentage (c.s.p) on the first beacon", function() { + var b = tf.beacons[0]; + + assert.isUndefined(b["c.s.p"]); + }); + + it("Should have not sent the distinct scrolls (c.s.d) on the first beacon", function() { + var b = tf.beacons[0]; + + assert.isUndefined(b["c.s.d"]); + }); + + it("Should have sent the scroll count (c.s) on the second beacon", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["c.s"]); + assert.equal(parseInt(b["c.s"], 10), 4); + }); + + it("Should have sent the scroll Y (pixels) (c.s.y) on the second beacon", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["c.s.y"]); + assert.operator(parseInt(b["c.s.y"], 10), ">", 0); + assert.operator(parseInt(b["c.s.y"], 10), "<=", 40000); + assert.closeTo(parseInt(b["c.s.y"], 10), 36000, 4000); + }); + + it("Should have sent the scroll percentage (c.s.p) on the second beacon", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["c.s.p"]); + assert.closeTo(parseInt(b["c.s.p"], 10), 400, 5); + }); + + it("Should have sent the distinct scrolls (c.s.d) on the second beacon", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["c.s.d"]); + assert.equal(parseInt(b["c.s.d"], 10), 2); + }); + + it("Should have sent the log (c.l) on the second beacon", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["c.l"]); + }); + + it("Should have logged the correct events (c.l) on the second beacon", function() { + var b = tf.lastBeacon(); + + var scrollLogs = BOOMR.plugins.Continuity.decompressLog(b["c.l"]).filter(function(obj) { + // LOG_TYPE_SCROLL === 0 + return obj.type === 0; + }); + + assert.equal(scrollLogs.length, 4); + + assert.operator(parseInt(scrollLogs[0].y, 36), ">=", 100); + assert.equal(parseInt(scrollLogs[1].y, 36), 0); + assert.operator(parseInt(scrollLogs[2].y, 36), ">=", 100); + assert.equal(parseInt(scrollLogs[3].y, 36), 0); + }); + + it("Should have the scroll timeline (c.t.scroll) on the second beacon", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["c.t.scroll"]); + assert.operator(b["c.t.scroll"].length, ">=", 1); + }); + + it("Should have the scroll percent timeline (c.t.scrollpct) on the second beacon", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["c.t.scrollpct"]); + assert.operator(b["c.t.scrollpct"].length, ">=", 1); + }); + + it("Should not have the interaction timeline (c.t.inter) on the second beacon", function() { + var b = tf.lastBeacon(); + + assert.isUndefined(b["c.t.inter"]); + }); + + it("Should not have the Time to First Interaction (c.ttfi) on the second beacon", function() { + var b = tf.lastBeacon(); + + assert.isUndefined(b["c.ttfi"]); + }); + + it("Should the same Continuity Epoch on the second beacon", function() { + var b1 = tf.beacons[0]; + var b2 = tf.beacons[1]; + + assert.equal(b1["c.e"], b2["c.e"]); + }); + + it("Should have last Continuity beacon time (c.lb) on the second beacon but not on the first", function() { + var b1 = tf.beacons[0]; + var b2 = tf.beacons[1]; + + // first beacon + assert.isUndefined(b1["c.lb"]); + + // second beacon + assert.isDefined(b2["c.lb"]); + assert.closeTo(parseInt(b2["c.lb"], 36), BOOMR.now(), 10000); + }); + + it("Should not have First Input Delay (c.fid) on the first beacon", function() { + var b = tf.beacons[0]; + + assert.isUndefined(b["c.fid"]); + }); + + it("Should not have First Input Delay (c.fid) on the second beacon", function() { + var b = tf.lastBeacon(); + + assert.isUndefined(b["c.fid"]); + }); +}); diff --git a/tests/page-templates/21-continuity/21-dom.html b/tests/page-templates/21-continuity/21-dom.html new file mode 100644 index 000000000..474a9a6c7 --- /dev/null +++ b/tests/page-templates/21-continuity/21-dom.html @@ -0,0 +1,29 @@ +<%= header %> +<%= boomerangScript %> + + +
    + + +<%= footer %> diff --git a/tests/page-templates/21-continuity/21-dom.js b/tests/page-templates/21-continuity/21-dom.js new file mode 100644 index 000000000..ca5e80ecc --- /dev/null +++ b/tests/page-templates/21-continuity/21-dom.js @@ -0,0 +1,77 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["mutInterval"]); + +describe("e2e/21-continuity/21-dom", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a single beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have the DOM size timeline (c.t.domsz)", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["c.t.domsz"]); + assert.operator(b["c.t.domsz"].length, ">=", 1); + + var buckets = BOOMR.plugins.Continuity.decompressBucketLog(b["c.t.domsz"]); + + // verify each grows + var last = 0; + + for (var i = 0; i < buckets.length; i++) { + if (buckets[i] === 0) { + // not reported + continue; + } + + assert.operator(buckets[i], ">=", last); + last = buckets[i]; + } + }); + + it("Should have the DOM length timeline (c.t.domln)", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["c.t.domln"]); + assert.operator(b["c.t.domln"].length, ">=", 1); + + var buckets = BOOMR.plugins.Continuity.decompressBucketLog(b["c.t.domln"]); + + // verify each grows + var last = 0; + + for (var i = 0; i < buckets.length; i++) { + if (buckets[i] === 0) { + // not reported + continue; + } + + assert.operator(buckets[i], ">=", last); + last = buckets[i]; + } + }); + + it("Should have the DOM mutation timeline (c.t.mut) (if MutationObserver is supported)", function() { + if (!t.isMutationObserverSupported()) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isDefined(b["c.t.mut"]); + assert.operator(b["c.t.mut"].length, ">=", 1); + + var buckets = BOOMR.plugins.Continuity.decompressBucketLog(b["c.t.mut"]); + + // verify each is 0 or less than 100 + for (var i = 0; i < buckets.length; i++) { + assert.operator(buckets[i], ">=", 0); + assert.operator(buckets[i], "<=", 100); + } + }); +}); diff --git a/tests/page-templates/21-continuity/22-page-busy-full.html b/tests/page-templates/21-continuity/22-page-busy-full.html new file mode 100644 index 000000000..a6909fc60 --- /dev/null +++ b/tests/page-templates/21-continuity/22-page-busy-full.html @@ -0,0 +1,20 @@ +<%= header %> +<%= boomerangScript %> + + + +<%= footer %> diff --git a/tests/page-templates/21-continuity/22-page-busy-full.js b/tests/page-templates/21-continuity/22-page-busy-full.js new file mode 100644 index 000000000..cdff9d3b1 --- /dev/null +++ b/tests/page-templates/21-continuity/22-page-busy-full.js @@ -0,0 +1,75 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/21-continuity/22-page-busy-full", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a single beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have set the Page Busy (c.b)", function() { + var b = tf.lastBeacon(); + + if (t.isFirefox()) { + assert.isUndefined(b["c.b"]); + } + else { + assert.isDefined(b["c.b"]); + } + }); + + it("Should have set the Page Busy percentage to over 90% (c.b)", function() { + var b = tf.lastBeacon(); + + if (t.isFirefox()) { + assert.isUndefined(b["c.b"]); + } + else { + assert.operator(parseInt(b["c.b"], 10), ">=", 90); + } + }); + + it("Should have the Page Busy timeline (c.t.busy)", function() { + var b = tf.lastBeacon(); + + if (t.isFirefox()) { + assert.isUndefined(b["c.t.busy"]); + } + else { + assert.isDefined(b["c.t.busy"]); + assert.operator(b["c.t.busy"].length, ">=", 1); + } + }); + + it("Should be able to calculate Page Busy (c.b) from the timeline (c.t.busy)", function() { + if (t.isFirefox()) { + // Firefox does not support Page Busy + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isDefined(b["c.t.busy"]); + + var buckets = BOOMR.plugins.Continuity.decompressBucketLog(b["c.t.busy"]); + + // count buckets + var busyCount = 0; + + for (var i = 0; i < buckets.length; i++) { + if (buckets[i] === 0) { + // not reported + continue; + } + + busyCount++; + } + + // calculation is within 10% of c.b + var calcBusy = Math.round(busyCount / buckets.length * 100); + + assert.closeTo(parseInt(b["c.b"], 10), calcBusy, 10); + }); +}); diff --git a/tests/page-templates/21-continuity/23-page-busy-partial.html b/tests/page-templates/21-continuity/23-page-busy-partial.html new file mode 100644 index 000000000..9bddab229 --- /dev/null +++ b/tests/page-templates/21-continuity/23-page-busy-partial.html @@ -0,0 +1,30 @@ +<%= header %> +<%= boomerangScript %> + + + +<%= footer %> diff --git a/tests/page-templates/21-continuity/23-page-busy-partial.js b/tests/page-templates/21-continuity/23-page-busy-partial.js new file mode 100644 index 000000000..366f1a948 --- /dev/null +++ b/tests/page-templates/21-continuity/23-page-busy-partial.js @@ -0,0 +1,73 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/21-continuity/23-page-busy-partial", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a single beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have set the Page Busy (c.b)", function() { + var b = tf.lastBeacon(); + + if (t.isFirefox()) { + assert.isUndefined(b["c.b"]); + } + else { + assert.isDefined(b["c.b"]); + } + }); + + it("Should have set the Page Busy percentage", function() { + var b = tf.lastBeacon(); + + if (t.isFirefox()) { + assert.isUndefined(b["c.b"]); + } + else { + assert.operator(parseInt(b["c.b"], 10), ">=", 0); + assert.operator(parseInt(b["c.b"], 10), "<=", 100); + } + }); + + it("Should have the Page Busy timeline (c.t.busy)", function() { + var b = tf.lastBeacon(); + + if (t.isFirefox()) { + assert.isUndefined(b["c.t.busy"]); + } + else { + assert.isDefined(b["c.t.busy"]); + assert.operator(b["c.t.busy"].length, ">=", 1); + } + }); + + it("Should be able to calculate Page Busy (c.b) from the timeline (c.t.busy)", function() { + if (t.isFirefox()) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isDefined(b["c.t.busy"]); + + var buckets = BOOMR.plugins.Continuity.decompressBucketLog(b["c.t.busy"]); + + // count buckets + var busyCount = 0; + + for (var i = 0; i < buckets.length; i++) { + // add the partial percentage of busy + busyCount += buckets[i] / 100; + } + + // calculation is within 20% of c.b - we can't be exact, as the actual + // c.b is calculated from each individual poll while c.t.busy has a + // summarized timeline of each 100ms + var calcBusy = Math.round(busyCount / buckets.length * 100); + + assert.closeTo(parseInt(b["c.b"], 10), calcBusy, 20); + }); +}); diff --git a/tests/page-templates/21-continuity/24-vis.html b/tests/page-templates/21-continuity/24-vis.html new file mode 100644 index 000000000..bdc22c7cf --- /dev/null +++ b/tests/page-templates/21-continuity/24-vis.html @@ -0,0 +1,35 @@ +<%= header %> +<%= boomerangScript %> + + + +<%= footer %> diff --git a/tests/page-templates/21-continuity/24-vis.js b/tests/page-templates/21-continuity/24-vis.js new file mode 100644 index 000000000..732359da3 --- /dev/null +++ b/tests/page-templates/21-continuity/24-vis.js @@ -0,0 +1,46 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/21-continuity/24-vis", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a single beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent the log (c.l)", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["c.l"]); + }); + + it("Should have logged the correct visibility events (c.l)", function() { + var b = tf.lastBeacon(); + + var logs = BOOMR.plugins.Continuity.decompressLog(b["c.l"]).filter(function(obj) { + // LOG_TYPE_VIS === 4 + return obj.type === 4; + }); + + assert.equal(logs.length, 4); + + // prerender + assert.equal(logs[0].s, 2); + + // visible + assert.equal(logs[1].s, 0); + + // hidden + assert.equal(logs[2].s, 1); + + // visible + assert.equal(logs[3].s, 0); + }); + + it("Should have not sent FID metric for visibility change interaction (c.fid)", function() { + var b = tf.lastBeacon(); + + assert.isUndefined(b["c.fid"], "c.fid should not have been present"); + }); +}); diff --git a/tests/page-templates/21-continuity/25-orn.html b/tests/page-templates/21-continuity/25-orn.html new file mode 100644 index 000000000..aa56c031a --- /dev/null +++ b/tests/page-templates/21-continuity/25-orn.html @@ -0,0 +1,34 @@ +<%= header %> +<%= boomerangScript %> + + + +<%= footer %> diff --git a/tests/page-templates/21-continuity/25-orn.js b/tests/page-templates/21-continuity/25-orn.js new file mode 100644 index 000000000..89b977ac8 --- /dev/null +++ b/tests/page-templates/21-continuity/25-orn.js @@ -0,0 +1,41 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["orientationChange", "orientation"]); + +describe("e2e/21-continuity/25-orn", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a single beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent the log (c.l)", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["c.l"]); + }); + + it("Should have logged the correct orientation events (c.l)", function() { + var b = tf.lastBeacon(); + + var logs = BOOMR.plugins.Continuity.decompressLog(b["c.l"]).filter(function(obj) { + // LOG_TYPE_ORN === 5 + return obj.type === 5; + }); + + assert.equal(logs.length, 3); + + assert.equal(parseInt(logs[0].a, 36), 0); + assert.equal(parseInt(logs[1].a, 36), 90); + assert.equal(parseInt(logs[2].a, 36), -90); + }); + + it("Should have not sent FID metric for orientation interaction (c.fid)", function() { + var b = tf.lastBeacon(); + + assert.isUndefined(b["c.fid"], "c.fid should not have been present"); + }); +}); diff --git a/tests/page-templates/21-continuity/26-mem.html b/tests/page-templates/21-continuity/26-mem.html new file mode 100644 index 000000000..3965b7739 --- /dev/null +++ b/tests/page-templates/21-continuity/26-mem.html @@ -0,0 +1,13 @@ +<%= header %> +<%= boomerangScript %> + + + +<%= footer %> diff --git a/tests/page-templates/21-continuity/26-mem.js b/tests/page-templates/21-continuity/26-mem.js new file mode 100644 index 000000000..1753cd220 --- /dev/null +++ b/tests/page-templates/21-continuity/26-mem.js @@ -0,0 +1,36 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/21-continuity/26-mem", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a single beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have the memory timeline (c.t.mem) (if supported)", function() { + var b = tf.lastBeacon(); + + if (!b["c.t.mem"]) { + return this.skip(); + } + + assert.isDefined(b["c.t.mem"]); + + var buckets = BOOMR.plugins.Continuity.decompressBucketLog(b["c.t.mem"]); + + var min = 100000; + + for (var i = 0; i < buckets.length; i++) { + if (buckets[i] === 0) { + // not reported + continue; + } + + // assume memory will only grow during this test + assert.isTrue(buckets[i] >= min); + min = buckets[i]; + } + }); +}); diff --git a/tests/page-templates/21-continuity/27-bat.html b/tests/page-templates/21-continuity/27-bat.html new file mode 100644 index 000000000..3b448947d --- /dev/null +++ b/tests/page-templates/21-continuity/27-bat.html @@ -0,0 +1,48 @@ +<%= header %> + +<%= boomerangScript %> + + + +<%= footer %> diff --git a/tests/page-templates/21-continuity/27-bat.js b/tests/page-templates/21-continuity/27-bat.js new file mode 100644 index 000000000..5d9e37a3b --- /dev/null +++ b/tests/page-templates/21-continuity/27-bat.js @@ -0,0 +1,40 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["battery"]); + +describe("e2e/21-continuity/27-bat", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a single beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have the battery timeline (c.t.bat) (if supported)", function() { + var b = tf.lastBeacon(); + + if (!b["c.t.bat"]) { + return this.skip(); + } + + assert.isDefined(b["c.t.bat"]); + + var buckets = BOOMR.plugins.Continuity.decompressBucketLog(b["c.t.bat"]); + + // starts at 50 + var min = 50; + + for (var i = 0; i < buckets.length; i++) { + if (buckets[i] === 0) { + // not reported + continue; + } + + // assume always growing + assert.operator(buckets[i], ">=", min); + min = buckets[i]; + } + }); +}); diff --git a/tests/page-templates/21-continuity/28-after-onload.html b/tests/page-templates/21-continuity/28-after-onload.html new file mode 100644 index 000000000..cdbaf357a --- /dev/null +++ b/tests/page-templates/21-continuity/28-after-onload.html @@ -0,0 +1,35 @@ +<%= header %> +<%= boomerangScript %> + + + +<%= footer %> diff --git a/tests/page-templates/21-continuity/28-after-onload.js b/tests/page-templates/21-continuity/28-after-onload.js new file mode 100644 index 000000000..1ae3b5b9c --- /dev/null +++ b/tests/page-templates/21-continuity/28-after-onload.js @@ -0,0 +1,252 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["ev"]); + +describe("e2e/21-continuity/28-after-onload", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent three beacons", function(done) { + t.ensureBeaconCount(done, 3); + }); + + // + // Beacon types + // + it("Should have the first beacon be a Page Load beacon", function() { + var b = tf.beacons[0]; + + assert.isUndefined(b["http.initiator"]); + }); + + it("Should have the second beacon be an Interaction beacon", function() { + var b = tf.beacons[1]; + + assert.equal(b["http.initiator"], "interaction"); + }); + + it("Should have the third beacon be an Interaction beacon", function() { + var b = tf.beacons[2]; + + assert.equal(b["http.initiator"], "interaction"); + }); + + // + // Second beacon (first Interaction beacon) data + // + it("Should have the Continuity Key Count of (c.k) of 2 on the second beacon", function() { + var b = tf.beacons[1]; + + assert.equal(b["c.k"], 2); + }); + + it("Should have the Continuity Epoch (c.e) as the start of the Page Load beacon on the second beacon (if NavigationTiming is supported)", function() { + if (!t.isNavigationTimingSupported()) { + return this.skip(); + } + + var b1 = tf.beacons[0]; + var b2 = tf.beacons[1]; + + var firstBeaconEnd = parseInt(b1["rt.tstart"], 10); + var secondBeaconEpoch = parseInt(b2["c.e"], 36); + + assert.equal(secondBeaconEpoch, firstBeaconEnd); + }); + + it("Should have the Continuity Last Beacon (c.e) after the end of the Page Load beacon on the second beacon", function() { + var b1 = tf.beacons[0]; + var b2 = tf.beacons[1]; + + var firstBeaconEnd = parseInt(b1["rt.end"], 10); + var secondBeaconLastBeacon = parseInt(b2["c.lb"], 36); + + assert.operator(secondBeaconLastBeacon, ">=", firstBeaconEnd); + }); + + it("Should have the Continuity Log (c.l) on the second beacon", function() { + var b = tf.beacons[1]; + + assert.isDefined(b["c.l"]); + }); + + it("Should have the correct Continuity Log events (c.l) on the second beacon", function() { + var b = tf.beacons[1]; + + var keyLogs = BOOMR.plugins.Continuity.decompressLog(b["c.l"]).filter(function(obj) { + // LOG_TYPE_KEY === 3 + return obj.type === 3; + }); + + assert.equal(keyLogs.length, 2); + }); + + it("Should have the Continuity Key Timeline (c.t.key) on the second beacon", function() { + var b = tf.beacons[1]; + + assert.isDefined(b["c.t.key"]); + assert.operator(b["c.t.key"].length, ">=", 1); + }); + + it("Should have the Continuity interaction timeline (c.t.inter) on the second beacon", function() { + var b = tf.beacons[1]; + + assert.isDefined(b["c.t.inter"]); + assert.operator(b["c.t.inter"].length, ">=", 1); + }); + + it("Should have the Continuity Time to First Interaction (c.ttfi) on the second beacon", function() { + var b = tf.beacons[1]; + + assert.isDefined(b["c.ttfi"]); + assert.operator(parseInt(b["c.ttfi"], 10), ">=", 1); + }); + + it("Should have the Continuity Start (rt.tstart) on the second beacon (if NavigationTiming is supported)", function() { + if (!t.isNavigationTimingSupported()) { + return this.skip(); + } + + var b1 = tf.beacons[0]; + var b2 = tf.beacons[1]; + + var firstBeaconEnd = parseInt(b1["rt.end"], 10); + var secondBeaconStart = parseInt(b2["rt.tstart"], 10); + + assert.operator(secondBeaconStart, ">=", firstBeaconEnd); + }); + + it("Should have the Continuity End (rt.end) on the second beacon", function() { + var b1 = tf.beacons[0]; + var b2 = tf.beacons[1]; + + var firstBeaconEnd = parseInt(b1["rt.end"], 10); + var secondBeaconStart = parseInt(b2["rt.tstart"], 10); + var secondBeaconEnd = parseInt(b2["rt.end"], 10); + + assert.operator(secondBeaconEnd, ">=", firstBeaconEnd); + assert.operator(secondBeaconEnd, ">=", secondBeaconStart); + }); + + // + // Third beacon (second Interaction beacon) data + // + it("Should have the Continuity Key Count of (c.k) of 1 on the third beacon", function() { + var b = tf.beacons[2]; + + assert.equal(b["c.k"], 1); + }); + + it("Should have the Continuity Epoch (c.e) as the start of the Page Load beacon on the third beacon (if NavigationTiming is supported)", function() { + if (!t.isNavigationTimingSupported()) { + return this.skip(); + } + + var b1 = tf.beacons[0]; + var b3 = tf.beacons[2]; + + var firstBeaconEnd = parseInt(b1["rt.tstart"], 10); + var thirdBeaconEpoch = parseInt(b3["c.e"], 36); + + assert.equal(thirdBeaconEpoch, firstBeaconEnd); + }); + + it("Should have the Continuity Frame Duration (c.f.d) on the third beacon larger than the second beacon", function() { + var b2 = tf.beacons[1]; + var b3 = tf.beacons[2]; + + if (!b2["c.f.d"] || !b3["c.f.d"]) { + return this.skip(); + } + + var second = parseInt(b2["c.f.d"], 10); + var third = parseInt(b3["c.f.d"], 10); + + assert.operator(third, ">", second); + }); + + it("Should have the Continuity Frame Start (c.f.s) on the third beacon larger than the second beacon", function() { + var b2 = tf.beacons[1]; + var b3 = tf.beacons[2]; + + if (!b2["c.f.s"] || !b3["c.f.s"]) { + return this.skip(); + } + + var second = parseInt(b2["c.f.s"], 36); + var third = parseInt(b3["c.f.s"], 36); + + assert.operator(third, ">", second); + }); + + it("Should have the Continuity Last Beacon (c.e) after the end of the second beacon on the third beacon", function() { + var b1 = tf.beacons[0]; + var b3 = tf.beacons[2]; + + var secondBeaconEnd = parseInt(b1["rt.end"], 10); + var thirdBeaconLastBeacon = parseInt(b3["c.lb"], 36); + + assert.operator(thirdBeaconLastBeacon, ">=", secondBeaconEnd); + }); + + it("Should have the Continuity Log (c.l) on the third beacon", function() { + var b = tf.beacons[2]; + + assert.isDefined(b["c.l"]); + }); + + it("Should have the correct Continuity Log events (c.l) on the third beacon", function() { + var b = tf.beacons[2]; + + var keyLogs = BOOMR.plugins.Continuity.decompressLog(b["c.l"]).filter(function(obj) { + // LOG_TYPE_KEY === 3 + return obj.type === 3; + }); + + assert.equal(keyLogs.length, 1); + }); + + it("Should have the Continuity Key Timeline (c.t.key) on the third beacon", function() { + var b = tf.beacons[2]; + + assert.isDefined(b["c.t.key"]); + assert.operator(b["c.t.key"].length, ">=", 1); + }); + + it("Should have the Continuity interaction timeline (c.t.inter) on the third beacon", function() { + var b = tf.beacons[2]; + + assert.isDefined(b["c.t.inter"]); + assert.operator(b["c.t.inter"].length, ">=", 1); + }); + + it("Should not have the Continuity Time to First Interaction (c.ttfi) on the third beacon", function() { + var b3 = tf.beacons[2]; + + assert.isUndefined(b3["c.ttfi"]); + }); + + it("Should have the Continuity Start (rt.tstart) on the third beacon", function() { + var b2 = tf.beacons[1]; + var b3 = tf.beacons[2]; + + var secondBeaconEnd = parseInt(b2["rt.end"], 10); + var thirdBeaconStart = parseInt(b3["rt.tstart"], 10); + + assert.operator(thirdBeaconStart, ">=", secondBeaconEnd); + }); + + it("Should have the Continuity End (rt.end) on the third beacon", function() { + var b2 = tf.beacons[1]; + var b3 = tf.beacons[2]; + + var secondBeaconEnd = parseInt(b2["rt.end"], 10); + var thirdBeaconStart = parseInt(b3["rt.tstart"], 10); + var thirdBeaconEnd = parseInt(b3["rt.end"], 10); + + assert.operator(thirdBeaconEnd, ">=", secondBeaconEnd); + assert.operator(thirdBeaconEnd, ">=", thirdBeaconStart); + }); +}); diff --git a/tests/page-templates/21-continuity/29-timeline.html b/tests/page-templates/21-continuity/29-timeline.html new file mode 100644 index 000000000..18c6fd87e --- /dev/null +++ b/tests/page-templates/21-continuity/29-timeline.html @@ -0,0 +1,39 @@ +<%= header %> +<%= boomerangScript %> + + + +
    +<%= footer %> diff --git a/tests/page-templates/21-continuity/29-timeline.js b/tests/page-templates/21-continuity/29-timeline.js new file mode 100644 index 000000000..8dd226223 --- /dev/null +++ b/tests/page-templates/21-continuity/29-timeline.js @@ -0,0 +1,53 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["ev"]); + +describe("e2e/21-continuity/29-timeline", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent two beacons", function(done) { + t.ensureBeaconCount(done, 2); + }); + + // + // Beacon types + // + it("Should have the first beacon be a Page Load beacon", function() { + var b = tf.beacons[0]; + + assert.isUndefined(b["http.initiator"]); + }); + + it("Should have the second beacon be an Interaction beacon", function() { + var b = tf.beacons[1]; + + assert.equal(b["http.initiator"], "interaction"); + }); + + // + // Second beacon (first Interaction beacon) data + // + it("Should have the Continuity DOM Size Timeline (c.t.domsz) on the second beacon", function() { + var b = tf.beacons[1]; + + assert.isDefined(b["c.t.domsz"]); + assert.operator(b["c.t.domsz"].length, ">=", 1); + }); + + it("Should have an ever-increasing Continuity DOM Size timeline (c.t.domsz) on the second beacon", function() { + var b = tf.beacons[1]; + + var buckets = BOOMR.plugins.Continuity.decompressBucketLog(b["c.t.domsz"]); + + // verify each grows + var last = 0; + + for (var i = 0; i < buckets.length; i++) { + assert.operator(buckets[i], ">=", last); + last = buckets[i]; + } + }); +}); diff --git a/tests/page-templates/21-continuity/30-tti-busy.html b/tests/page-templates/21-continuity/30-tti-busy.html new file mode 100644 index 000000000..488300df8 --- /dev/null +++ b/tests/page-templates/21-continuity/30-tti-busy.html @@ -0,0 +1,43 @@ +<%= header %> +<%= continuitySnippet %> +<%= boomerangScript %> + +

    30-tti-busy

    + + + + +<%= footer %> diff --git a/tests/page-templates/21-continuity/30-tti-busy.js b/tests/page-templates/21-continuity/30-tti-busy.js new file mode 100644 index 000000000..e0e297b4e --- /dev/null +++ b/tests/page-templates/21-continuity/30-tti-busy.js @@ -0,0 +1,72 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["workStart", "workDone"]); + +describe("e2e/21-continuity/30-tti-busy", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a single beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have set the Visually Ready to First Paint or domContentLoadedEventEnd (c.tti.vr)", function() { + var b = tf.lastBeacon(); + + var vr = t.getFirstOrContentfulPaint(); + var p = window.performance; + + // domContentLoadedEventEnd + if (p && p.timing && p.timing.domContentLoadedEventEnd) { + vr = Math.max(vr, p.timing.domContentLoadedEventEnd); + } + + if (vr) { + var st = parseInt(b["rt.tstart"], 10); + + assert.isDefined(b["c.tti.vr"]); + assert.closeTo(parseInt(b["c.tti.vr"], 10), vr - st, 20); + } + }); + + it("Should have set the Time to Interactive (c.tti)", function() { + var b = tf.lastBeacon(); + + if (t.isFirefox()) { + // no TTI method was available (e.g. Firefox without FPS/LongTask/Busy) + return this.skip(); + } + + if (t.isNavigationTimingSupported()) { + var workDoneTs = window.workDone - performance.timing.navigationStart; + var workStartTs = window.workStart - performance.timing.navigationStart; + + assert.isDefined(b["c.tti"]); + assert.operator(parseInt(b["c.tti"], 10), ">=", workStartTs); + + // the interval should end around the same time as workDoneTs, plus some rounding + assert.operator(parseInt(b["c.tti"], 10), ">=", workDoneTs - 200); + + // should be minimum bound by TTVR + var minTTI = Math.max(t.getFirstOrContentfulPaint() - performance.timing.navigationStart, workDoneTs); + + // should be within 200ms + assert.closeTo(parseInt(b["c.tti"], 10), minTTI, 200); + } + }); + + it("Should have set the Time to Interactive Method (c.tti.m)", function() { + var b = tf.lastBeacon(); + + if (t.isFirefox()) { + // no TTI method was available (e.g. Firefox without FPS/LongTask/Busy) + return this.skip(); + } + + assert.isDefined(b["c.tti.m"]); + + assert.equal(b["c.tti.m"], "b"); + }); +}); diff --git a/tests/page-templates/21-continuity/31-tti-raf.html b/tests/page-templates/21-continuity/31-tti-raf.html new file mode 100644 index 000000000..733479ce2 --- /dev/null +++ b/tests/page-templates/21-continuity/31-tti-raf.html @@ -0,0 +1,43 @@ +<%= header %> +<%= continuitySnippet %> +<%= boomerangScript %> + +

    31-tti-raf

    + + + + +<%= footer %> diff --git a/tests/page-templates/21-continuity/31-tti-raf.js b/tests/page-templates/21-continuity/31-tti-raf.js new file mode 100644 index 000000000..94b665b0f --- /dev/null +++ b/tests/page-templates/21-continuity/31-tti-raf.js @@ -0,0 +1,70 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["workStart", "workDone"]); + +describe("e2e/21-continuity/31-tti-raf", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a single beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have set the Visually Ready to First Paint or domContentLoadedEventEnd (c.tti.vr)", function() { + var b = tf.lastBeacon(); + + var vr = t.getFirstOrContentfulPaint(); + var p = window.performance; + + // domContentLoadedEventEnd + if (p && p.timing && p.timing.domContentLoadedEventEnd) { + vr = Math.max(vr, p.timing.domContentLoadedEventEnd); + } + + if (vr) { + var st = parseInt(b["rt.tstart"], 10); + + assert.isDefined(b["c.tti.vr"]); + assert.closeTo(parseInt(b["c.tti.vr"], 10), vr - st, 20); + } + }); + + it("Should have set the Time to Interactive (c.tti)", function() { + var b = tf.lastBeacon(); + + if (t.isNavigationTimingSupported()) { + var workDoneTs = window.workDone - performance.timing.navigationStart; + var workStartTs = window.workStart - performance.timing.navigationStart; + + assert.isDefined(b["c.tti"]); + assert.operator(parseInt(b["c.tti"], 10), ">=", workStartTs); + + // the interval should end around the same time as workDoneTs, plus some rounding + assert.operator(parseInt(b["c.tti"], 10), ">=", workDoneTs - 200); + + // should be minimum bound by TTVR + var minTTI = Math.max(t.getFirstOrContentfulPaint() - performance.timing.navigationStart, workDoneTs); + + // should be within 200ms + // + // NOTE: Disabled for now, rAF is too imprecise in slower environments (e.g. Docker) + // to expect it to be close to our expected values. + // + // assert.closeTo(parseInt(b["c.tti"], 10), minTTI, 200); + } + }); + + it("Should have set the Time to Interactive Method to 'raf' (c.tti.m) (if requestAnimationFrame is supported)", function() { + var b = tf.lastBeacon(); + + if (!window.requestAnimationFrame) { + return this.skip(); + } + + assert.isDefined(b["c.tti.m"]); + + assert.equal(b["c.tti.m"], "raf"); + }); +}); diff --git a/tests/page-templates/21-continuity/33-interaction-iframe.html b/tests/page-templates/21-continuity/33-interaction-iframe.html new file mode 100644 index 000000000..747a9805c --- /dev/null +++ b/tests/page-templates/21-continuity/33-interaction-iframe.html @@ -0,0 +1,47 @@ +<%= header %> + + +<%= boomerangSnippet %> +

    33-interaction-iframe loading...

    + + + +<%= footer %> diff --git a/tests/page-templates/21-continuity/33-interaction-iframe.js b/tests/page-templates/21-continuity/33-interaction-iframe.js new file mode 100644 index 000000000..1cdf24b8a --- /dev/null +++ b/tests/page-templates/21-continuity/33-interaction-iframe.js @@ -0,0 +1,95 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["ttfi"]); + +describe("e2e/21-continuity/33-interaction-iframe", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a single beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent the Time to First Interaction (c.ttfi)", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["c.ttfi"]); + var ttfi = parseInt(b["c.ttfi"], 10); + + if (t.isNavigationTimingSupported()) { + var st = parseInt(b["rt.tstart"], 10); + + assert.closeTo(st + ttfi, window.ttfi, 50); + } + else { + assert.operator(ttfi, ">=", 0); + } + }); + + it("Should have the interaction timeline (c.t.inter)", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["c.t.inter"]); + assert.operator(b["c.t.inter"].length, ">=", 1); + }); + + it("Could have the interaction delay timeline (c.t.interdly)", function() { + var b = tf.lastBeacon(); + + if (b["c.i.a"]) { + assert.isDefined(b["c.t.interdly"]); + assert.operator(b["c.t.interdly"].length, ">=", 1); + } + }); + + it("Could have interaction delay metrics (c.i.a, c.i.dc and c.i.dt) in all except Safari", function() { + if (t.isSafari()) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + if (!b["c.i.a"]) { + assert.operator(parseInt(b["c.i.a"], 10), ">=", 1); + assert.operator(parseInt(b["c.i.dc"], 10), ">=", 1); + assert.operator(parseInt(b["c.i.dc"], 10), "<=", 10); + assert.operator(parseInt(b["c.i.dt"], 10), ">=", 1); + } + }); + + it("Should not have interaction delay metrics (c.i.a, c.i.dc and c.i.dt) in Safari", function() { + if (!t.isSafari()) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isUndefined(b["c.i.a"]); + assert.isUndefined(b["c.i.dc"]); + assert.isUndefined(b["c.i.dc"]); + assert.isUndefined(b["c.i.dt"]); + }); + + it("Should have First Input Delay (c.fid) in all except Safari", function() { + if (t.isSafari()) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isDefined(b["c.fid"]); + assert.operator(parseInt(b["c.fid"], 10), ">=", 19); // we had a 20ms busy wait, allow for some fuzzing + }); + + it("Should not have First Input Delay (c.fid) in Safari", function() { + if (!t.isSafari()) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isUndefined(b["c.fid"]); + }); +}); diff --git a/tests/page-templates/21-continuity/33-tti-hero-images-bg-image.html b/tests/page-templates/21-continuity/33-tti-hero-images-bg-image.html new file mode 100644 index 000000000..4540e41c1 --- /dev/null +++ b/tests/page-templates/21-continuity/33-tti-hero-images-bg-image.html @@ -0,0 +1,38 @@ +<%= header %> +<%= continuitySnippet %> +<%= boomerangScript %> + +

    33-tti-hero-images-bg-image

    + +
    +
    +
    +
    + + +<%= footer %> diff --git a/tests/page-templates/21-continuity/33-tti-hero-images-bg-image.js b/tests/page-templates/21-continuity/33-tti-hero-images-bg-image.js new file mode 100644 index 000000000..f44f6cd02 --- /dev/null +++ b/tests/page-templates/21-continuity/33-tti-hero-images-bg-image.js @@ -0,0 +1,51 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["OnImageLoaded", "img", "heroImagesLoaded", "timeToInteractive"]); + +describe("e2e/21-continuity/33-tti-hero-images-bg-image", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a single beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have set the Visually Ready to when the last Hero Image was loaded (c.tti.vr) (if ResourceTiming is supported)", function() { + if (t.isResourceTimingSupported()) { + var b = tf.lastBeacon(); + + var vr = window.heroImagesLoaded; + + var st = parseInt(b["rt.tstart"], 10); + + assert.isDefined(b["c.tti.vr"]); + assert.closeTo(parseInt(b["c.tti.vr"], 10), vr - st, 50); + } + }); + + it("Should have set the Time to Interactive (c.tti)", function() { + var b = tf.lastBeacon(); + + if (t.isNavigationTimingSupported()) { + var workDoneTs = window.timeToInteractive - performance.timing.navigationStart; + + assert.isDefined(b["c.tti"]); + assert.closeTo(parseInt(b["c.tti"], 10), workDoneTs, 200); + } + }); + + it("Should have set the Time to Hero Images (c.tti.hi)", function() { + if (t.isResourceTimingSupported()) { + var b = tf.lastBeacon(); + + var vr = window.heroImagesLoaded; + + var st = parseInt(b["rt.tstart"], 10); + + assert.isDefined(b["c.tti.hi"]); + assert.closeTo(parseInt(b["c.tti.hi"], 10), vr - st, 50); + } + }); +}); diff --git a/tests/page-templates/21-continuity/34-keep-going-api-beacon.html b/tests/page-templates/21-continuity/34-keep-going-api-beacon.html new file mode 100644 index 000000000..19224323a --- /dev/null +++ b/tests/page-templates/21-continuity/34-keep-going-api-beacon.html @@ -0,0 +1,32 @@ +<%= header %> +<%= continuitySnippet %> +<%= boomerangScript %> + +

    34-keep-going-api-beacon

    + + + + +<%= footer %> diff --git a/tests/page-templates/21-continuity/34-keep-going-api-beacon.js b/tests/page-templates/21-continuity/34-keep-going-api-beacon.js new file mode 100644 index 000000000..b92aaf142 --- /dev/null +++ b/tests/page-templates/21-continuity/34-keep-going-api-beacon.js @@ -0,0 +1,62 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/21-continuity/34-keep-going-api-beacon", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent two beacons", function(done) { + this.timeout(15000); + + t.ensureBeaconCount(done, 2); + }); + + it("Should have set the Visually Ready to First Paint or domContentLoadedEventEnd (c.tti.vr)", function() { + var b = tf.lastBeacon(); + + var vr = t.getFirstOrContentfulPaint(); + var p = window.performance; + + // domContentLoadedEventEnd + if (p && p.timing && p.timing.domContentLoadedEventEnd) { + vr = Math.max(vr, p.timing.domContentLoadedEventEnd); + } + + if (vr) { + var st = parseInt(b["rt.tstart"], 10); + + assert.isDefined(b["c.tti.vr"]); + assert.closeTo(parseInt(b["c.tti.vr"], 10), vr - st, 20); + } + }); + + it("Should have set the Time to Interactive (c.tti)", function() { + var b = tf.lastBeacon(); + + if (t.isNavigationTimingSupported()) { + var p = window.performance; + + assert.isDefined(b["c.tti"]); + + var tti = parseInt(b["c.tti"], 10); + + assert.operator(tti, ">=", p.timing.domContentLoadedEventEnd - p.timing.navigationStart); + } + }); + + it("Should have set the Time to Interactive Method (c.tti.m)", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["c.tti.m"]); + + if (window.PerformanceLongTaskTiming) { + assert.equal(b["c.tti.m"], "lt"); + } + else if (window.requestAnimationFrame) { + assert.equal(b["c.tti.m"], "raf"); + } + else { + assert.equal(b["c.tti.m"], "b"); + } + }); +}); diff --git a/tests/page-templates/21-continuity/35-ttvr-lcp.html b/tests/page-templates/21-continuity/35-ttvr-lcp.html new file mode 100644 index 000000000..552f75abf --- /dev/null +++ b/tests/page-templates/21-continuity/35-ttvr-lcp.html @@ -0,0 +1,20 @@ +<%= header %> +<%= continuitySnippet %> +<%= boomerangScript %> + +

    14-tti

    + + + + +<%= footer %> diff --git a/tests/page-templates/21-continuity/35-ttvr-lcp.js b/tests/page-templates/21-continuity/35-ttvr-lcp.js new file mode 100644 index 000000000..346ae7118 --- /dev/null +++ b/tests/page-templates/21-continuity/35-ttvr-lcp.js @@ -0,0 +1,25 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/21-continuity/35-ttvr-lcp", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a single beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have set the Visually Ready to Largest Contentful Paint (c.tti.vr)", function() { + var b = tf.lastBeacon(); + + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + assert.isDefined(b["c.tti.vr"]); + var lcp = parseInt(b["pt.lcp"], 10); + var vr = parseInt(b["c.tti.vr"], 10); + + assert.closeTo(vr, lcp, 20); + }); +}); diff --git a/tests/page-templates/21-continuity/36-cls.html b/tests/page-templates/21-continuity/36-cls.html new file mode 100644 index 000000000..0075f418a --- /dev/null +++ b/tests/page-templates/21-continuity/36-cls.html @@ -0,0 +1,56 @@ +<%= header %> + + +<%= boomerangScript %> + + + + + + +<%= footer %> diff --git a/tests/page-templates/21-continuity/36-cls.js b/tests/page-templates/21-continuity/36-cls.js new file mode 100644 index 000000000..5183a5f28 --- /dev/null +++ b/tests/page-templates/21-continuity/36-cls.js @@ -0,0 +1,41 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["clsScoreOnBeaconSend", "clsScore", "topScoreOnBeaconSend", "topScore", "observer", "onBeforeBeaconSend"]); + +describe("e2e/21-continuity/36-cls", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a beacon", function() { + // ensure we fired a beacon ('beacon') + assert.isTrue(tf.fired_onbeacon); + }); + + it("Should have set c.cls (if PerformanceObserver is supported)", function(done) { + if (!typeof BOOMR.window.PerformanceObserver === "function" || !BOOMR.window.LayoutShift) { + return this.skip(); + } + + var clsScore = BOOMR.plugins.Continuity.decompressClsScore(tf.lastBeacon()["c.cls"]); + + assert.isNumber(clsScore); + assert.equal(clsScore, parseFloat(clsScoreOnBeaconSend.toFixed(3))); + + done(); + }); + + it("Should have set c.cls.tops (if PerformanceObserver is supported)", function(done) { + if (!typeof BOOMR.window.PerformanceObserver === "function" || !BOOMR.window.LayoutShift) { + return this.skip(); + } + + var topScore = BOOMR.plugins.Continuity.decompressClsScore(tf.lastBeacon()["c.cls.tops"]); + + assert.isNumber(topScore); + assert.equal(topScore, topScoreOnBeaconSend); + + done(); + }); +}); diff --git a/tests/page-templates/21-continuity/37-touchstart.html b/tests/page-templates/21-continuity/37-touchstart.html new file mode 100644 index 000000000..2b7826024 --- /dev/null +++ b/tests/page-templates/21-continuity/37-touchstart.html @@ -0,0 +1,19 @@ +<%= header %> +<%= boomerangScript %> + + + +<%= footer %> diff --git a/tests/page-templates/21-continuity/37-touchstart.js b/tests/page-templates/21-continuity/37-touchstart.js new file mode 100644 index 000000000..718a5beae --- /dev/null +++ b/tests/page-templates/21-continuity/37-touchstart.js @@ -0,0 +1,26 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["ts"]); + +describe("e2e/21-continuity/37-touchstart", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a single beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should not have sent touchstart timeline (c.t.ts)", function() { + var b = tf.lastBeacon(); + + assert.isUndefined(b["c.t.ts"], "c.t.ts should have been present"); + }); + + it("Should have sent FID metric for touchstart interaction (c.fid)", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["c.fid"], "c.fid should have been present"); + }); +}); diff --git a/tests/page-templates/21-continuity/38-mousedown.html b/tests/page-templates/21-continuity/38-mousedown.html new file mode 100644 index 000000000..c2b7a2d6d --- /dev/null +++ b/tests/page-templates/21-continuity/38-mousedown.html @@ -0,0 +1,24 @@ +<%= header %> +<%= boomerangScript %> + + + + +<%= footer %> diff --git a/tests/page-templates/21-continuity/38-mousedown.js b/tests/page-templates/21-continuity/38-mousedown.js new file mode 100644 index 000000000..f7b956fd7 --- /dev/null +++ b/tests/page-templates/21-continuity/38-mousedown.js @@ -0,0 +1,26 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["md"]); + +describe("e2e/21-continuity/38-mousedown", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a single beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should not have sent mousedown timeline (c.t.md)", function() { + var b = tf.lastBeacon(); + + assert.isUndefined(b["c.t.md"], "c.t.md should have been present"); + }); + + it("Should have sent FID metric for mousedown interaction (c.fid)", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["c.fid"], "c.fid should have been present"); + }); +}); diff --git a/tests/page-templates/21-continuity/39-pointerdown.html b/tests/page-templates/21-continuity/39-pointerdown.html new file mode 100644 index 000000000..11de238e2 --- /dev/null +++ b/tests/page-templates/21-continuity/39-pointerdown.html @@ -0,0 +1,27 @@ +<%= header %> +<%= boomerangScript %> + + + +<%= footer %> diff --git a/tests/page-templates/21-continuity/39-pointerdown.js b/tests/page-templates/21-continuity/39-pointerdown.js new file mode 100644 index 000000000..e34a3b5c8 --- /dev/null +++ b/tests/page-templates/21-continuity/39-pointerdown.js @@ -0,0 +1,26 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["pd", "pu"]); + +describe("e2e/21-continuity/39-pointerdown", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a single beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should not have sent pointerdown timeline (c.t.pd)", function() { + var b = tf.lastBeacon(); + + assert.isUndefined(b["c.t.pd"], "c.t.pd should not have been present"); + }); + + it("Should have sent FID metric for pointerdown interaction (c.fid)", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["c.fid"], "c.fid should have been present"); + }); +}); diff --git a/tests/page-templates/21-continuity/40-pointerdown-pointercancel.html b/tests/page-templates/21-continuity/40-pointerdown-pointercancel.html new file mode 100644 index 000000000..ddd41969c --- /dev/null +++ b/tests/page-templates/21-continuity/40-pointerdown-pointercancel.html @@ -0,0 +1,39 @@ +<%= header %> +<%= boomerangScript %> + + + +<%= footer %> diff --git a/tests/page-templates/21-continuity/40-pointerdown-pointercancel.js b/tests/page-templates/21-continuity/40-pointerdown-pointercancel.js new file mode 100644 index 000000000..fa2852c61 --- /dev/null +++ b/tests/page-templates/21-continuity/40-pointerdown-pointercancel.js @@ -0,0 +1,26 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["pd", "pc", "pu"]); + +describe("e2e/21-continuity/40-pointerdown-pointercancel", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a single beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should not have sent pointerdown timeline with pointercancel event(c.t.pd)", function() { + var b = tf.lastBeacon(); + + assert.isUndefined(b["c.t.pd"], "c.t.pd should not have been present"); + }); + + it("Should have not sent FID metric for pointerdown interaction (c.fid)", function() { + var b = tf.lastBeacon(); + + assert.isUndefined(b["c.fid"], "c.fid should not have been present"); + }); +}); diff --git a/tests/page-templates/21-continuity/41-mouseevent-cancellable-false.html b/tests/page-templates/21-continuity/41-mouseevent-cancellable-false.html new file mode 100644 index 000000000..35f29423d --- /dev/null +++ b/tests/page-templates/21-continuity/41-mouseevent-cancellable-false.html @@ -0,0 +1,54 @@ +<%= header %> +<%= boomerangScript %> + + + + + + + +<%= footer %> diff --git a/tests/page-templates/21-continuity/41-mouseevent-cancellable-false.js b/tests/page-templates/21-continuity/41-mouseevent-cancellable-false.js new file mode 100644 index 000000000..74698e376 --- /dev/null +++ b/tests/page-templates/21-continuity/41-mouseevent-cancellable-false.js @@ -0,0 +1,111 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["ev1", "ev2"]); + +describe("e2e/21-continuity/41-mouseevent-cancellable-false", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a single beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent the click count (c.c) (if creating Mouse Events is supported)", function() { + if (window.cannotCreateMouseEvent) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isDefined(b["c.c"]); + assert.equal(parseInt(b["c.c"], 10), 10); + }); + + it("Should have sent the rage click count (c.c.r) (if creating Mouse Events is supported)", function() { + if (window.cannotCreateMouseEvent) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isDefined(b["c.c.r"]); + assert.equal(parseInt(b["c.c.r"], 10), 2); + }); + + it("Should have sent the log (c.l) (if creating Mouse Events is supported)", function() { + if (window.cannotCreateMouseEvent) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isDefined(b["c.l"]); + }); + + it("Should have logged the correct events (c.l) (if creating Mouse Events is supported)", function() { + if (window.cannotCreateMouseEvent) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + var clickLogs = BOOMR.plugins.Continuity.decompressLog(b["c.l"]).filter(function(obj) { + // LOG_TYPE_CLICK === 1 + return obj.type === 1; + }); + + assert.equal(clickLogs.length, 10); + + for (var i = 0; i < clickLogs.length; i++) { + // should all be x,y === 100 or 200 + var x = parseInt(clickLogs[i].x, 36); + var y = parseInt(clickLogs[i].y, 36); + + assert.isTrue(x === 100 || x === 200); + assert.isTrue(y === 100 || y === 200); + } + }); + + it("Should have the click timeline (c.t.click) (if creating Mouse Events is supported)", function() { + if (window.cannotCreateMouseEvent) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isDefined(b["c.t.click"]); + assert.operator(b["c.t.click"].length, ">=", 1); + }); + + it("Should not have the interaction timeline (c.t.inter) (if creating Mouse Events is supported)", function() { + if (window.cannotCreateMouseEvent) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isUndefined(b["c.t.inter"]); + }); + + it("Should not have the Time to First Interaction (c.ttfi) (if creating Mouse Events is supported)", function() { + if (window.cannotCreateMouseEvent) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isUndefined(b["c.ttfi"]); + }); + + it("Should not have First Input Delay (c.fid) (if creating Mouse Events is supported)", function() { + if (window.cannotCreateMouseEvent) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isUndefined(b["c.fid"]); + }); +}); diff --git a/tests/page-templates/21-continuity/42-keydown-cancellable-false.html b/tests/page-templates/21-continuity/42-keydown-cancellable-false.html new file mode 100644 index 000000000..b4b81a51d --- /dev/null +++ b/tests/page-templates/21-continuity/42-keydown-cancellable-false.html @@ -0,0 +1,22 @@ +<%= header %> +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/21-continuity/42-keydown-cancellable-false.js b/tests/page-templates/21-continuity/42-keydown-cancellable-false.js new file mode 100644 index 000000000..69a0554ad --- /dev/null +++ b/tests/page-templates/21-continuity/42-keydown-cancellable-false.js @@ -0,0 +1,70 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["ev"]); + +describe("e2e/21-continuity/42-keydown-cancellable-false", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a single beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent the key count (c.k)", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["c.k"]); + assert.equal(parseInt(b["c.k"], 10), 3); + }); + + it("Should have sent the esc key count (c.k.e)", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["c.k.e"]); + assert.equal(parseInt(b["c.k.e"], 10), 3); + }); + + it("Should have sent the log (c.l)", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["c.l"]); + }); + + it("Should have logged the correct events (c.l)", function() { + var b = tf.lastBeacon(); + + var keyLogs = BOOMR.plugins.Continuity.decompressLog(b["c.l"]).filter(function(obj) { + // LOG_TYPE_KEY === 3 + return obj.type === 3; + }); + + assert.equal(keyLogs.length, 3); + }); + + it("Should have the key timeline (c.t.key)", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["c.t.key"]); + assert.operator(b["c.t.key"].length, ">=", 1); + }); + + it("Should not have the interaction timeline (c.t.inter)", function() { + var b = tf.lastBeacon(); + + assert.isUndefined(b["c.t.inter"]); + }); + + it("Should not have the Time to First Interaction (c.ttfi)", function() { + var b = tf.lastBeacon(); + + assert.isUndefined(b["c.ttfi"]); + }); + + it("Should not have First Input Delay (c.fid)", function() { + var b = tf.lastBeacon(); + + assert.isUndefined(b["c.fid"]); + }); +}); diff --git a/tests/page-templates/21-continuity/43-cls-sources.html b/tests/page-templates/21-continuity/43-cls-sources.html new file mode 100644 index 000000000..438f8c13c --- /dev/null +++ b/tests/page-templates/21-continuity/43-cls-sources.html @@ -0,0 +1,81 @@ +<%= header %> + + +<%= boomerangScript %> + + + + + + +<%= footer %> diff --git a/tests/page-templates/21-continuity/43-cls-sources.js b/tests/page-templates/21-continuity/43-cls-sources.js new file mode 100644 index 000000000..2b04781d3 --- /dev/null +++ b/tests/page-templates/21-continuity/43-cls-sources.js @@ -0,0 +1,68 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["clsSourcesOnBeaconSend", "clsSources", "topIDOnBeaconSend", "topID", "topScore", "observer", "onBeforeBeaconSend"]); + +describe("e2e/21-continuity/43-cls-sources", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + // We would like to skip testing for browsers that do not support CLS. + var isClsSupported = t.isCLSSupported(); + + it("Should have sent a beacon", function() { + // ensure we fired a beacon ('beacon') + assert.isTrue(tf.fired_onbeacon); + }); + + it("Should have set c.cls.d (if PerformanceObserver is supported)", function(done) { + if (!isClsSupported) { + return this.skip(); + } + + var clsSources = BOOMR.plugins.Continuity.decompressClsSources(tf.lastBeacon()["c.cls.d"]); + + assert(Array.isArray(clsSources)); + + for (var entry = 0; entry < clsSources.length; entry++) { + assert.deepEqual(Object.keys(clsSources[entry]), Object.keys(clsSourcesOnBeaconSend[entry])); + + assert.isNumber(clsSources[entry].value); + assert(clsSources[entry].value < 1); + + assert.isNumber(clsSources[entry].startTime); + + var sourceList = clsSources[entry].sources; + + assert.isArray(sourceList); + + for (var source = 0; source < sourceList.length; source++) { + assert.deepEqual(Object.keys(sourceList[source]), Object.keys(clsSourcesOnBeaconSend[entry].sources[source])); + assert.isString(sourceList[source].selector); + assert.isObject(sourceList[source].previousRect); + assert.isObject(sourceList[source].currentRect); + + assert.strictEqual(sourceList[source].selector, clsSourcesOnBeaconSend[entry].sources[source].selector); + assert.deepEqual(sourceList[source].previousRect, clsSourcesOnBeaconSend[entry].sources[source].previousRect); + assert.deepEqual(sourceList[source].currentRect, clsSourcesOnBeaconSend[entry].sources[source].currentRect); + } + + assert.equal(clsSources[entry].value, clsSourcesOnBeaconSend[entry].value); + assert.equal(clsSources[entry].startTime, clsSourcesOnBeaconSend[entry].startTime); + assert.deepEqual(clsSources[entry].sources, clsSourcesOnBeaconSend[entry].sources); + } + + done(); + }); + + it("Should have set c.cls.topid (if PerformanceObserver is supported)", function(done) { + if (!isClsSupported) { + return this.skip(); + } + + assert.isString(tf.lastBeacon()["c.cls.topid"]); + assert.strictEqual(tf.lastBeacon()["c.cls.topid"], topIDOnBeaconSend); + done(); + }); +}); diff --git a/tests/page-templates/21-continuity/44-zero-cls.html b/tests/page-templates/21-continuity/44-zero-cls.html new file mode 100644 index 000000000..1eb0dd2a2 --- /dev/null +++ b/tests/page-templates/21-continuity/44-zero-cls.html @@ -0,0 +1,13 @@ +<%= header %> +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/21-continuity/44-zero-cls.js b/tests/page-templates/21-continuity/44-zero-cls.js new file mode 100644 index 000000000..b2ef3009f --- /dev/null +++ b/tests/page-templates/21-continuity/44-zero-cls.js @@ -0,0 +1,40 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/21-continuity/44-zero-cls", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + // We would like to skip testing for browsers that do not support CLS. + var isClsSupported = t.isCLSSupported(); + + it("Should have sent a beacon", function() { + // ensure we fired a beacon ('beacon') + assert.isTrue(tf.fired_onbeacon); + }); + + it("Should have set c.cls of 0", function() { + if (!isClsSupported) { + this.skip(); + } + + assert.equal(tf.lastBeacon()["c.cls"], 0); + }); + + it("Should not have set c.cls.topid", function() { + if (!isClsSupported) { + this.skip(); + } + + assert.isUndefined(tf.lastBeacon()["c.cls.topid"]); + }); + + it("Should not have set c.cls.d", function() { + if (!isClsSupported) { + this.skip(); + } + + assert.isUndefined(tf.lastBeacon()["c.cls.d"]); + }); +}); + diff --git a/tests/page-templates/21-continuity/45-tti-prerendered.html b/tests/page-templates/21-continuity/45-tti-prerendered.html new file mode 100644 index 000000000..d1912cadb --- /dev/null +++ b/tests/page-templates/21-continuity/45-tti-prerendered.html @@ -0,0 +1,20 @@ +<%= header %> +<%= continuitySnippet %> +<%= boomerangScript %> + +

    45-tti-prerendered

    + + + + +<%= footer %> diff --git a/tests/page-templates/21-continuity/45-tti-prerendered.js b/tests/page-templates/21-continuity/45-tti-prerendered.js new file mode 100644 index 000000000..df12f828b --- /dev/null +++ b/tests/page-templates/21-continuity/45-tti-prerendered.js @@ -0,0 +1,26 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/21-continuity/45-tti-prerendered", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a single beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have set the Time to Interactive (c.tti), offset by Activation Start", function() { + if (!t.isNavigationTimingSupported() || !t.isPrerenderingSupported()) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isDefined(b["c.tti"]); + + var tti = parseInt(b["c.tti"], 10); + + assert.operator(tti, ">=", 1); + assert.operator(tti, "<=", 10000); + }); +}); diff --git a/tests/page-templates/21-continuity/46-ttfi-prerendered.html b/tests/page-templates/21-continuity/46-ttfi-prerendered.html new file mode 100644 index 000000000..3594691ac --- /dev/null +++ b/tests/page-templates/21-continuity/46-ttfi-prerendered.html @@ -0,0 +1,31 @@ +<%= header %> +<%= continuitySnippet %> +<%= boomerangScript %> + +

    46-ttfi-prerendered

    + + + + +<%= footer %> diff --git a/tests/page-templates/21-continuity/46-ttfi-prerendered.js b/tests/page-templates/21-continuity/46-ttfi-prerendered.js new file mode 100644 index 000000000..87175f0df --- /dev/null +++ b/tests/page-templates/21-continuity/46-ttfi-prerendered.js @@ -0,0 +1,30 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/21-continuity/46-ttfi-prerendered", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a single beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have set the Time to First Interaction (c.ttfi), offset by Activation Start", function() { + if (!t.isNavigationTimingSupported() || !t.isPrerenderingSupported()) { + return this.skip(); + } + + var b = tf.lastBeacon(); + + assert.isDefined(b["c.ttfi"]); + + var ttfi = parseInt(b["c.ttfi"], 10); + + // we waited 2s to fire it, but activation took 1.9 second, so it should be between 0 and 1s + var ttfiDispatchedAt = BOOMR_test.dispatchedAt; + var activationStart = BOOMR_test.fakeActivationStartOffset; + var calculatedTtfi = ttfiDispatchedAt - activationStart; + + assert.closeTo(ttfi, calculatedTtfi, 200); + }); +}); diff --git a/tests/page-templates/21-continuity/support/longtaskframe.html b/tests/page-templates/21-continuity/support/longtaskframe.html new file mode 100644 index 000000000..1ea8119af --- /dev/null +++ b/tests/page-templates/21-continuity/support/longtaskframe.html @@ -0,0 +1,18 @@ + + +

    LongTaskFrame

    + + + diff --git a/tests/page-templates/21-continuity/tests.json5 b/tests/page-templates/21-continuity/tests.json5 new file mode 100644 index 000000000..1f7b1f963 --- /dev/null +++ b/tests/page-templates/21-continuity/tests.json5 @@ -0,0 +1,5 @@ +{ + all: { + requires: ["continuity"] + } +} diff --git a/tests/page-templates/22-early/00-config-before-onload.html b/tests/page-templates/22-early/00-config-before-onload.html new file mode 100644 index 000000000..49d41c66b --- /dev/null +++ b/tests/page-templates/22-early/00-config-before-onload.html @@ -0,0 +1,43 @@ +<%= header %> + + +<%= boomerangSnippet %> + + + + +<%= footer %> diff --git a/tests/page-templates/22-early/00-config-before-onload.js b/tests/page-templates/22-early/00-config-before-onload.js new file mode 100644 index 000000000..e4e1ff039 --- /dev/null +++ b/tests/page-templates/22-early/00-config-before-onload.js @@ -0,0 +1,50 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +describe("e2e/22-early/00-config-before-onload", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent two beacons", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 2); + }); + + describe("Beacon 1 (early)", function() { + var i = 0; + + it("Should be an early beacon", function() { + var b = tf.beacons[i]; + + assert.isDefined(b.early); + }); + + // the following tests are only executed if mPulse's PageParams plugin exists + if (BOOMR.plugins.PageParams) { + it("Should have a h.pg of MYPAGEGROUP", function() { + var b = tf.beacons[i]; + + assert.equal(b["h.pg"], "MYPAGEGROUP"); + }); + } + }); + + describe("Beacon 2 (page view)", function() { + var i = 1; + + it("Should not be an early beacon", function() { + var b = tf.beacons[i]; + + assert.isUndefined(b.early); + }); + + // the following tests are only executed if mPulse's PageParams plugin exists + if (BOOMR.plugins.PageParams) { + it("Should have a h.pg of MYPAGEGROUP", function() { + var b = tf.beacons[i]; + + assert.equal(b["h.pg"], "MYPAGEGROUP"); + }); + } + }); +}); diff --git a/tests/page-templates/22-early/01-config-after-onload.html b/tests/page-templates/22-early/01-config-after-onload.html new file mode 100644 index 000000000..7f58b115a --- /dev/null +++ b/tests/page-templates/22-early/01-config-after-onload.html @@ -0,0 +1,43 @@ +<%= header %> + + +<%= boomerangSnippet %> + + + + +<%= footer %> diff --git a/tests/page-templates/22-early/01-config-after-onload.js b/tests/page-templates/22-early/01-config-after-onload.js new file mode 100644 index 000000000..aff86895c --- /dev/null +++ b/tests/page-templates/22-early/01-config-after-onload.js @@ -0,0 +1,31 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +describe("e2e/22-early/01-config-after-onload", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent one beacons", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 1); + }); + + describe("Beacon 1 (page view)", function() { + var i = 0; + + it("Should not be an early beacon", function() { + var b = tf.beacons[i]; + + assert.isUndefined(b.early); + }); + + // the following tests are only executed if mPulse's PageParams plugin exists + if (BOOMR.plugins.PageParams) { + it("Should have a h.pg of MYPAGEGROUP", function() { + var b = tf.beacons[i]; + + assert.equal(b["h.pg"], "MYPAGEGROUP"); + }); + } + }); +}); diff --git a/tests/page-templates/22-early/02-prerender-onload-not-fired.html b/tests/page-templates/22-early/02-prerender-onload-not-fired.html new file mode 100644 index 000000000..4c59e2bf9 --- /dev/null +++ b/tests/page-templates/22-early/02-prerender-onload-not-fired.html @@ -0,0 +1,66 @@ +<%= header %> + + +<%= boomerangScript %> + + + + +<%= footer %> diff --git a/tests/page-templates/22-early/02-prerender-onload-not-fired.js b/tests/page-templates/22-early/02-prerender-onload-not-fired.js new file mode 100644 index 000000000..cafde1891 --- /dev/null +++ b/tests/page-templates/22-early/02-prerender-onload-not-fired.js @@ -0,0 +1,69 @@ +/* eslint-env mocha */ +/* global BOOMR_test,t_visible */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["t_visible"]); + +describe("e2e/22-early/02-prerender-onload-not-fired", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent two beacons", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 2); + }); + + describe("Beacon 1 (early)", function() { + var i = 0; + + it("Should be an early beacon", function() { + var b = tf.beacons[i]; + + assert.isDefined(b.early); + }); + + // the following tests are only executed if mPulse's PageParams plugin exists + if (BOOMR.plugins.PageParams) { + it("Should have a h.pg of MYPAGEGROUP", function() { + var b = tf.beacons[i]; + + assert.equal(b["h.pg"], "MYPAGEGROUP"); + }); + } + + it("Should have a vis.pre = 1", function() { + var b = tf.beacons[i]; + + assert.equal(b["vis.pre"], "1"); + }); + + // it("Should have been sent after visible event", function() { + // // TODO + // }); + }); + + describe("Beacon 2 (page view)", function() { + var i = 1; + + it("Should not be an early beacon", function() { + var b = tf.beacons[i]; + + assert.isUndefined(b.early); + }); + + // the following tests are only executed if mPulse's PageParams plugin exists + if (BOOMR.plugins.PageParams) { + it("Should have a h.pg of MYPAGEGROUP", function() { + var b = tf.beacons[i]; + + assert.equal(b["h.pg"], "MYPAGEGROUP"); + }); + } + + it("Should have a vis.pre = 1", function() { + var b = tf.beacons[i]; + + assert.equal(b["vis.pre"], "1"); + }); + }); +}); diff --git a/tests/page-templates/22-early/03-prerender-onload-fired.html b/tests/page-templates/22-early/03-prerender-onload-fired.html new file mode 100644 index 000000000..9cd6827f1 --- /dev/null +++ b/tests/page-templates/22-early/03-prerender-onload-fired.html @@ -0,0 +1,64 @@ +<%= header %> + + +<%= boomerangScript %> + + + + +<%= footer %> diff --git a/tests/page-templates/22-early/03-prerender-onload-fired.js b/tests/page-templates/22-early/03-prerender-onload-fired.js new file mode 100644 index 000000000..9ba586f71 --- /dev/null +++ b/tests/page-templates/22-early/03-prerender-onload-fired.js @@ -0,0 +1,37 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +describe("e2e/22-early/03-prerender-onload-fired", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent one beacons", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 1); + }); + + describe("Beacon 1 (page view)", function() { + var i = 0; + + it("Should not be an early beacon", function() { + var b = tf.beacons[i]; + + assert.isUndefined(b.early); + }); + + // the following tests are only executed if mPulse's PageParams plugin exists + if (BOOMR.plugins.PageParams) { + it("Should have a h.pg of MYPAGEGROUP", function() { + var b = tf.beacons[i]; + + assert.equal(b["h.pg"], "MYPAGEGROUP"); + }); + } + + it("Should have a vis.pre = 1", function() { + var b = tf.beacons[i]; + + assert.equal(b["vis.pre"], "1"); + }); + }); +}); diff --git a/tests/page-templates/22-early/04-prerender-config-after-onload.html b/tests/page-templates/22-early/04-prerender-config-after-onload.html new file mode 100644 index 000000000..69bcd9ed4 --- /dev/null +++ b/tests/page-templates/22-early/04-prerender-config-after-onload.html @@ -0,0 +1,64 @@ +<%= header %> + + +<%= boomerangScript %> + + + + +<%= footer %> diff --git a/tests/page-templates/22-early/04-prerender-config-after-onload.js b/tests/page-templates/22-early/04-prerender-config-after-onload.js new file mode 100644 index 000000000..6ec70c109 --- /dev/null +++ b/tests/page-templates/22-early/04-prerender-config-after-onload.js @@ -0,0 +1,31 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +describe("e2e/22-early/04-prerender-config-after-onload", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent one beacons", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 1); + }); + + describe("Beacon 1 (page view)", function() { + var i = 0; + + it("Should not be an early beacon", function() { + var b = tf.beacons[i]; + + assert.isUndefined(b.early); + }); + + // the following tests are only executed if mPulse's PageParams plugin exists + if (BOOMR.plugins.PageParams) { + it("Should have a h.pg of MYPAGEGROUP", function() { + var b = tf.beacons[i]; + + assert.equal(b["h.pg"], "MYPAGEGROUP"); + }); + } + }); +}); diff --git a/tests/page-templates/22-early/05-disabled.html b/tests/page-templates/22-early/05-disabled.html new file mode 100644 index 000000000..0df01bec8 --- /dev/null +++ b/tests/page-templates/22-early/05-disabled.html @@ -0,0 +1,39 @@ +<%= header %> + + +<%= boomerangSnippet %> + + + + +<%= footer %> diff --git a/tests/page-templates/22-early/05-disabled.js b/tests/page-templates/22-early/05-disabled.js new file mode 100644 index 000000000..d4bb712bb --- /dev/null +++ b/tests/page-templates/22-early/05-disabled.js @@ -0,0 +1,31 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +describe("e2e/22-early/05-disabled", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent one beacons", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 1); + }); + + describe("Beacon 1 (page view)", function() { + var i = 0; + + it("Should not be an early beacon", function() { + var b = tf.beacons[i]; + + assert.isUndefined(b.early); + }); + + // the following tests are only executed if mPulse's PageParams plugin exists + if (BOOMR.plugins.PageParams) { + it("Should have a h.pg of MYPAGEGROUP", function() { + var b = tf.beacons[i]; + + assert.equal(b["h.pg"], "MYPAGEGROUP"); + }); + } + }); +}); diff --git a/tests/page-templates/22-early/08-autorun-false.html b/tests/page-templates/22-early/08-autorun-false.html new file mode 100644 index 000000000..08be0aa40 --- /dev/null +++ b/tests/page-templates/22-early/08-autorun-false.html @@ -0,0 +1,45 @@ +<%= header %> + + +<%= boomerangSnippet %> + + + + +<%= footer %> diff --git a/tests/page-templates/22-early/08-autorun-false.js b/tests/page-templates/22-early/08-autorun-false.js new file mode 100644 index 000000000..24f66307f --- /dev/null +++ b/tests/page-templates/22-early/08-autorun-false.js @@ -0,0 +1,31 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +describe("e2e/22-early/08-autorun-false", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent one beacons", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 1); + }); + + describe("Beacon 1 (page view)", function() { + var i = 0; + + it("Should not be an early beacon", function() { + var b = tf.beacons[i]; + + assert.isUndefined(b.early); + }); + + // the following tests are only executed if mPulse's PageParams plugin exists + if (BOOMR.plugins.PageParams) { + it("Should have a h.pg of MYPAGEGROUP", function() { + var b = tf.beacons[i]; + + assert.equal(b["h.pg"], "MYPAGEGROUP"); + }); + } + }); +}); diff --git a/tests/page-templates/22-early/09-navigation-type-backforward.html b/tests/page-templates/22-early/09-navigation-type-backforward.html new file mode 100644 index 000000000..ee8177306 --- /dev/null +++ b/tests/page-templates/22-early/09-navigation-type-backforward.html @@ -0,0 +1,42 @@ +<%= header %> + + +<%= boomerangSnippet %> + + + + +<%= footer %> diff --git a/tests/page-templates/22-early/09-navigation-type-backforward.js b/tests/page-templates/22-early/09-navigation-type-backforward.js new file mode 100644 index 000000000..faf0501ca --- /dev/null +++ b/tests/page-templates/22-early/09-navigation-type-backforward.js @@ -0,0 +1,31 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +describe("e2e/22-early/09-navigation-type-backforward", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent one beacons", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 1); + }); + + describe("Beacon 1 (page view)", function() { + var i = 0; + + it("Should not be an early beacon", function() { + var b = tf.beacons[i]; + + assert.isUndefined(b.early); + }); + + // the following tests are only executed if mPulse's PageParams plugin exists + if (BOOMR.plugins.PageParams) { + it("Should have a h.pg of MYPAGEGROUP", function() { + var b = tf.beacons[i]; + + assert.equal(b["h.pg"], "MYPAGEGROUP"); + }); + } + }); +}); diff --git a/tests/page-templates/22-early/10-navigation-type-reload.html b/tests/page-templates/22-early/10-navigation-type-reload.html new file mode 100644 index 000000000..89118b5ce --- /dev/null +++ b/tests/page-templates/22-early/10-navigation-type-reload.html @@ -0,0 +1,42 @@ +<%= header %> + + +<%= boomerangSnippet %> + + + + +<%= footer %> diff --git a/tests/page-templates/22-early/10-navigation-type-reload.js b/tests/page-templates/22-early/10-navigation-type-reload.js new file mode 100644 index 000000000..c03cfa55c --- /dev/null +++ b/tests/page-templates/22-early/10-navigation-type-reload.js @@ -0,0 +1,31 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +describe("e2e/22-early/10-navigation-type-reload", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent one beacons", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 1); + }); + + describe("Beacon 1 (page view)", function() { + var i = 0; + + it("Should not be an early beacon", function() { + var b = tf.beacons[i]; + + assert.isUndefined(b.early); + }); + + // the following tests are only executed if mPulse's PageParams plugin exists + if (BOOMR.plugins.PageParams) { + it("Should have a h.pg of MYPAGEGROUP", function() { + var b = tf.beacons[i]; + + assert.equal(b["h.pg"], "MYPAGEGROUP"); + }); + } + }); +}); diff --git a/tests/page-templates/22-early/12-addvar.html b/tests/page-templates/22-early/12-addvar.html new file mode 100644 index 000000000..a0c9e754f --- /dev/null +++ b/tests/page-templates/22-early/12-addvar.html @@ -0,0 +1,41 @@ +<%= header %> + + +<%= boomerangSnippet %> + + + + +<%= footer %> diff --git a/tests/page-templates/22-early/12-addvar.js b/tests/page-templates/22-early/12-addvar.js new file mode 100644 index 000000000..fe41025cb --- /dev/null +++ b/tests/page-templates/22-early/12-addvar.js @@ -0,0 +1,106 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +describe("e2e/22-early/12-addVar", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent three beacons", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 3); + }); + + describe("Beacon 1 (early)", function() { + var i = 0; + + it("Should be an early beacon", function() { + var b = tf.beacons[i]; + + assert.isDefined(b.early); + }); + + it("Should have the persistent addVar", function() { + var b = tf.beacons[i]; + + assert.equal(b.multi, "a"); + }); + + it("Should have the non-persistent addVar", function() { + var b = tf.beacons[i]; + + assert.equal(b.single, "b"); + }); + + it("Should not have addVars that were added later", function() { + var b = tf.beacons[i]; + + assert.isUndefined(b.multi2); + assert.isUndefined(b.single2); + }); + }); + + describe("Beacon 2 (page view)", function() { + var i = 1; + + it("Should not be an early beacon", function() { + var b = tf.beacons[i]; + + assert.isUndefined(b.early); + }); + + it("Should have the persistent addVar", function() { + var b = tf.beacons[i]; + + assert.equal(b.multi, "a"); + }); + + it("Should have the non-persistent addVar", function() { + var b = tf.beacons[i]; + + assert.equal(b.single, "b"); + }); + + it("Should not have addVars that were added later", function() { + var b = tf.beacons[i]; + + assert.isUndefined(b.multi2); + assert.isUndefined(b.single2); + }); + }); + + describe("Beacon 3 (responseEnd)", function() { + var i = 2; + + it("Should not be an early beacon", function() { + var b = tf.beacons[i]; + + assert.isUndefined(b.early); + }); + + it("Should be a responseEnd beacon", function() { + var b = tf.beacons[i]; + + assert.equal(b["rt.start"], "manual"); + assert.equal(b["xhr.pg"], "foo"); + }); + + it("Should have the persistent addVar", function() { + var b = tf.beacons[i]; + + assert.equal(b.multi, "a"); + }); + + it("Should not have the non-persistent addVar", function() { + var b = tf.beacons[i]; + + assert.isUndefined(b.single); + }); + + it("Should have addVars added for this beacon", function() { + var b = tf.beacons[i]; + + assert.equal(b.multi2, "c"); + assert.equal(b.single2, "d"); + }); + }); +}); diff --git a/tests/page-templates/22-early/13-xhr-before-onload-alwayssendxhr.html b/tests/page-templates/22-early/13-xhr-before-onload-alwayssendxhr.html new file mode 100644 index 000000000..37be84648 --- /dev/null +++ b/tests/page-templates/22-early/13-xhr-before-onload-alwayssendxhr.html @@ -0,0 +1,38 @@ +<%= header %> + + +<%= boomerangSnippet %> + + + +<%= footer %> diff --git a/tests/page-templates/22-early/13-xhr-before-onload-alwayssendxhr.js b/tests/page-templates/22-early/13-xhr-before-onload-alwayssendxhr.js new file mode 100644 index 000000000..358a809d3 --- /dev/null +++ b/tests/page-templates/22-early/13-xhr-before-onload-alwayssendxhr.js @@ -0,0 +1,87 @@ +/* eslint-env mocha */ +/* global assert */ + +describe("13-xhr-before-onload-alwayssendxhr", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should get 3 beacons: 1 early, 1 onload, 1 xhr (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 3); + }, + this.skip.bind(this)); + }); + + it("Should have the fisrt beacon be an early beacon", function() { + assert.isDefined(tf.beacons[0].early); + }); + + it("Should have the second beacon be 'navigation' (if NavigationTiming is supported)", function() { + if (t.isNavigationTimingSupported()) { + assert.equal(tf.beacons[1]["rt.start"], "navigation"); + } + else { + this.skip(); + } + }); + + it("Should have the second beacon be 'none' (if NavigationTiming is not supported)", function() { + if (!t.isNavigationTimingSupported()) { + assert.equal(tf.beacons[1]["rt.start"], "none"); + } + else { + this.skip(); + } + }); + + it("Should have the second beacon have a restiming parameter (if ResourceTiming is supported)", function() { + if (t.isResourceTimingSupported()) { + assert.isDefined(tf.beacons[1].restiming); + } + else { + this.skip(); + } + }); + + it("Should have the second beacon have a rt.bmr parameter (if ResourceTiming is supported)", function() { + if (t.isResourceTimingSupported()) { + assert.isDefined(tf.beacons[1]["rt.bmr"]); + } + else { + this.skip(); + } + }); + + it("Should have the second beacon have a t_other parameter", function() { + assert.isDefined(tf.beacons[1].t_other); + + assert.include(tf.beacons[1].t_other, "boomerang"); + + if (t.isNavigationTimingSupported()) { + // these timers are never started, when we check to add them to beacon, + // because they don't have `start` on them, we check `cached_t_start` - which won't + // have a value becaus there's no nav timing + assert.include(tf.beacons[1].t_other, "boomr_fb"); + assert.include(tf.beacons[1].t_other, "boomr_ld"); + assert.include(tf.beacons[1].t_other, "boomr_lat"); + } + else { + this.skip(); + } + }); + + it("Should have the third beacon be an XHR", function() { + assert.equal(tf.beacons[2]["http.initiator"], "xhr"); + }); + + it("Should have the third beacon be missing the rt.bmr parameter", function() { + assert.isUndefined(tf.beacons[2]["rt.bmr"]); + }); + + it("Should have the third beacon be missing the t_other parameter", function() { + assert.isUndefined(tf.beacons[2].t_other); + }); +}); diff --git a/tests/page-templates/22-early/tests.json5 b/tests/page-templates/22-early/tests.json5 new file mode 100644 index 000000000..94f226c5a --- /dev/null +++ b/tests/page-templates/22-early/tests.json5 @@ -0,0 +1,5 @@ +{ + all: { + requires: ["early"] + } +} diff --git a/tests/page-templates/23-mobile/00-mobile-basic.html b/tests/page-templates/23-mobile/00-mobile-basic.html new file mode 100644 index 000000000..f3191c1c1 --- /dev/null +++ b/tests/page-templates/23-mobile/00-mobile-basic.html @@ -0,0 +1,9 @@ +<%= header %> +<%= boomerangSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/23-mobile/00-mobile-basic.js b/tests/page-templates/23-mobile/00-mobile-basic.js new file mode 100644 index 000000000..dc9c99aa7 --- /dev/null +++ b/tests/page-templates/23-mobile/00-mobile-basic.js @@ -0,0 +1,27 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/23-mobile/00-mobile-basic", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have at least one key starting with mob", function() { + if (t.isNetworkAPISupported()) { + var k, + actual = false, + b = tf.beacons[0]; + + for (k in b) { + if (k.indexOf("mob") === 0) { + actual = true; + } + } + + assert.isTrue(actual); + } + }); +}); diff --git a/tests/page-templates/23-mobile/01-mobile-variable-mapping.html b/tests/page-templates/23-mobile/01-mobile-variable-mapping.html new file mode 100644 index 000000000..7603f99ce --- /dev/null +++ b/tests/page-templates/23-mobile/01-mobile-variable-mapping.html @@ -0,0 +1,9 @@ +<%= header %> +<%= boomerangSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/23-mobile/01-mobile-variable-mapping.js b/tests/page-templates/23-mobile/01-mobile-variable-mapping.js new file mode 100644 index 000000000..bc68f7c91 --- /dev/null +++ b/tests/page-templates/23-mobile/01-mobile-variable-mapping.js @@ -0,0 +1,139 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/23-mobile/01-mobile-variable-mapping", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should map type to ct when available", function() { + if (t.isNetworkAPISupported()) { + var connection, + b = tf.beacons[0]; + + if (typeof navigator === "object") { + connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection || navigator.msConnection; + } + + if (connection && "type" in connection && connection.type) { + assert.isTrue("mob.ct" in b); + assert.equal(b["mob.ct"], connection.type); + } + } + }); + + it("Should map bandwidth to bw when available", function() { + if (t.isNetworkAPISupported()) { + var connection, + b = tf.beacons[0]; + + if (typeof navigator === "object") { + connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection || navigator.msConnection; + } + + if (connection && "bandwidth" in connection && connection.bandwidth) { + assert.isTrue("mob.bw" in b); + assert.equal(b["mob.bw"], connection.bandwidth); + } + } + }); + + it("Should map metered to mt when available", function() { + if (t.isNetworkAPISupported()) { + var connection, + b = tf.beacons[0]; + + if (typeof navigator === "object") { + connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection || navigator.msConnection; + } + + if (connection && "metered" in connection && connection.metered) { + assert.isTrue("mob.mt" in b); + assert.equal(b["mob.mt"], connection.metered); + } + } + }); + + it("Should map effectiveType to etype when available", function() { + if (t.isNetworkAPISupported()) { + var connection, + b = tf.beacons[0]; + + if (typeof navigator === "object") { + connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection || navigator.msConnection; + } + + if (connection && "effectiveType" in connection && connection.effectiveType) { + assert.isTrue("mob.etype" in b); + assert.equal(b["mob.etype"], connection.effectiveType); + } + } + }); + + it("Should map downlinkMax to lm when available", function() { + if (t.isNetworkAPISupported()) { + var connection, + b = tf.beacons[0]; + + if (typeof navigator === "object") { + connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection || navigator.msConnection; + } + + if (connection && "downlinkMax" in connection && connection.downlinkMax) { + assert.isTrue("mob.lm" in b); + assert.equal(b["mob.lm"], connection.downlinkMax); + } + } + }); + + it("Should map downlink to dl when available", function() { + if (t.isNetworkAPISupported()) { + var connection, + b = tf.beacons[0]; + + if (typeof navigator === "object") { + connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection || navigator.msConnection; + } + + if (connection && "downlink" in connection && connection.downlink) { + assert.isTrue("mob.dl" in b); + assert.equal(b["mob.dl"], connection.downlink); + } + } + }); + + it("Should map rtt to rtt when available", function() { + if (t.isNetworkAPISupported()) { + var connection, + b = tf.beacons[0]; + + if (typeof navigator === "object") { + connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection || navigator.msConnection; + } + + if (connection && "rtt" in connection && connection.rtt) { + assert.isTrue("mob.rtt" in b); + assert.equal(b["mob.rtt"], connection.rtt); + } + } + }); + + it("Should map saveData to sd when available", function() { + if (t.isNetworkAPISupported()) { + var connection, + b = tf.beacons[0]; + + if (typeof navigator === "object") { + connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection || navigator.msConnection; + } + + if (connection && "saveData" in connection && connection.saveData) { + assert.isTrue("mob.sd" in b); + assert.equal(b["mob.sd"], connection.saveData); + } + } + }); +}); diff --git a/tests/page-templates/23-mobile/tests.json5 b/tests/page-templates/23-mobile/tests.json5 new file mode 100644 index 000000000..06a7aba01 --- /dev/null +++ b/tests/page-templates/23-mobile/tests.json5 @@ -0,0 +1,5 @@ +{ + all: { + requires: ["mobile"] + } +} diff --git a/tests/page-templates/25-cookie/00-cookie-null.html b/tests/page-templates/25-cookie/00-cookie-null.html new file mode 100644 index 000000000..041011714 --- /dev/null +++ b/tests/page-templates/25-cookie/00-cookie-null.html @@ -0,0 +1,12 @@ +<%= header %> +<%= boomerangSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/25-cookie/00-cookie-null.js b/tests/page-templates/25-cookie/00-cookie-null.js new file mode 100644 index 000000000..0fb4adfac --- /dev/null +++ b/tests/page-templates/25-cookie/00-cookie-null.js @@ -0,0 +1,23 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/25-cookie/00-cookie-null", function() { + var tf = BOOMR.plugins.TestFramework; + + it("Should have sent a beacon", function() { + // ensure we fired a beacon ('beacon') + assert.isTrue(tf.fired_onbeacon); + }); + + it("Should not have included rt.ss on the beacon", function() { + assert.isUndefined(tf.lastBeacon()["rt.ss"]); + }); + + it("Should not have included rt.sl on the beacon", function() { + assert.isUndefined(tf.lastBeacon()["rt.sl"]); + }); + + it("Should not have included rt.si on the beacon", function() { + assert.isUndefined(tf.lastBeacon()["rt.si"]); + }); +}); diff --git a/tests/page-templates/25-cookie/01-cookie-deprecated.html b/tests/page-templates/25-cookie/01-cookie-deprecated.html new file mode 100644 index 000000000..e06da560b --- /dev/null +++ b/tests/page-templates/25-cookie/01-cookie-deprecated.html @@ -0,0 +1,16 @@ +<%= header %> + +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/25-cookie/01-cookie-deprecated.js b/tests/page-templates/25-cookie/01-cookie-deprecated.js new file mode 100644 index 000000000..c998e8e27 --- /dev/null +++ b/tests/page-templates/25-cookie/01-cookie-deprecated.js @@ -0,0 +1,17 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/25-cookie/01-cookie-deprecated", function() { + var tf = BOOMR.plugins.TestFramework; + + it("Should have sent a beacon", function() { + // ensure we fired a beacon ('beacon') + assert.isTrue(tf.fired_onbeacon); + }); + + it("Should not have included Session History (rt.sh) in the cookie", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.sh, "Session History should be removed"); + }); +}); diff --git a/tests/page-templates/25-cookie/02-cookie-from-new-session.html b/tests/page-templates/25-cookie/02-cookie-from-new-session.html new file mode 100644 index 000000000..08f6e128a --- /dev/null +++ b/tests/page-templates/25-cookie/02-cookie-from-new-session.html @@ -0,0 +1,11 @@ +<%= header %> +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/25-cookie/02-cookie-from-new-session.js b/tests/page-templates/25-cookie/02-cookie-from-new-session.js new file mode 100644 index 000000000..c8593c5be --- /dev/null +++ b/tests/page-templates/25-cookie/02-cookie-from-new-session.js @@ -0,0 +1,137 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/25-cookie/02-cookie-from-new-session", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a beacon", function() { + // ensure we fired a beacon ('beacon') + assert.isTrue(tf.fired_onbeacon); + }); + + it("Should have set the Compression Level (z)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isDefined(cookie.z); + }); + + it("Should have set the Session ID (si)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isDefined(cookie.si); + assert.match(cookie.si, /-/); + }); + + it("Should have a Session Length (sl) of 1", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(cookie.sl, 1); + }); + + it("Should have a Session Start (ss) of around now", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + var startTime = +(new Date()); + var ss = parseInt(cookie.ss, 36); + + // greater than 60 seconds ago + assert.operator(ss, ">=", startTime - 60000); + + // not in the future + assert.operator(ss, "<", startTime); + }); + + it("Should have a Total Time (tt) of the same duration as the navigation", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(parseInt(cookie.tt, 36), tf.lastBeacon().t_done || 0); + }); + + it("Should not have Off By One (obo) set (if NavigationTiming is supported)", function() { + if (!t.isNavigationTimingSupported()) { + return this.skip(); + } + + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.obo); + }); + + it("Should have Off By One (obo) set (if NavigationTiming is not supported)", function() { + if (t.isNavigationTimingSupported()) { + return this.skip(); + } + + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(cookie.obo, 1); + }); + + it("Should have set the Beacon URL (bcn)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(cookie.bcn, "/beacon"); + }); + + it("Should have set the Beacon Domain (dm)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(cookie.dm, "boomerang-test.local"); + }); + + it("Should not have set Session Expiry (se)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.se); + }); + + it("Should not have set Rate Limited (rl)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.rl); + }); + + it("Should have set the Load Time (ld)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + var startTime = +(new Date()); + var ld = parseInt(cookie.ld, 36) + parseInt(cookie.ss, 36); + + // greater than 60 seconds ago + assert.operator(ld, ">=", startTime - 60000); + + // not in the future + assert.operator(ld, "<", startTime); + }); + + it("Should not have set Before Unload Time (ul)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.ul); + }); + + it("Should not have set Unload Time (hd)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.hd); + }); + + it("Should not have set Click Time (cl)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.cl); + }); + + it("Should not have set Referrer (r)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.r); + }); + + it("Should not have set New URL (nu)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.nu); + }); +}); diff --git a/tests/page-templates/25-cookie/03-cookie-from-previous-nav.html b/tests/page-templates/25-cookie/03-cookie-from-previous-nav.html new file mode 100644 index 000000000..4b04e53ac --- /dev/null +++ b/tests/page-templates/25-cookie/03-cookie-from-previous-nav.html @@ -0,0 +1,23 @@ +<%= header %> + +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/25-cookie/03-cookie-from-previous-nav.js b/tests/page-templates/25-cookie/03-cookie-from-previous-nav.js new file mode 100644 index 000000000..be7e9895f --- /dev/null +++ b/tests/page-templates/25-cookie/03-cookie-from-previous-nav.js @@ -0,0 +1,132 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["lastNav"]); + +describe("e2e/25-cookie/03-cookie-from-previous-nav", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a beacon", function() { + // ensure we fired a beacon ('beacon') + assert.isTrue(tf.fired_onbeacon); + }); + + it("Should have set the Compression Level (z)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isDefined(cookie.z); + }); + + it("Should have set the Session ID (si) of 'abc123-1234'", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(cookie.si, "abc123-1234"); + }); + + it("Should have a Session Length (sl) of 11", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(parseInt(cookie.sl, 36), 11); + }); + + it("Should have a Session Start (ss) of around the last navigation", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(parseInt(cookie.ss, 36), window.lastNav); + }); + + it("Should have a Total Time (tt) of the same duration as the navigation plus the last nav of 1000", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(parseInt(cookie.tt, 36), (tf.lastBeacon().t_done || 0) + 1000); + }); + + it("Should not have Off By One (obo) set (if NavigationTiming is supported)", function() { + if (!t.isNavigationTimingSupported()) { + return this.skip(); + } + + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.obo); + }); + + it("Should have Off By One (obo) set (if NavigationTiming is not supported)", function() { + if (t.isNavigationTimingSupported()) { + return this.skip(); + } + + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(cookie.obo, 1); + }); + + it("Should have set the Beacon URL (bcn)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(cookie.bcn, "/beacon"); + }); + + it("Should have set the Beacon Domain (dm)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(cookie.dm, "boomerang-test.local"); + }); + + it("Should not have set Session Expiry (se)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.se); + }); + + it("Should not have set Rate Limited (rl)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.rl); + }); + + it("Should have set the Load Time (ld)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + var startTime = +(new Date()); + var ld = parseInt(cookie.ld, 36) + parseInt(cookie.ss, 36); + + // greater than 60 seconds ago + assert.operator(ld, ">=", startTime - 60000); + + // not in the future + assert.operator(ld, "<", startTime); + }); + + it("Should not have set Before Unload Time (ul)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.ul); + }); + + it("Should not have set Unload Time (hd)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.hd); + }); + + it("Should not have set Click Time (cl)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.cl); + }); + + it("Should not have set Referrer (r)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.r); + }); + + it("Should not have set New URL (nu)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.nu); + }); +}); diff --git a/tests/page-templates/25-cookie/04-cookie-from-previous-nav-referrer.html b/tests/page-templates/25-cookie/04-cookie-from-previous-nav-referrer.html new file mode 100644 index 000000000..793c20232 --- /dev/null +++ b/tests/page-templates/25-cookie/04-cookie-from-previous-nav-referrer.html @@ -0,0 +1,24 @@ +<%= header %> + +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/25-cookie/04-cookie-from-previous-nav-referrer.js b/tests/page-templates/25-cookie/04-cookie-from-previous-nav-referrer.js new file mode 100644 index 000000000..fd80375a3 --- /dev/null +++ b/tests/page-templates/25-cookie/04-cookie-from-previous-nav-referrer.js @@ -0,0 +1,132 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["lastNav"]); + +describe("e2e/25-cookie/04-cookie-from-previous-nav-referrer", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a beacon", function() { + // ensure we fired a beacon ('beacon') + assert.isTrue(tf.fired_onbeacon); + }); + + it("Should have set the Compression Level (z)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isDefined(cookie.z); + }); + + it("Should have set the Session ID (si) of 'abc123-1234'", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(cookie.si, "abc123-1234"); + }); + + it("Should have a Session Length (sl) of 2", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(cookie.sl, 2); + }); + + it("Should have a Session Start (ss) of around the last navigation", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(parseInt(cookie.ss, 36), window.lastNav); + }); + + it("Should have a Total Time (tt) of the same duration as the navigation plus the last nav of 1000", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(parseInt(cookie.tt, 36), (tf.lastBeacon().t_done || 0) + 1000); + }); + + it("Should not have Off By One (obo) set (if NavigationTiming is supported)", function() { + if (!t.isNavigationTimingSupported()) { + return this.skip(); + } + + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.obo); + }); + + it("Should have Off By One (obo) set (if NavigationTiming is not supported)", function() { + if (t.isNavigationTimingSupported()) { + return this.skip(); + } + + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(cookie.obo, 1); + }); + + it("Should have set the Beacon URL (bcn)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(cookie.bcn, "/beacon"); + }); + + it("Should have set the Beacon Domain (dm)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(cookie.dm, "boomerang-test.local"); + }); + + it("Should not have set Session Expiry (se)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.se); + }); + + it("Should not have set Rate Limited (rl)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.rl); + }); + + it("Should have set the Load Time (ld)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + var startTime = +(new Date()); + var ld = parseInt(cookie.ld, 36) + parseInt(cookie.ss, 36); + + // greater than 60 seconds ago + assert.operator(ld, ">=", startTime - 60000); + + // not in the future + assert.operator(ld, "<", startTime); + }); + + it("Should not have set Before Unload Time (ul)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.ul); + }); + + it("Should not have set Unload Time (hd)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.hd); + }); + + it("Should not have set Click Time (cl)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.cl); + }); + + it("Should not have set Referrer (r)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.r); + }); + + it("Should not have set New URL (nu)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.nu); + }); +}); diff --git a/tests/page-templates/25-cookie/05-cookie-after-beforeunload.html b/tests/page-templates/25-cookie/05-cookie-after-beforeunload.html new file mode 100644 index 000000000..bcd4cff2f --- /dev/null +++ b/tests/page-templates/25-cookie/05-cookie-after-beforeunload.html @@ -0,0 +1,32 @@ +<%= header %> + +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/25-cookie/05-cookie-after-beforeunload.js b/tests/page-templates/25-cookie/05-cookie-after-beforeunload.js new file mode 100644 index 000000000..ea8154ceb --- /dev/null +++ b/tests/page-templates/25-cookie/05-cookie-after-beforeunload.js @@ -0,0 +1,148 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["lastNav", "unloadTimeFired"]); + +describe("e2e/25-cookie/05-cookie-after-beforeunload", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a beacon", function() { + // ensure we fired a beacon ('beacon') + assert.isTrue(tf.fired_onbeacon); + }); + + it("Should have set the Compression Level (z)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isDefined(cookie.z); + }); + + it("Should have set the Session ID (si) of 'abc123-1234'", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(cookie.si, "abc123-1234"); + }); + + it("Should have a Session Length (sl) of 2", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(cookie.sl, 2); + }); + + it("Should have a Session Start (ss) of around the last navigation", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(parseInt(cookie.ss, 36), window.lastNav); + }); + + it("Should have a Total Time (tt) of the same duration as the navigation plus the last nav of 1000", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(parseInt(cookie.tt, 36), (tf.lastBeacon().t_done || 0) + 1000); + }); + + it("Should not have Off By One (obo) set (if NavigationTiming is supported)", function() { + if (!t.isNavigationTimingSupported()) { + return this.skip(); + } + + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.obo); + }); + + it("Should have Off By One (obo) set (if NavigationTiming is not supported)", function() { + if (t.isNavigationTimingSupported()) { + return this.skip(); + } + + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(cookie.obo, 1); + }); + + it("Should have set the Beacon URL (bcn)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(cookie.bcn, "/beacon"); + }); + + it("Should have set the Beacon Domain (dm)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(cookie.dm, "boomerang-test.local"); + }); + + it("Should not have set Session Expiry (se)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.se); + }); + + it("Should not have set Rate Limited (rl)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.rl); + }); + + it("Should have set the Load Time (ld)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + var startTime = +(new Date()); + var ld = parseInt(cookie.ld, 36) + parseInt(cookie.ss, 36); + + // greater than 60 seconds ago + assert.operator(ld, ">=", startTime - 60000); + + // not in the future + assert.operator(ld, "<", startTime); + }); + + it("Should have set Before Unload Time (ul)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + var ul = parseInt(cookie.ul, 36) + parseInt(cookie.ss, 36); + + assert.closeTo(ul, unloadTimeFired, 100); + }); + + it("Should not have set Unload Time (hd)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.hd); + }); + + it("Should not have set Click Time (cl)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.cl); + }); + + it("Should have set Referrer (r) (if NavigationTiming isn't supported)", function() { + if (t.isNavigationTimingSupported()) { + return this.skip(); + } + + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(cookie.r, t.hashString(document.URL)); + }); + + it("Should not have set Referrer (r) (if NavigationTiming is supported)", function() { + if (!t.isNavigationTimingSupported()) { + return this.skip(); + } + + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.r); + }); + + it("Should not have set New URL (nu)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.nu); + }); +}); diff --git a/tests/page-templates/25-cookie/06-cookie-after-unload.html b/tests/page-templates/25-cookie/06-cookie-after-unload.html new file mode 100644 index 000000000..513d634b8 --- /dev/null +++ b/tests/page-templates/25-cookie/06-cookie-after-unload.html @@ -0,0 +1,32 @@ +<%= header %> + +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/25-cookie/06-cookie-after-unload.js b/tests/page-templates/25-cookie/06-cookie-after-unload.js new file mode 100644 index 000000000..8fdda2ae0 --- /dev/null +++ b/tests/page-templates/25-cookie/06-cookie-after-unload.js @@ -0,0 +1,148 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["lastNav", "unloadTimeFired"]); + +describe("e2e/25-cookie/06-cookie-after-unload", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a beacon", function() { + // ensure we fired a beacon ('beacon') + assert.isTrue(tf.fired_onbeacon); + }); + + it("Should have set the Compression Level (z)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isDefined(cookie.z); + }); + + it("Should have set the Session ID (si) of 'abc123-1234'", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(cookie.si, "abc123-1234"); + }); + + it("Should have a Session Length (sl) of 2", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(cookie.sl, 2); + }); + + it("Should have a Session Start (ss) of around the last navigation", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(parseInt(cookie.ss, 36), window.lastNav); + }); + + it("Should have a Total Time (tt) of the same duration as the navigation plus the last nav of 1000", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(parseInt(cookie.tt, 36), (tf.lastBeacon().t_done || 0) + 1000); + }); + + it("Should not have Off By One (obo) set (if NavigationTiming is supported)", function() { + if (!t.isNavigationTimingSupported()) { + return this.skip(); + } + + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.obo); + }); + + it("Should have Off By One (obo) set (if NavigationTiming is not supported)", function() { + if (t.isNavigationTimingSupported()) { + return this.skip(); + } + + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(cookie.obo, 1); + }); + + it("Should have set the Beacon URL (bcn)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(cookie.bcn, "/beacon"); + }); + + it("Should have set the Beacon Domain (dm)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(cookie.dm, "boomerang-test.local"); + }); + + it("Should not have set Session Expiry (se)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.se); + }); + + it("Should not have set Rate Limited (rl)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.rl); + }); + + it("Should have set the Load Time (ld)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + var startTime = +(new Date()); + var ld = parseInt(cookie.ld, 36) + parseInt(cookie.ss, 36); + + // greater than 60 seconds ago + assert.operator(ld, ">=", startTime - 60000); + + // not in the future + assert.operator(ld, "<", startTime); + }); + + it("Should not have set Before Unload Time (ul)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.ul); + }); + + it("Should have set Unload Time (hd)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + var hd = parseInt(cookie.hd, 36) + parseInt(cookie.ss, 36); + + assert.closeTo(hd, unloadTimeFired, 100); + }); + + it("Should not have set Click Time (cl)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.cl); + }); + + it("Should have set Referrer (r) (if NavigationTiming isn't supported)", function() { + if (t.isNavigationTimingSupported()) { + return this.skip(); + } + + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(cookie.r, t.hashString(document.URL)); + }); + + it("Should not have set Referrer (r) (if NavigationTiming is supported)", function() { + if (!t.isNavigationTimingSupported()) { + return this.skip(); + } + + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.r); + }); + + it("Should not have set New URL (nu)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.nu); + }); +}); diff --git a/tests/page-templates/25-cookie/07-cookie-referrer-non-nt.html b/tests/page-templates/25-cookie/07-cookie-referrer-non-nt.html new file mode 100644 index 000000000..9b11d177f --- /dev/null +++ b/tests/page-templates/25-cookie/07-cookie-referrer-non-nt.html @@ -0,0 +1,22 @@ +<%= header %> + +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/25-cookie/07-cookie-referrer-non-nt.js b/tests/page-templates/25-cookie/07-cookie-referrer-non-nt.js new file mode 100644 index 000000000..1e5b8e1c7 --- /dev/null +++ b/tests/page-templates/25-cookie/07-cookie-referrer-non-nt.js @@ -0,0 +1,17 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/25-cookie/07-cookie-referrer-non-nt", function() { + var tf = BOOMR.plugins.TestFramework; + + it("Should have sent a beacon", function() { + // ensure we fired a beacon ('beacon') + assert.isTrue(tf.fired_onbeacon); + }); + + it("Should have set Referrer (r)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(cookie.r, BOOMR.utils.hashString(document.URL)); + }); +}); diff --git a/tests/page-templates/25-cookie/08-cookie-referrer-nt.html b/tests/page-templates/25-cookie/08-cookie-referrer-nt.html new file mode 100644 index 000000000..ed748e47a --- /dev/null +++ b/tests/page-templates/25-cookie/08-cookie-referrer-nt.html @@ -0,0 +1,28 @@ +<%= header %> + +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/25-cookie/08-cookie-referrer-nt.js b/tests/page-templates/25-cookie/08-cookie-referrer-nt.js new file mode 100644 index 000000000..cddad412a --- /dev/null +++ b/tests/page-templates/25-cookie/08-cookie-referrer-nt.js @@ -0,0 +1,17 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/25-cookie/08-cookie-referrer-nt", function() { + var tf = BOOMR.plugins.TestFramework; + + it("Should have sent a beacon", function() { + // ensure we fired a beacon ('beacon') + assert.isTrue(tf.fired_onbeacon); + }); + + it("Should not have set Referrer (r)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.r); + }); +}); diff --git a/tests/page-templates/25-cookie/09-cookie-from-previous-nav-referrer-non-nt.html b/tests/page-templates/25-cookie/09-cookie-from-previous-nav-referrer-non-nt.html new file mode 100644 index 000000000..239903fa8 --- /dev/null +++ b/tests/page-templates/25-cookie/09-cookie-from-previous-nav-referrer-non-nt.html @@ -0,0 +1,30 @@ +<%= header %> + +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/25-cookie/09-cookie-from-previous-nav-referrer-non-nt.js b/tests/page-templates/25-cookie/09-cookie-from-previous-nav-referrer-non-nt.js new file mode 100644 index 000000000..2786e040d --- /dev/null +++ b/tests/page-templates/25-cookie/09-cookie-from-previous-nav-referrer-non-nt.js @@ -0,0 +1,133 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["lastNav", "lastLoad", "lastUnload"]); + +describe("e2e/25-cookie/09-cookie-from-previous-nav-referrer-non-nt", function() { + var tf = BOOMR.plugins.TestFramework; + + it("Should have sent a beacon", function() { + // ensure we fired a beacon ('beacon') + assert.isTrue(tf.fired_onbeacon); + }); + + describe("Cookie", function() { + it("Should have set the Compression Level (z)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isDefined(cookie.z); + }); + + it("Should have set the Session ID (si) of 'abc123-1234'", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(cookie.si, "abc123-1234"); + }); + + it("Should have a Session Length (sl) of 2", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(cookie.sl, 2); + }); + + it("Should have a Session Start (ss) of around the last navigation", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(parseInt(cookie.ss, 36), window.lastNav); + }); + + it("Should have a Total Time (tt) of the same duration as the navigation plus the last nav of 1000", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(parseInt(cookie.tt, 36), tf.lastBeacon().t_done + 1000); + }); + + it("Should not have Off By One (obo) set", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.obo); + }); + + it("Should have set the Beacon URL (bcn)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(cookie.bcn, "/beacon"); + }); + + it("Should have set the Beacon Domain (dm)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(cookie.dm, "boomerang-test.local"); + }); + + it("Should not have set Session Expiry (se)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.se); + }); + + it("Should not have set Rate Limited (rl)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.rl); + }); + + it("Should have set the Load Time (ld)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + var startTime = +(new Date()); + var ld = parseInt(cookie.ld, 36) + parseInt(cookie.ss, 36); + + // greater than 60 seconds ago + assert.operator(ld, ">=", startTime - 60000); + + // not in the future + assert.operator(ld, "<", startTime); + }); + + it("Should not have set Before Unload Time (ul)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.ul); + }); + + it("Should not have set Unload Time (hd)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.hd); + }); + + it("Should not have set Click Time (cl)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.cl); + }); + + it("Should not have set Referrer (r)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.r); + }); + + it("Should not have set New URL (nu)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.nu); + }); + }); + + describe("Beacon", function() { + it("Should have set rt.start=cookie", function() { + assert.equal(tf.lastBeacon()["rt.start"], "cookie"); + }); + + it("Should have a navigation time (t_done) of around 9 seconds", function() { + assert.closeTo(parseInt(tf.lastBeacon().t_done, 10), 9000, 2000); + }); + + it("Should have a navigation start time (rt.tstart) of around the cookie ul", function() { + assert.equal(parseInt(tf.lastBeacon()["rt.tstart"], 10), window.lastUnload); + }); + }); +}); diff --git a/tests/page-templates/25-cookie/10-cookie-from-previous-nav-bad-referrer-non-nt.html b/tests/page-templates/25-cookie/10-cookie-from-previous-nav-bad-referrer-non-nt.html new file mode 100644 index 000000000..8422e1ad2 --- /dev/null +++ b/tests/page-templates/25-cookie/10-cookie-from-previous-nav-bad-referrer-non-nt.html @@ -0,0 +1,30 @@ +<%= header %> + +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/25-cookie/10-cookie-from-previous-nav-bad-referrer-non-nt.js b/tests/page-templates/25-cookie/10-cookie-from-previous-nav-bad-referrer-non-nt.js new file mode 100644 index 000000000..426cd2e9b --- /dev/null +++ b/tests/page-templates/25-cookie/10-cookie-from-previous-nav-bad-referrer-non-nt.js @@ -0,0 +1,133 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["lastNav", "lastLoad", "lastUnload"]); + +describe("e2e/25-cookie/10-cookie-from-previous-nav-bad-referrer-non-nt", function() { + var tf = BOOMR.plugins.TestFramework; + + it("Should have sent a beacon", function() { + // ensure we fired a beacon ('beacon') + assert.isTrue(tf.fired_onbeacon); + }); + + describe("Cookie", function() { + it("Should have set the Compression Level (z)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isDefined(cookie.z); + }); + + it("Should have set the Session ID (si) of 'abc123-1234'", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(cookie.si, "abc123-1234"); + }); + + it("Should have a Session Length (sl) of 2", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(cookie.sl, 2); + }); + + it("Should have a Session Start (ss) of around the last navigation", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(parseInt(cookie.ss, 36), window.lastNav); + }); + + it("Should have a Total Time (tt) of the last nav of 1000", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(parseInt(cookie.tt, 36), 1000); + }); + + it("Should have Off By One (obo) set to 1", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(parseInt(cookie.obo, 36), 1); + }); + + it("Should have set the Beacon URL (bcn)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(cookie.bcn, "/beacon"); + }); + + it("Should have set the Beacon Domain (dm)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(cookie.dm, "boomerang-test.local"); + }); + + it("Should not have set Session Expiry (se)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.se); + }); + + it("Should not have set Rate Limited (rl)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.rl); + }); + + it("Should have set the Load Time (ld)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + var startTime = +(new Date()); + var ld = parseInt(cookie.ld, 36) + parseInt(cookie.ss, 36); + + // greater than 60 seconds ago + assert.operator(ld, ">=", startTime - 60000); + + // not in the future + assert.operator(ld, "<", startTime); + }); + + it("Should not have set Before Unload Time (ul)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.ul); + }); + + it("Should not have set Unload Time (hd)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.hd); + }); + + it("Should not have set Click Time (cl)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.cl); + }); + + it("Should not have set Referrer (r)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.r); + }); + + it("Should not have set New URL (nu)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.nu); + }); + }); + + describe("Beacon", function() { + it("Should have set rt.start=none", function() { + assert.equal(tf.lastBeacon()["rt.start"], "none"); + }); + + it("Should be missing a navigation time (t_done)", function() { + assert.isUndefined(tf.lastBeacon().t_done); + }); + + it("Should be missing a navigation start time (rt.tstart)", function() { + assert.isUndefined(tf.lastBeacon()["rt.tstart"]); + }); + }); +}); diff --git a/tests/page-templates/25-cookie/11-cookie-session-reset.html b/tests/page-templates/25-cookie/11-cookie-session-reset.html new file mode 100644 index 000000000..2c0d95fc0 --- /dev/null +++ b/tests/page-templates/25-cookie/11-cookie-session-reset.html @@ -0,0 +1,24 @@ +<%= header %> + +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/25-cookie/11-cookie-session-reset.js b/tests/page-templates/25-cookie/11-cookie-session-reset.js new file mode 100644 index 000000000..f059a9891 --- /dev/null +++ b/tests/page-templates/25-cookie/11-cookie-session-reset.js @@ -0,0 +1,139 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["lastNav"]); + +describe("e2e/25-cookie/11-cookie-session-reset", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a beacon", function() { + // ensure we fired a beacon ('beacon') + assert.isTrue(tf.fired_onbeacon); + }); + + it("Should have set the Compression Level (z)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isDefined(cookie.z); + }); + + it("Should have set the Session ID (si) of 'abc123-1234'", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(cookie.si, "abc123-1234"); + }); + + it("Should have a Session Length (sl) of 1", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(cookie.sl, 1); + }); + + it("Should have a Session Start (ss) of around now", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + var startTime = +(new Date()); + var ss = parseInt(cookie.ss, 36); + + // greater than 60 seconds ago + assert.operator(ss, ">=", startTime - 60000); + + // not in the future + assert.operator(ss, "<", startTime); + }); + + it("Should have a Total Time (tt) of the same duration as the navigation plus", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(parseInt(cookie.tt, 36), tf.lastBeacon().t_done || 0); + }); + + it("Should not have Off By One (obo) set (if NavigationTiming is supported)", function() { + if (!t.isNavigationTimingSupported()) { + return this.skip(); + } + + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.obo); + }); + + it("Should have Off By One (obo) set (if NavigationTiming is not supported)", function() { + if (t.isNavigationTimingSupported()) { + return this.skip(); + } + + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(cookie.obo, 1); + }); + + it("Should have set the Beacon URL (bcn)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(cookie.bcn, "/beacon"); + }); + + it("Should have set the Beacon Domain (dm)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(cookie.dm, "boomerang-test.local"); + }); + + it("Should not have set Session Expiry (se)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.se); + }); + + it("Should not have set Rate Limited (rl)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.rl); + }); + + it("Should have set the Load Time (ld)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + var startTime = +(new Date()); + var ld = parseInt(cookie.ld, 36) + parseInt(cookie.ss, 36); + + // greater than 60 seconds ago + assert.operator(ld, ">=", startTime - 60000); + + // not in the future + assert.operator(ld, "<", startTime); + }); + + it("Should not have set Before Unload Time (ul)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.ul); + }); + + it("Should not have set Unload Time (hd)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.hd); + }); + + it("Should not have set Click Time (cl)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.cl); + }); + + it("Should not have set Referrer (r)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.r); + }); + + it("Should not have set New URL (nu)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.nu); + }); +}); diff --git a/tests/page-templates/25-cookie/12-cookie-from-previous-nav-click-non-nt.html b/tests/page-templates/25-cookie/12-cookie-from-previous-nav-click-non-nt.html new file mode 100644 index 000000000..47e82c9c4 --- /dev/null +++ b/tests/page-templates/25-cookie/12-cookie-from-previous-nav-click-non-nt.html @@ -0,0 +1,29 @@ +<%= header %> + +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/25-cookie/12-cookie-from-previous-nav-click-non-nt.js b/tests/page-templates/25-cookie/12-cookie-from-previous-nav-click-non-nt.js new file mode 100644 index 000000000..deee58e1d --- /dev/null +++ b/tests/page-templates/25-cookie/12-cookie-from-previous-nav-click-non-nt.js @@ -0,0 +1,133 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["lastNav", "lastLoad", "lastUnload"]); + +describe("e2e/25-cookie/12-cookie-from-previous-nav-click-non-nt", function() { + var tf = BOOMR.plugins.TestFramework; + + it("Should have sent a beacon", function() { + // ensure we fired a beacon ('beacon') + assert.isTrue(tf.fired_onbeacon); + }); + + describe("Cookie", function() { + it("Should have set the Compression Level (z)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isDefined(cookie.z); + }); + + it("Should have set the Session ID (si) of 'abc123-1234'", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(cookie.si, "abc123-1234"); + }); + + it("Should have a Session Length (sl) of 2", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(parseInt(cookie.sl, 36), 2); + }); + + it("Should have a Session Start (ss) of around the last navigation", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(parseInt(cookie.ss, 36), window.lastNav); + }); + + it("Should have a Total Time (tt) of the same duration as the navigation plus the last nav of 1000", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(parseInt(cookie.tt, 36), tf.lastBeacon().t_done + 1000); + }); + + it("Should not have Off By One (obo) set", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.obo); + }); + + it("Should have set the Beacon URL (bcn)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(cookie.bcn, "/beacon"); + }); + + it("Should have set the Beacon Domain (dm)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(cookie.dm, "boomerang-test.local"); + }); + + it("Should not have set Session Expiry (se)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.se); + }); + + it("Should not have set Rate Limited (rl)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.rl); + }); + + it("Should have set the Load Time (ld)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + var startTime = +(new Date()); + var ld = parseInt(cookie.ld, 36) + parseInt(cookie.ss, 36); + + // greater than 60 seconds ago + assert.operator(ld, ">=", startTime - 60000); + + // not in the future + assert.operator(ld, "<", startTime); + }); + + it("Should not have set Before Unload Time (ul)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.ul); + }); + + it("Should not have set Unload Time (hd)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.hd); + }); + + it("Should not have set Click Time (cl)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.cl); + }); + + it("Should not have set Referrer (r)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.r); + }); + + it("Should not have set New URL (nu)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.nu); + }); + }); + + describe("Beacon", function() { + it("Should have set rt.start=cookie", function() { + assert.equal(tf.lastBeacon()["rt.start"], "cookie"); + }); + + it("Should have a navigation time (t_done) of around 9 seconds", function() { + assert.closeTo(parseInt(tf.lastBeacon().t_done, 10), 9000, 2000); + }); + + it("Should have a navigation start time (rt.tstart) of around the cookie ul", function() { + assert.equal(parseInt(tf.lastBeacon()["rt.tstart"], 10), window.lastUnload); + }); + }); +}); diff --git a/tests/page-templates/25-cookie/13-cookie-from-previous-nav-click-different-non-nt.html b/tests/page-templates/25-cookie/13-cookie-from-previous-nav-click-different-non-nt.html new file mode 100644 index 000000000..26d14469b --- /dev/null +++ b/tests/page-templates/25-cookie/13-cookie-from-previous-nav-click-different-non-nt.html @@ -0,0 +1,29 @@ +<%= header %> + +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/25-cookie/13-cookie-from-previous-nav-click-different-non-nt.js b/tests/page-templates/25-cookie/13-cookie-from-previous-nav-click-different-non-nt.js new file mode 100644 index 000000000..a947128a0 --- /dev/null +++ b/tests/page-templates/25-cookie/13-cookie-from-previous-nav-click-different-non-nt.js @@ -0,0 +1,133 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["lastNav", "lastLoad", "lastUnload"]); + +describe("e2e/25-cookie/13-cookie-from-previous-nav-click-different-non-nt", function() { + var tf = BOOMR.plugins.TestFramework; + + it("Should have sent a beacon", function() { + // ensure we fired a beacon ('beacon') + assert.isTrue(tf.fired_onbeacon); + }); + + describe("Cookie", function() { + it("Should have set the Compression Level (z)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isDefined(cookie.z); + }); + + it("Should have set the Session ID (si) of 'abc123-1234'", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(cookie.si, "abc123-1234"); + }); + + it("Should have a Session Length (sl) of 2", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(parseInt(cookie.sl, 36), 2); + }); + + it("Should have a Session Start (ss) of around the last navigation", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(parseInt(cookie.ss, 36), window.lastNav); + }); + + it("Should have a Total Time (tt) of the last nav of 1000", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(parseInt(cookie.tt, 36), 1000); + }); + + it("Should have Off By One (obo) set to 1", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(parseInt(cookie.obo, 36), 1); + }); + + it("Should have set the Beacon URL (bcn)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(cookie.bcn, "/beacon"); + }); + + it("Should have set the Beacon Domain (dm)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(cookie.dm, "boomerang-test.local"); + }); + + it("Should not have set Session Expiry (se)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.se); + }); + + it("Should not have set Rate Limited (rl)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.rl); + }); + + it("Should have set the Load Time (ld)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + var startTime = +(new Date()); + var ld = parseInt(cookie.ld, 36) + parseInt(cookie.ss, 36); + + // greater than 60 seconds ago + assert.operator(ld, ">=", startTime - 60000); + + // not in the future + assert.operator(ld, "<", startTime); + }); + + it("Should not have set Before Unload Time (ul)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.ul); + }); + + it("Should not have set Unload Time (hd)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.hd); + }); + + it("Should not have set Click Time (cl)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.cl); + }); + + it("Should not have set Referrer (r)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.r); + }); + + it("Should not have set New URL (nu)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.nu); + }); + }); + + describe("Beacon", function() { + it("Should have set rt.start=none", function() { + assert.equal(tf.lastBeacon()["rt.start"], "none"); + }); + + it("Should be missing a navigation time (t_done)", function() { + assert.isUndefined(tf.lastBeacon().t_done); + }); + + it("Should be missing a navigation start time (rt.tstart)", function() { + assert.isUndefined(tf.lastBeacon()["rt.tstart"]); + }); + }); +}); diff --git a/tests/page-templates/25-cookie/14-cookie-non-compressed.html b/tests/page-templates/25-cookie/14-cookie-non-compressed.html new file mode 100644 index 000000000..a69d7dda0 --- /dev/null +++ b/tests/page-templates/25-cookie/14-cookie-non-compressed.html @@ -0,0 +1,22 @@ +<%= header %> + +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/25-cookie/14-cookie-non-compressed.js b/tests/page-templates/25-cookie/14-cookie-non-compressed.js new file mode 100644 index 000000000..0acf55b15 --- /dev/null +++ b/tests/page-templates/25-cookie/14-cookie-non-compressed.js @@ -0,0 +1,132 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["lastNav"]); + +describe("e2e/25-cookie/14-cookie-non-compressed", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a beacon", function() { + // ensure we fired a beacon ('beacon') + assert.isTrue(tf.fired_onbeacon); + }); + + it("Should have set the Compression Level (z)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isDefined(cookie.z); + }); + + it("Should have set the Session ID (si) of 'abc123-1234'", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(cookie.si, "abc123-1234"); + }); + + it("Should have a Session Length (sl) of 2", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(cookie.sl, 2); + }); + + it("Should have a Session Start (ss) of around the last navigation", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(parseInt(cookie.ss, 36), window.lastNav); + }); + + it("Should have a Total Time (tt) of the same duration as the navigation plus the last nav of 1000", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(parseInt(cookie.tt, 36), (tf.lastBeacon().t_done || 0) + 1000); + }); + + it("Should not have Off By One (obo) set (if NavigationTiming is supported)", function() { + if (!t.isNavigationTimingSupported()) { + return this.skip(); + } + + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.obo); + }); + + it("Should have Off By One (obo) set (if NavigationTiming is not supported)", function() { + if (t.isNavigationTimingSupported()) { + return this.skip(); + } + + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(cookie.obo, 1); + }); + + it("Should have set the Beacon URL (bcn)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(cookie.bcn, "/beacon"); + }); + + it("Should have set the Beacon Domain (dm)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(cookie.dm, "boomerang-test.local"); + }); + + it("Should not have set Session Expiry (se)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.se); + }); + + it("Should not have set Rate Limited (rl)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.rl); + }); + + it("Should have set the Load Time (ld)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + var startTime = +(new Date()); + var ld = parseInt(cookie.ld, 36) + parseInt(cookie.ss, 36); + + // greater than 60 seconds ago + assert.operator(ld, ">=", startTime - 60000); + + // not in the future + assert.operator(ld, "<", startTime); + }); + + it("Should not have set Before Unload Time (ul)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.ul); + }); + + it("Should not have set Unload Time (hd)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.hd); + }); + + it("Should not have set Click Time (cl)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.cl); + }); + + it("Should not have set Referrer (r)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.r); + }); + + it("Should not have set New URL (nu)", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isUndefined(cookie.nu); + }); +}); diff --git a/tests/page-templates/25-cookie/15-session-expiry.html b/tests/page-templates/25-cookie/15-session-expiry.html new file mode 100644 index 000000000..a0dfe1806 --- /dev/null +++ b/tests/page-templates/25-cookie/15-session-expiry.html @@ -0,0 +1,14 @@ +<%= header %> +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/25-cookie/15-session-expiry.js b/tests/page-templates/25-cookie/15-session-expiry.js new file mode 100644 index 000000000..196659d2c --- /dev/null +++ b/tests/page-templates/25-cookie/15-session-expiry.js @@ -0,0 +1,17 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/25-cookie/15-session-expiry", function() { + var tf = BOOMR.plugins.TestFramework; + + it("Should have sent a beacon", function() { + // ensure we fired a beacon ('beacon') + assert.isTrue(tf.fired_onbeacon); + }); + + it("Should have a Session Expiry (se) of 100", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.equal(parseInt(cookie.se, 36), 100); + }); +}); diff --git a/tests/page-templates/25-cookie/16-cookie-subdomain.html b/tests/page-templates/25-cookie/16-cookie-subdomain.html new file mode 100644 index 000000000..39c395eb6 --- /dev/null +++ b/tests/page-templates/25-cookie/16-cookie-subdomain.html @@ -0,0 +1,13 @@ +<%= header %> +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/25-cookie/16-cookie-subdomain.js b/tests/page-templates/25-cookie/16-cookie-subdomain.js new file mode 100644 index 000000000..c21daf305 --- /dev/null +++ b/tests/page-templates/25-cookie/16-cookie-subdomain.js @@ -0,0 +1,23 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/25-cookie/16-cookie-subdomain", function() { + var tf = BOOMR.plugins.TestFramework; + + it("Should have sent a beacon", function() { + // ensure we fired a beacon ('beacon') + assert.isTrue(tf.fired_onbeacon); + }); + + it("Should have only set a single cookie", function() { + assert.equal(document.cookie.split("; ").length, 1); + }); + + it("Should have set the cookie on the www domain (if running from www)", function() { + if (document.location.host.indexOf("www.") === -1) { + return this.skip(); + } + + assert.isTrue(BOOMR.utils.getCookie("RT").indexOf("www.") !== -1); + }); +}); diff --git a/tests/page-templates/25-cookie/17-cookie-domain-bad.html b/tests/page-templates/25-cookie/17-cookie-domain-bad.html new file mode 100644 index 000000000..653a8a726 --- /dev/null +++ b/tests/page-templates/25-cookie/17-cookie-domain-bad.html @@ -0,0 +1,13 @@ +<%= header %> +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/25-cookie/17-cookie-domain-bad.js b/tests/page-templates/25-cookie/17-cookie-domain-bad.js new file mode 100644 index 000000000..009294f97 --- /dev/null +++ b/tests/page-templates/25-cookie/17-cookie-domain-bad.js @@ -0,0 +1,19 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/25-cookie/16-cookie-subdomain", function() { + var tf = BOOMR.plugins.TestFramework; + + it("Should have sent a beacon", function() { + // ensure we fired a beacon ('beacon') + assert.isTrue(tf.fired_onbeacon); + }); + + it("Should have only set a single cookie", function() { + assert.equal(document.cookie.split("; ").length, 1); + }); + + it("Should have set the cookie on the current domain", function() { + assert.isTrue(BOOMR.utils.getCookie("RT").indexOf("dm=" + location.hostname) !== -1); + }); +}); diff --git a/tests/page-templates/26-iframedelay/00-iframedelay.html b/tests/page-templates/26-iframedelay/00-iframedelay.html new file mode 100644 index 000000000..9ee16ec51 --- /dev/null +++ b/tests/page-templates/26-iframedelay/00-iframedelay.html @@ -0,0 +1,20 @@ +<%= header %> + + + +<%= boomerangSnippet %> + + + +<%= footer %> diff --git a/tests/page-templates/26-iframedelay/00-iframedelay.js b/tests/page-templates/26-iframedelay/00-iframedelay.js new file mode 100644 index 000000000..159673066 --- /dev/null +++ b/tests/page-templates/26-iframedelay/00-iframedelay.js @@ -0,0 +1,88 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert,BOOMR */ + +describe("e2e/26-iframedelay/00-iframedelay", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + function getIFrameBeacon(id) { + return document.getElementById(id).contentWindow.BOOMR.plugins.TestFramework.lastBeacon(); + } + + it("The base page should have only sent one page load beacon", function(done) { + t.ensureBeaconCount(done, 1); + }); + + it("IFrame 1 Should have only sent one page load beacon", function(done) { + var w = document.getElementById("frame1").contentWindow; + + w.BOOMR_test.ensureBeaconCount(done, 1); + }); + + it("IFrame 2 Should have only sent one page load beacon", function(done) { + var w = document.getElementById("frame2").contentWindow; + + w.BOOMR_test.ensureBeaconCount(done, 1); + }); + + describe("IFrame 1 Beacon 1 (onload)", function(done) { + var id = "frame1"; + + it("Should have a t_done greater than 1500ms", function() { + var b = getIFrameBeacon(id); + + assert.operator(b.t_done, ">=", 1500); + }); + }); + + describe("IFrame 2 Beacon 1 (onload)", function(done) { + var id = "frame2"; + + it("Should have a t_done greater than 1500ms", function() { + var b = getIFrameBeacon(id); + + assert.operator(b.t_done, ">=", 1500); + }); + }); + + describe("Base page Beacon 1", function() { + it("Should have page_ready (pr) flag", function() { + var b = tf.lastBeacon(); + + assert.equal(b.pr, "1"); + }); + + it("Should have ifdl.done param", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["ifdl.done"]); + }); + + it("Should have ifdl.ct param", function() { + var b = tf.lastBeacon(); + + assert.equal(b["ifdl.ct"], "2"); + }); + + it("Should have ifdl.r param", function() { + var b = tf.lastBeacon(); + + assert.equal(b["ifdl.r"], "0"); + }); + + it("Should have ifdl.mon param", function() { + var b = tf.lastBeacon(); + + assert.equal(b["ifdl.mon"], "2"); + }); + + it("Should have rt.end of the slowest iframe", function() { + var b = tf.lastBeacon(), + bf1 = getIFrameBeacon("frame1"), + bf2 = getIFrameBeacon("frame2"); + var loadEnd = Math.max(bf1["rt.end"], bf2["rt.end"]); + + assert.equal(b["rt.end"], loadEnd); + }); + }); +}); diff --git a/tests/page-templates/26-iframedelay/01-fast-iframes.html b/tests/page-templates/26-iframedelay/01-fast-iframes.html new file mode 100644 index 000000000..e29dbd0ff --- /dev/null +++ b/tests/page-templates/26-iframedelay/01-fast-iframes.html @@ -0,0 +1,22 @@ +<%= header %> + + + +<%= boomerangSnippet %> + + + + +<%= footer %> diff --git a/tests/page-templates/26-iframedelay/01-fast-iframes.js b/tests/page-templates/26-iframedelay/01-fast-iframes.js new file mode 100644 index 000000000..bad08a5bc --- /dev/null +++ b/tests/page-templates/26-iframedelay/01-fast-iframes.js @@ -0,0 +1,93 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert,BOOMR */ + +describe("e2e/26-iframedelay/01-fast-iframes", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + function getIFrameBeacon(id) { + return document.getElementById(id).contentWindow.BOOMR.plugins.TestFramework.lastBeacon(); + } + + it("The base page should have only sent one page load beacon", function(done) { + t.ensureBeaconCount(done, 1); + }); + + it("IFrame 1 Should have only sent one page load beacon", function(done) { + var w = document.getElementById("frame1").contentWindow; + + w.BOOMR_test.ensureBeaconCount(done, 1); + }); + + it("IFrame 2 Should have only sent one page load beacon", function(done) { + var w = document.getElementById("frame2").contentWindow; + + w.BOOMR_test.ensureBeaconCount(done, 1); + }); + + describe("IFrame 1 Beacon 1 (onload)", function(done) { + var id = "frame1"; + + it("Should have a t_done less than 1s", function() { + var b = getIFrameBeacon(id); + + assert.operator(b.t_done, "<", 1000); + }); + }); + + describe("IFrame 2 Beacon 1 (onload)", function(done) { + var id = "frame2"; + + it("Should have a t_done less than 1s", function() { + var b = getIFrameBeacon(id); + + assert.operator(b.t_done, "<", 1000); + }); + }); + + describe("Base page Beacon 1", function() { + it("Should not have page_ready (pr) flag", function() { + var b = tf.lastBeacon(); + + assert.isUndefined(b.pr); + }); + + it("Should have ifdl.done param and be greater than the rt.end of the slowest iframe", function() { + var b = tf.lastBeacon(), + bf1 = getIFrameBeacon("frame1"), + bf2 = getIFrameBeacon("frame2"); + + assert.isDefined(b["ifdl.done"]); + var loadEnd = Math.max(bf1["rt.end"], bf2["rt.end"]); + + assert.operator(b["ifdl.done"], ">", loadEnd); + }); + + it("Should have ifdl.ct param", function() { + var b = tf.lastBeacon(); + + assert.equal(b["ifdl.ct"], "2"); + }); + + it("Should have ifdl.r param", function() { + var b = tf.lastBeacon(); + + assert.equal(b["ifdl.r"], "0"); + }); + + it("Should have ifdl.mon param", function() { + var b = tf.lastBeacon(); + + assert.equal(b["ifdl.mon"], "2"); + }); + + it("Should have rt.end greater than the slowest iframe", function() { + var b = tf.lastBeacon(), + bf1 = getIFrameBeacon("frame1"), + bf2 = getIFrameBeacon("frame2"); + var loadEnd = Math.max(bf1["rt.end"], bf2["rt.end"]); + + assert.operator(b["rt.end"], ">=", loadEnd); + }); + }); +}); diff --git a/tests/page-templates/26-iframedelay/02-delayed-boomr.html b/tests/page-templates/26-iframedelay/02-delayed-boomr.html new file mode 100644 index 000000000..ebad0beac --- /dev/null +++ b/tests/page-templates/26-iframedelay/02-delayed-boomr.html @@ -0,0 +1,25 @@ +<%= header %> + + + +<%= boomerangSnippet %> + + + + +<%= footer %> diff --git a/tests/page-templates/26-iframedelay/02-delayed-boomr.js b/tests/page-templates/26-iframedelay/02-delayed-boomr.js new file mode 100644 index 000000000..640808f3e --- /dev/null +++ b/tests/page-templates/26-iframedelay/02-delayed-boomr.js @@ -0,0 +1,88 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert,BOOMR */ + +describe("e2e/26-iframedelay/01-delayed-boomr", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + function getIFrameBeacon(id) { + return document.getElementById(id).contentWindow.BOOMR.plugins.TestFramework.lastBeacon(); + } + + it("The base page should have only sent one page load beacon", function(done) { + t.ensureBeaconCount(done, 1); + }); + + it("IFrame 1 Should have only sent one page load beacon", function(done) { + var w = document.getElementById("frame1").contentWindow; + + w.BOOMR_test.ensureBeaconCount(done, 1); + }); + + it("IFrame 2 Should have only sent one page load beacon", function(done) { + var w = document.getElementById("frame2").contentWindow; + + w.BOOMR_test.ensureBeaconCount(done, 1); + }); + + describe("IFrame 1 Beacon 1 (onload)", function(done) { + var id = "frame1"; + + it("Should have a t_done less than ~1s", function() { + var b = getIFrameBeacon(id); + + assert.operator(b.t_done, "<", 1050); + }); + }); + + describe("IFrame 2 Beacon 1 (onload)", function(done) { + var id = "frame2"; + + it("Should have a t_done less than ~1s", function() { + var b = getIFrameBeacon(id); + + assert.operator(b.t_done, "<", 1050); + }); + }); + + describe("Base page Beacon 1", function() { + it("Should have page_ready (pr) flag", function() { + var b = tf.lastBeacon(); + + assert.equal(b.pr, "1"); + }); + + it("Should have ifdl.done param", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["ifdl.done"]); + }); + + it("Should have ifdl.ct param", function() { + var b = tf.lastBeacon(); + + assert.equal(b["ifdl.ct"], "2"); + }); + + it("Should have ifdl.r param", function() { + var b = tf.lastBeacon(); + + assert.equal(b["ifdl.r"], "0"); + }); + + it("Should have ifdl.mon param", function() { + var b = tf.lastBeacon(); + + assert.equal(b["ifdl.mon"], "2"); + }); + + it("Should have rt.end of the slowest iframe", function() { + var b = tf.lastBeacon(), + bf1 = getIFrameBeacon("frame1"), + bf2 = getIFrameBeacon("frame2"); + var loadEnd = Math.max(bf1["rt.end"], bf2["rt.end"]); + + assert.equal(b["rt.end"], loadEnd); + }); + }); +}); diff --git a/tests/page-templates/26-iframedelay/03-delayed-iframes.html b/tests/page-templates/26-iframedelay/03-delayed-iframes.html new file mode 100644 index 000000000..8dd8fa602 --- /dev/null +++ b/tests/page-templates/26-iframedelay/03-delayed-iframes.html @@ -0,0 +1,21 @@ +<%= header %> + + + +<%= boomerangSnippet %> + + + +<%= footer %> diff --git a/tests/page-templates/26-iframedelay/03-delayed-iframes.js b/tests/page-templates/26-iframedelay/03-delayed-iframes.js new file mode 100644 index 000000000..832e6ce80 --- /dev/null +++ b/tests/page-templates/26-iframedelay/03-delayed-iframes.js @@ -0,0 +1,93 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert,BOOMR */ + +describe("e2e/26-iframedelay/03-delayed-iframes", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + function getIFrameBeacon(id) { + return document.getElementById(id).contentWindow.BOOMR.plugins.TestFramework.lastBeacon(); + } + + it("The base page should have only sent one page load beacon", function(done) { + t.ensureBeaconCount(done, 1); + }); + + it("IFrame 1 Should have only sent one page load beacon", function(done) { + var w = document.getElementById("frame1").contentWindow; + + w.BOOMR_test.ensureBeaconCount(done, 1); + }); + + it("IFrame 2 Should have only sent one page load beacon", function(done) { + var w = document.getElementById("frame2").contentWindow; + + w.BOOMR_test.ensureBeaconCount(done, 1); + }); + + describe("IFrame 1 Beacon 1 (onload)", function(done) { + var id = "frame1"; + + it("Should have a t_done less than 1s", function() { + var b = getIFrameBeacon(id); + + assert.operator(b.t_done, "<", 1000); + }); + }); + + describe("IFrame 2 Beacon 1 (onload)", function(done) { + var id = "frame2"; + + it("Should have a t_done less than 1s", function() { + var b = getIFrameBeacon(id); + + assert.operator(b.t_done, "<", 1000); + }); + }); + + describe("Base page Beacon 1", function() { + it("Should have page_ready (pr) flag", function() { + var b = tf.lastBeacon(); + + assert.equal(b.pr, "1"); + }); + + it("Should have ifdl.done param and be greater than the rt.end of the slowest iframe", function() { + var b = tf.lastBeacon(), + bf1 = getIFrameBeacon("frame1"), + bf2 = getIFrameBeacon("frame2"); + + assert.isDefined(b["ifdl.done"]); + var loadEnd = Math.max(bf1["rt.end"], bf2["rt.end"]); + + assert.operator(b["ifdl.done"], ">", loadEnd); + }); + + it("Should have ifdl.ct param", function() { + var b = tf.lastBeacon(); + + assert.equal(b["ifdl.ct"], "2"); + }); + + it("Should have ifdl.r param", function() { + var b = tf.lastBeacon(); + + assert.equal(b["ifdl.r"], "0"); + }); + + it("Should have ifdl.mon param", function() { + var b = tf.lastBeacon(); + + assert.equal(b["ifdl.mon"], "2"); + }); + + it("Should have rt.end of the slowest iframe", function() { + var b = tf.lastBeacon(), + bf1 = getIFrameBeacon("frame1"), + bf2 = getIFrameBeacon("frame2"); + var loadEnd = Math.max(bf1["rt.end"], bf2["rt.end"]); + + assert.equal(b["rt.end"], loadEnd); + }); + }); +}); diff --git a/tests/page-templates/26-iframedelay/04-iframedelay-co.html b/tests/page-templates/26-iframedelay/04-iframedelay-co.html new file mode 100644 index 000000000..73559e140 --- /dev/null +++ b/tests/page-templates/26-iframedelay/04-iframedelay-co.html @@ -0,0 +1,20 @@ +<%= header %> + + + +<%= boomerangSnippet %> + + + +<%= footer %> diff --git a/tests/page-templates/26-iframedelay/04-iframedelay-co.js b/tests/page-templates/26-iframedelay/04-iframedelay-co.js new file mode 100644 index 000000000..d80830937 --- /dev/null +++ b/tests/page-templates/26-iframedelay/04-iframedelay-co.js @@ -0,0 +1,165 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert,BOOMR */ + +describe("e2e/26-iframedelay/04-iframedelay-co", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + function getIFrameBeacon(id, callback) { + function handler(event) { + var data = JSON.parse(event.data); + + if (data && data.msg === "beacon") { + if (window.addEventListener) { + window.removeEventListener("message", handler); + } + else { + window.dettachEvent("onmessage", handler); + } + + callback(data.data); + } + } + + if (window.addEventListener) { + window.addEventListener("message", handler); + } + else { + window.attachEvent("onmessage", handler); + } + + // ask the iframe for lastBeacon data + document.getElementById(id).contentWindow.postMessage(JSON.stringify({msg: "lastBeacon"}), "*"); + } + + function ensureBeaconCountIFrame(id, count, callback) { + function handler(event) { + var data = JSON.parse(event.data); + + if (data && data.msg === "count") { + if (window.addEventListener) { + window.removeEventListener("message", handler); + } + else { + window.dettachEvent("onmessage", handler); + } + + if (!data.data) { + assert.fail("ensureBeaconCount failed"); + } + + callback(); + } + } + + if (window.addEventListener) { + window.addEventListener("message", handler); + } + else { + window.attachEvent("onmessage", handler); + } + + // ask the iframe for lastBeacon data + document.getElementById(id).contentWindow.postMessage(JSON.stringify({msg: "ensureBeaconCount", count: count}), "*"); + } + + it("The base page should have only sent one page load beacon", function(done) { + t.ensureBeaconCount(done, 1); + }); + + it("IFrame 1 Should have only sent one page load beacon", function(done) { + ensureBeaconCountIFrame("frame1", 1, done); + }); + + it("IFrame 2 Should have only sent one page load beacon", function(done) { + ensureBeaconCountIFrame("frame2", 1, done); + }); + + describe("IFrame 1 Beacon 1 (onload)", function() { + var id = "frame1"; + + it("Should have a t_done greater than 1500ms", function(done) { + this.timeout(10000); + getIFrameBeacon(id, function(b) { + assert.operator(b.t_done, ">=", 1500); + done(); + }); + }); + + it("Should not have a loaded boomerang in an child iframe", function(done) { + this.timeout(10000); + getIFrameBeacon(id, function(b) { + assert.isUndefined(b["if"]); + done(); + }); + }); + }); + + describe("IFrame 2 Beacon 1 (onload)", function() { + var id = "frame2"; + + it("Should have a t_done greater than 1500ms", function(done) { + this.timeout(10000); + getIFrameBeacon(id, function(b) { + assert.operator(b.t_done, ">=", 1500); + done(); + }); + }); + + it("Should not have a loaded boomerang in an child iframe", function(done) { + this.timeout(10000); + getIFrameBeacon(id, function(b) { + assert.isUndefined(b["if"]); + done(); + }); + }); + }); + + describe("Base page Beacon 1", function() { + it("Should have page_ready (pr) flag", function() { + var b = tf.lastBeacon(); + + assert.equal(b.pr, "1"); + }); + + it("Should have ifdl.done param", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["ifdl.done"]); + }); + + it("Should have ifdl.ct param", function() { + var b = tf.lastBeacon(); + + assert.equal(b["ifdl.ct"], "2"); + }); + + it("Should have ifdl.r param", function() { + var b = tf.lastBeacon(); + + assert.equal(b["ifdl.r"], "0"); + }); + + it("Should have ifdl.mon param", function() { + var b = tf.lastBeacon(); + + assert.equal(b["ifdl.mon"], "2"); + }); + + it("Should have rt.end of the slowest iframe", function(done) { + var b = tf.lastBeacon(), + bf1, bf2; + + getIFrameBeacon("frame1", function(b1) { + bf1 = b1; + getIFrameBeacon("frame2", function(b2) { + bf2 = b2; + var loadEnd = Math.max(bf1["rt.end"], bf2["rt.end"]); + + assert.equal(b["rt.end"], loadEnd); + done(); + }); + }); + }); + }); +}); diff --git a/tests/page-templates/26-iframedelay/05-iframedelay-snippet.html b/tests/page-templates/26-iframedelay/05-iframedelay-snippet.html new file mode 100644 index 000000000..6c8d5c650 --- /dev/null +++ b/tests/page-templates/26-iframedelay/05-iframedelay-snippet.html @@ -0,0 +1,20 @@ +<%= header %> + + + +<%= boomerangSnippet %> + + + +<%= footer %> diff --git a/tests/page-templates/26-iframedelay/05-iframedelay-snippet.js b/tests/page-templates/26-iframedelay/05-iframedelay-snippet.js new file mode 100644 index 000000000..efa52891d --- /dev/null +++ b/tests/page-templates/26-iframedelay/05-iframedelay-snippet.js @@ -0,0 +1,88 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert,BOOMR */ + +describe("e2e/26-iframedelay/05-iframedelay-snippet", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + function getIFrameBeacon(id) { + return document.getElementById(id).contentWindow.BOOMR.plugins.TestFramework.lastBeacon(); + } + + it("The base page should have only sent one page load beacon", function(done) { + t.ensureBeaconCount(done, 1); + }); + + it("IFrame 1 Should have only sent one page load beacon", function(done) { + var w = document.getElementById("frame1").contentWindow; + + w.BOOMR_test.ensureBeaconCount(done, 1); + }); + + it("IFrame 2 Should have only sent one page load beacon", function(done) { + var w = document.getElementById("frame2").contentWindow; + + w.BOOMR_test.ensureBeaconCount(done, 1); + }); + + describe("IFrame 1 Beacon 1 (onload)", function(done) { + var id = "frame1"; + + it("Should have a t_done greater than 1500ms", function() { + var b = getIFrameBeacon(id); + + assert.operator(b.t_done, ">=", 1500); + }); + }); + + describe("IFrame 2 Beacon 1 (onload)", function(done) { + var id = "frame2"; + + it("Should have a t_done greater than 1500ms", function() { + var b = getIFrameBeacon(id); + + assert.operator(b.t_done, ">=", 1500); + }); + }); + + describe("Base page Beacon 1", function() { + it("Should have page_ready (pr) flag", function() { + var b = tf.lastBeacon(); + + assert.equal(b.pr, "1"); + }); + + it("Should have ifdl.done param", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["ifdl.done"]); + }); + + it("Should have ifdl.ct param", function() { + var b = tf.lastBeacon(); + + assert.equal(b["ifdl.ct"], "2"); + }); + + it("Should have ifdl.r param", function() { + var b = tf.lastBeacon(); + + assert.equal(b["ifdl.r"], "0"); + }); + + it("Should have ifdl.mon param", function() { + var b = tf.lastBeacon(); + + assert.equal(b["ifdl.mon"], "2"); + }); + + it("Should have rt.end of the slowest iframe", function() { + var b = tf.lastBeacon(), + bf1 = getIFrameBeacon("frame1"), + bf2 = getIFrameBeacon("frame2"); + var loadEnd = Math.max(bf1["rt.end"], bf2["rt.end"]); + + assert.equal(b["rt.end"], loadEnd); + }); + }); +}); diff --git a/tests/page-templates/26-iframedelay/06-iframedelay-snippet-co.html b/tests/page-templates/26-iframedelay/06-iframedelay-snippet-co.html new file mode 100644 index 000000000..08cc99402 --- /dev/null +++ b/tests/page-templates/26-iframedelay/06-iframedelay-snippet-co.html @@ -0,0 +1,20 @@ +<%= header %> + + + +<%= boomerangSnippet %> + + + +<%= footer %> diff --git a/tests/page-templates/26-iframedelay/06-iframedelay-snippet-co.js b/tests/page-templates/26-iframedelay/06-iframedelay-snippet-co.js new file mode 100644 index 000000000..70913d4ef --- /dev/null +++ b/tests/page-templates/26-iframedelay/06-iframedelay-snippet-co.js @@ -0,0 +1,165 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert,BOOMR */ + +describe("e2e/26-iframedelay/06-iframedelay-snippet-co", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + function getIFrameBeacon(id, callback) { + function handler(event) { + var data = JSON.parse(event.data); + + if (data && data.msg === "beacon") { + if (window.addEventListener) { + window.removeEventListener("message", handler); + } + else { + window.dettachEvent("onmessage", handler); + } + + callback(data.data); + } + } + + if (window.addEventListener) { + window.addEventListener("message", handler); + } + else { + window.attachEvent("onmessage", handler); + } + + // ask the iframe for lastBeacon data + document.getElementById(id).contentWindow.postMessage(JSON.stringify({msg: "lastBeacon"}), "*"); + } + + function ensureBeaconCountIFrame(id, count, callback) { + function handler(event) { + var data = JSON.parse(event.data); + + if (data && data.msg === "count") { + if (window.addEventListener) { + window.removeEventListener("message", handler); + } + else { + window.dettachEvent("onmessage", handler); + } + + if (!data.data) { + assert.fail("ensureBeaconCount failed"); + } + + callback(); + } + } + + if (window.addEventListener) { + window.addEventListener("message", handler); + } + else { + window.attachEvent("onmessage", handler); + } + + // ask the iframe for lastBeacon data + document.getElementById(id).contentWindow.postMessage(JSON.stringify({msg: "ensureBeaconCount", count: count}), "*"); + } + + it("The base page should have only sent one page load beacon", function(done) { + t.ensureBeaconCount(done, 1); + }); + + it("IFrame 1 Should have only sent one page load beacon", function(done) { + ensureBeaconCountIFrame("frame1", 1, done); + }); + + it("IFrame 2 Should have only sent one page load beacon", function(done) { + ensureBeaconCountIFrame("frame2", 1, done); + }); + + describe("IFrame 1 Beacon 1 (onload)", function() { + var id = "frame1"; + + it("Should have a t_done greater than 1500ms", function(done) { + this.timeout(10000); + getIFrameBeacon(id, function(b) { + assert.operator(b.t_done, ">=", 1500); + done(); + }); + }); + + it("Should have a loaded boomerang in an child iframe", function(done) { + this.timeout(10000); + getIFrameBeacon(id, function(b) { + assert.isDefined(b["if"]); + done(); + }); + }); + }); + + describe("IFrame 2 Beacon 1 (onload)", function() { + var id = "frame2"; + + it("Should have a t_done greater than 1500ms", function(done) { + this.timeout(10000); + getIFrameBeacon(id, function(b) { + assert.operator(b.t_done, ">=", 1500); + done(); + }); + }); + + it("Should have a loaded boomerang in an child iframe", function(done) { + this.timeout(10000); + getIFrameBeacon(id, function(b) { + assert.isDefined(b["if"]); + done(); + }); + }); + }); + + describe("Base page Beacon 1", function() { + it("Should have page_ready (pr) flag", function() { + var b = tf.lastBeacon(); + + assert.equal(b.pr, "1"); + }); + + it("Should have ifdl.done param", function() { + var b = tf.lastBeacon(); + + assert.isDefined(b["ifdl.done"]); + }); + + it("Should have ifdl.ct param", function() { + var b = tf.lastBeacon(); + + assert.equal(b["ifdl.ct"], "2"); + }); + + it("Should have ifdl.r param", function() { + var b = tf.lastBeacon(); + + assert.equal(b["ifdl.r"], "0"); + }); + + it("Should have ifdl.mon param", function() { + var b = tf.lastBeacon(); + + assert.equal(b["ifdl.mon"], "2"); + }); + + it("Should have rt.end of the slowest iframe", function(done) { + var b = tf.lastBeacon(), + bf1, bf2; + + getIFrameBeacon("frame1", function(b1) { + bf1 = b1; + getIFrameBeacon("frame2", function(b2) { + bf2 = b2; + var loadEnd = Math.max(bf1["rt.end"], bf2["rt.end"]); + + assert.equal(b["rt.end"], loadEnd); + done(); + }); + }); + }); + }); +}); diff --git a/tests/page-templates/26-iframedelay/support/delayed-boomr.html b/tests/page-templates/26-iframedelay/support/delayed-boomr.html new file mode 100644 index 000000000..6b7e2e0df --- /dev/null +++ b/tests/page-templates/26-iframedelay/support/delayed-boomr.html @@ -0,0 +1,34 @@ + + + + IFrame w/ Boomerang + + + + + + + + + + + +
    + + + + \ No newline at end of file diff --git a/tests/page-templates/26-iframedelay/support/delayed-pageready-snippet.html b/tests/page-templates/26-iframedelay/support/delayed-pageready-snippet.html new file mode 100644 index 000000000..33c1546a6 --- /dev/null +++ b/tests/page-templates/26-iframedelay/support/delayed-pageready-snippet.html @@ -0,0 +1,66 @@ + + + + IFrame w/ Boomerang + + + + + + + + + + <%= boomerangSnippet %> + + +
    + + + + diff --git a/tests/page-templates/26-iframedelay/support/delayed-pageready.html b/tests/page-templates/26-iframedelay/support/delayed-pageready.html new file mode 100644 index 000000000..9a312b435 --- /dev/null +++ b/tests/page-templates/26-iframedelay/support/delayed-pageready.html @@ -0,0 +1,60 @@ + + + + IFrame w/ Boomerang + + + + + + + + + + + +
    + + + + diff --git a/tests/page-templates/26-iframedelay/support/instant-pageready.html b/tests/page-templates/26-iframedelay/support/instant-pageready.html new file mode 100644 index 000000000..c303d9dcd --- /dev/null +++ b/tests/page-templates/26-iframedelay/support/instant-pageready.html @@ -0,0 +1,36 @@ + + + + IFrame w/ Boomerang + + + + + + + + + + + +
    + + + + diff --git a/tests/page-templates/26-iframedelay/tests.json5 b/tests/page-templates/26-iframedelay/tests.json5 new file mode 100644 index 000000000..a62a8b825 --- /dev/null +++ b/tests/page-templates/26-iframedelay/tests.json5 @@ -0,0 +1,5 @@ +{ + all: { + requires: ["iframe-delay"] + } +} diff --git a/tests/page-templates/27-loader-snippet/00-script-mode.html b/tests/page-templates/27-loader-snippet/00-script-mode.html new file mode 100644 index 000000000..371feb1c0 --- /dev/null +++ b/tests/page-templates/27-loader-snippet/00-script-mode.html @@ -0,0 +1,9 @@ +<%= header %> +<%= boomerangSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/27-loader-snippet/00-script-mode.js b/tests/page-templates/27-loader-snippet/00-script-mode.js new file mode 100644 index 000000000..b47d6f745 --- /dev/null +++ b/tests/page-templates/27-loader-snippet/00-script-mode.js @@ -0,0 +1,35 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/27-loader-snippet/00-script-mode", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a beacon", function() { + assert.isTrue(tf.fired_onbeacon); + }); + + it("Should have used the SCRIPT Snippet method (if IE 6 or IE 7 or IE 8)", function() { + if (!t.isIE()) { + return this.skip(); + } + + if (navigator.userAgentData || !navigator.userAgent.match(/MSIE [678]\./)) { + return this.skip(); + } + + assert.isTrue(t.snippetWasLoadedScript()); + }); + + it("Should have set sm=s (if IE 6 or IE 7 or IE 8)", function() { + if (!t.isIE()) { + return this.skip(); + } + + if (navigator.userAgentData || !navigator.userAgent.match(/MSIE [678]\./)) { + return this.skip(); + } + + assert.equal("s", tf.lastBeacon().sm); + }); +}); diff --git a/tests/page-templates/27-loader-snippet/01-script-mode-forced.html b/tests/page-templates/27-loader-snippet/01-script-mode-forced.html new file mode 100644 index 000000000..bfc55ffea --- /dev/null +++ b/tests/page-templates/27-loader-snippet/01-script-mode-forced.html @@ -0,0 +1,16 @@ +<%= header %> + +<%= boomerangSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/27-loader-snippet/01-script-mode-forced.js b/tests/page-templates/27-loader-snippet/01-script-mode-forced.js new file mode 100644 index 000000000..1e01d68aa --- /dev/null +++ b/tests/page-templates/27-loader-snippet/01-script-mode-forced.js @@ -0,0 +1,70 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["isIE", "isEdge", "addEventListener", "attachEvent", "forcedSnippetScript"]); + +describe("e2e/27-loader-snippet/01-script-mode-forced", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a beacon", function() { + assert.isTrue(tf.fired_onbeacon); + }); + + it("Should have used the SCRIPT Snippet method (if forcing the SCRIPT method is supported)", function() { + if (!window.forcedSnippetScript) { + return this.skip(); + } + + assert.isTrue(t.snippetWasLoadedScript()); + }); + + it("Should have set sm=s (if forcing the SCRIPT method is supported)", function() { + if (!window.forcedSnippetScript) { + return this.skip(); + } + + assert.equal("s", tf.lastBeacon().sm); + }); + + it("Should not have used the IFRAME Snippet method (if forcing the SCRIPT method is supported)", function() { + if (!window.forcedSnippetScript) { + return this.skip(); + } + + assert.isFalse(t.snippetWasLoadedIframe()); + }); + + it("Should not have used the Preload Snippet method", function() { + assert.isFalse(t.snippetWasLoadedPreload()); + }); + + it("Should have added a SCRIPT with id 'boomr-async' (if forcing the SCRIPT method is supported)", function() { + if (!window.forcedSnippetScript) { + return this.skip(); + } + + assert.isNotNull(t.findBoomerangLoaderScript()); + }); + + it("Should have added a SCRIPT to the same block (BODY) as the loader snippet (if not IE)", function() { + if (window.isIE && !window.isEdge) { + return this.skip(); + } + + assert.strictEqual(t.findBoomerangLoaderScript().parentNode.tagName, "BODY"); + }); + + it("Should have added a SCRIPT to the HEAD (if IE and if forcing the SCRIPT method is supported)", function() { + if (!window.forcedSnippetScript) { + return this.skip(); + } + + if (!window.isIE || window.isEdge) { + return this.skip(); + } + + assert.strictEqual(t.findBoomerangLoaderScript().parentNode.tagName, "HEAD"); + }); +}); diff --git a/tests/page-templates/27-loader-snippet/02-iframe-mode.html b/tests/page-templates/27-loader-snippet/02-iframe-mode.html new file mode 100644 index 000000000..959d8194c --- /dev/null +++ b/tests/page-templates/27-loader-snippet/02-iframe-mode.html @@ -0,0 +1,9 @@ +<%= header %> +<%= boomerangSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/27-loader-snippet/02-iframe-mode.js b/tests/page-templates/27-loader-snippet/02-iframe-mode.js new file mode 100644 index 000000000..ad5e36c0b --- /dev/null +++ b/tests/page-templates/27-loader-snippet/02-iframe-mode.js @@ -0,0 +1,35 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/27-loader-snippet/02-iframe-mode", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a beacon", function() { + assert.isTrue(tf.fired_onbeacon); + }); + + it("Should have used the IFRAME Snippet method (if not IE 6/7 and doesn't support Preload)", function() { + if (!t.supportsLoaderIframe()) { + return this.skip(); + } + + if (t.isPreloadSupported()) { + return this.skip(); + } + + assert.isTrue(t.snippetWasLoadedIframe()); + }); + + it("Should have set sm=i (if not IE 6/7 and doesn't support Preload)", function() { + if (!t.supportsLoaderIframe()) { + return this.skip(); + } + + if (t.isPreloadSupported()) { + return this.skip(); + } + + assert.equal("i", tf.lastBeacon().sm); + }); +}); diff --git a/tests/page-templates/27-loader-snippet/03-iframe-mode-forced.html b/tests/page-templates/27-loader-snippet/03-iframe-mode-forced.html new file mode 100644 index 000000000..a01a5b4d9 --- /dev/null +++ b/tests/page-templates/27-loader-snippet/03-iframe-mode-forced.html @@ -0,0 +1,14 @@ +<%= header %> + +<%= boomerangSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/27-loader-snippet/03-iframe-mode-forced.js b/tests/page-templates/27-loader-snippet/03-iframe-mode-forced.js new file mode 100644 index 000000000..734899986 --- /dev/null +++ b/tests/page-templates/27-loader-snippet/03-iframe-mode-forced.js @@ -0,0 +1,139 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/27-loader-snippet/03-iframe-mode-forced", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a beacon", function() { + assert.isTrue(tf.fired_onbeacon); + }); + + it("Should not have used the SCRIPT Snippet method (if not IE 6/7)", function() { + if (!t.supportsLoaderIframe()) { + return this.skip(); + } + + assert.isFalse(t.snippetWasLoadedScript()); + }); + + it("Should have set sm=i (if not IE 6/7)", function() { + if (!t.supportsLoaderIframe()) { + return this.skip(); + } + + assert.equal("i", tf.lastBeacon().sm); + }); + + it("Should have used the IFRAME Snippet method (if not IE 6/7)", function() { + if (!t.supportsLoaderIframe()) { + return this.skip(); + } + + assert.isTrue(t.snippetWasLoadedIframe()); + }); + + it("Should not have used the Preload Snippet method", function() { + assert.isFalse(t.snippetWasLoadedPreload()); + }); + + it("Should have added a IFRAME with src 'about:blank' (if not IE 6/7)", function() { + if (!t.supportsLoaderIframe()) { + return this.skip(); + } + + var iframe = t.findBoomerangLoaderFrame(); + + assert.equal("about:blank", iframe.src); + }); + + it("Should have added a IFRAME to the same block (BODY) as the loader snippet (if not IE)", function() { + if (!t.supportsLoaderIframe() || t.isIE()) { + return this.skip(); + } + + var iframe = t.findBoomerangLoaderFrame(); + + assert.equal(iframe.parentNode.tagName, "BODY"); + }); + + it("Should have added a IFRAME to HEAD (if IE 8-11)", function() { + if (!t.supportsLoaderIframe() || !t.isIE()) { + return this.skip(); + } + + var iframe = t.findBoomerangLoaderFrame(); + + assert.equal(iframe.parentNode.tagName, "HEAD"); + }); + + it("Should have added a IFRAME with title '' (if not IE 6/7)", function() { + if (!t.supportsLoaderIframe()) { + return this.skip(); + } + + var iframe = t.findBoomerangLoaderFrame(); + + assert.strictEqual(iframe.title, ""); + }); + + it("Should have added a IFRAME with role 'presentation' (if not IE 6/7)", function() { + if (!t.supportsLoaderIframe()) { + return this.skip(); + } + + var iframe = t.findBoomerangLoaderFrame(); + + assert.equal(iframe.role, "presentation"); + }); + + it("Should have added a IFRAME with loading 'eager' (if not IE 6/7)", function() { + if (!t.supportsLoaderIframe()) { + return this.skip(); + } + + var iframe = t.findBoomerangLoaderFrame(); + + assert.equal(iframe.loading, "eager"); + }); + + it("Should have added a IFRAME with width 0 (if not IE 6/7)", function() { + if (!t.supportsLoaderIframe()) { + return this.skip(); + } + + var iframe = t.findBoomerangLoaderFrame(); + + assert.equal(iframe.width, 0); + }); + + it("Should have added a IFRAME with height 0 (if not IE 6/7)", function() { + if (!t.supportsLoaderIframe()) { + return this.skip(); + } + + var iframe = t.findBoomerangLoaderFrame(); + + assert.equal(iframe.height, 0); + }); + + it("Should have added a IFRAME with border '0px' (if not IE 6/7)", function() { + if (!t.supportsLoaderIframe()) { + return this.skip(); + } + + var iframe = t.findBoomerangLoaderFrame(); + + assert.include(iframe.style.border, "0px"); + }); + + it("Should have added a IFRAME with display 'none' (if not IE 6/7)", function() { + if (!t.supportsLoaderIframe()) { + return this.skip(); + } + + var iframe = t.findBoomerangLoaderFrame(); + + assert.equal(iframe.style.display, "none"); + }); +}); diff --git a/tests/page-templates/27-loader-snippet/04-preloader-mode.html b/tests/page-templates/27-loader-snippet/04-preloader-mode.html new file mode 100644 index 000000000..2f8bb5b83 --- /dev/null +++ b/tests/page-templates/27-loader-snippet/04-preloader-mode.html @@ -0,0 +1,9 @@ +<%= header %> +<%= boomerangSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/27-loader-snippet/04-preloader-mode.js b/tests/page-templates/27-loader-snippet/04-preloader-mode.js new file mode 100644 index 000000000..ac8749b7a --- /dev/null +++ b/tests/page-templates/27-loader-snippet/04-preloader-mode.js @@ -0,0 +1,95 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/27-loader-snippet/04-preloader-mode", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a beacon", function() { + assert.isTrue(tf.fired_onbeacon); + }); + + it("Should have used the Preload Snippet method (if Preload is supported)", function() { + if (!t.isPreloadSupported()) { + return this.skip(); + } + + assert.isTrue(t.snippetWasLoadedPreload()); + }); + + it("Should have set sm=p (if Preload is supported)", function() { + if (!t.isPreloadSupported()) { + return this.skip(); + } + + assert.equal("p", tf.lastBeacon().sm); + }); + + it("Should not have used the Preload Snippet method (if Preload is not supported)", function() { + if (t.isPreloadSupported()) { + return this.skip(); + } + + assert.isFalse(t.snippetWasLoadedPreload()); + }); + + it("Should have added LINK rel 'preload' (if Preload is supported)", function() { + if (!t.isPreloadSupported()) { + return this.skip(); + } + + var link = t.findBoomerangLoaderLinkPreload(); + + assert.equal("preload", link.rel); + }); + + it("Should have added LINK as 'script' (if Preload is supported)", function() { + if (!t.isPreloadSupported()) { + return this.skip(); + } + + var link = t.findBoomerangLoaderLinkPreload(); + + assert.equal("script", link.as); + }); + + it("Should have added LINK to the same block (BODY) as the loader snippet (if Preload is supported)", function() { + if (!t.isPreloadSupported()) { + return this.skip(); + } + + var link = t.findBoomerangLoaderLinkPreload(); + + assert.equal(link.parentNode.tagName, "BODY"); + }); + + it("Should have added SCRIPT id 'boomr-scr-as' (if Preload is supported)", function() { + if (!t.isPreloadSupported()) { + return this.skip(); + } + + var script = t.findBoomerangLoaderScriptPreload(); + + assert.equal("boomr-scr-as", script.id); + }); + + it("Should have added SCRIPT async (if Preload is supported)", function() { + if (!t.isPreloadSupported()) { + return this.skip(); + } + + var script = t.findBoomerangLoaderScriptPreload(); + + assert.isTrue(script.async); + }); + + it("Should have added SCRIPT to the same block (BODY) as the loader snippet (if Preload is supported )", function() { + if (!t.isPreloadSupported()) { + return this.skip(); + } + + var script = t.findBoomerangLoaderScriptPreload(); + + assert.equal(script.parentNode.tagName, "BODY"); + }); +}); diff --git a/tests/page-templates/27-loader-snippet/05-csp.html b/tests/page-templates/27-loader-snippet/05-csp.html new file mode 100644 index 000000000..2ef8636ed --- /dev/null +++ b/tests/page-templates/27-loader-snippet/05-csp.html @@ -0,0 +1,21 @@ +<%= header %> + +<%= boomerangSnippet %> + + + +<%= footer %> diff --git a/tests/page-templates/27-loader-snippet/05-csp.html.headers b/tests/page-templates/27-loader-snippet/05-csp.html.headers new file mode 100644 index 000000000..baa589a6d --- /dev/null +++ b/tests/page-templates/27-loader-snippet/05-csp.html.headers @@ -0,0 +1,6 @@ +Content-Security-Policy: frame-src 'none' +Content-Security-Policy: child-src 'none' +Content-Security-Policy: script-src boomerang-test.local:* 'nonce-boomerang-test-1' 'nonce-boomerang-test-2' 'nonce-boomerang-test-3' 'nonce-boomerang-test-4' 'nonce-boomerang-test-5' +Content-Security-Policy: style-src boomerang-test.local:* +Content-Security-Policy: img-src boomerang-test.local:* +Content-Security-Policy: connect-src boomerang-test.local:* diff --git a/tests/page-templates/27-loader-snippet/05-csp.js b/tests/page-templates/27-loader-snippet/05-csp.js new file mode 100644 index 000000000..cdd5e8481 --- /dev/null +++ b/tests/page-templates/27-loader-snippet/05-csp.js @@ -0,0 +1,38 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["violations"]); + +describe("e2e/27-loader-snippet/05-csp", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a beacon", function() { + assert.isTrue(tf.fired_onbeacon); + }); + + it("Should have used the Preload Snippet method (if Preload is supported)", function() { + if (!t.isPreloadSupported()) { + return this.skip(); + } + + assert.isTrue(t.snippetWasLoadedPreload()); + }); + + it("Should not have used the Preload Snippet method (if Preload is not supported)", function() { + if (t.isPreloadSupported()) { + return this.skip(); + } + + assert.isFalse(t.snippetWasLoadedPreload()); + }); + + it("Should have detected the CSP violation for the one script without a nonce (if SecurityPolicyViolationEvent is supported)", function() { + if (typeof window.SecurityPolicyViolationEvent === "undefined") { + return this.skip(); + } + + assert.equal(1, window.violations.length); + }); +}); diff --git a/tests/page-templates/27-loader-snippet/06-preloader-mode-delayed.html b/tests/page-templates/27-loader-snippet/06-preloader-mode-delayed.html new file mode 100644 index 000000000..7a5d6cdf3 --- /dev/null +++ b/tests/page-templates/27-loader-snippet/06-preloader-mode-delayed.html @@ -0,0 +1,9 @@ +<%= header %> +<%= boomerangDelayedSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/27-loader-snippet/06-preloader-mode-delayed.js b/tests/page-templates/27-loader-snippet/06-preloader-mode-delayed.js new file mode 100644 index 000000000..adbe63f0f --- /dev/null +++ b/tests/page-templates/27-loader-snippet/06-preloader-mode-delayed.js @@ -0,0 +1,156 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["BOOMR_script_delay"]); + +describe("e2e/27-loader-snippet/06-preloader-mode-delayed", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a beacon", function() { + assert.isTrue(tf.fired_onbeacon); + }); + + it("Should have used the Preload Snippet method (if Preload is supported)", function() { + if (!t.isPreloadSupported()) { + return this.skip(); + } + + assert.isTrue(t.snippetWasLoadedPreload()); + }); + + it("Should have set sm=if (if Preload is supported)", function() { + if (!t.isPreloadSupported()) { + return this.skip(); + } + + assert.equal("if", tf.lastBeacon().sm); + }); + + it("Should not have used the Preload Snippet method (if Preload is not supported)", function() { + if (t.isPreloadSupported()) { + return this.skip(); + } + + assert.isFalse(t.snippetWasLoadedPreload()); + }); + + it("Should have added LINK rel 'preload' (if Preload is supported)", function() { + if (!t.isPreloadSupported()) { + return this.skip(); + } + + var link = t.findBoomerangLoaderLinkPreload(); + + assert.equal("preload", link.rel); + }); + + it("Should have added LINK as 'script' (if Preload is supported)", function() { + if (!t.isPreloadSupported()) { + return this.skip(); + } + + var link = t.findBoomerangLoaderLinkPreload(); + + assert.equal("script", link.as); + }); + + it("Should have added LINK to the same block (BODY) as the loader snippet (if Preload is supported)", function() { + if (!t.isPreloadSupported()) { + return this.skip(); + } + + var link = t.findBoomerangLoaderLinkPreload(); + + assert.equal(link.parentNode.tagName, "BODY"); + }); + + it("Should not have added SCRIPT id 'boomr-scr-as' (if Preload is supported)", function() { + if (!t.isPreloadSupported()) { + return this.skip(); + } + + assert.isNull(t.findBoomerangLoaderScriptPreload()); + }); + + it("Should have added a IFRAME with src 'about:blank' (if Preload is supported)", function() { + if (!t.isPreloadSupported()) { + return this.skip(); + } + + var iframe = t.findBoomerangLoaderFrame(); + + assert.equal("about:blank", iframe.src); + }); + + it("Should have added a IFRAME to the same block (BODY) as the loader snippet (if Preload is supported)", function() { + if (!t.isPreloadSupported()) { + return this.skip(); + } + + var iframe = t.findBoomerangLoaderFrame(); + + assert.equal(iframe.parentNode.tagName, "BODY"); + }); + + it("Should have added a IFRAME with title '' (if Preload is supported)", function() { + if (!t.isPreloadSupported()) { + return this.skip(); + } + + var iframe = t.findBoomerangLoaderFrame(); + + assert.strictEqual(iframe.title, ""); + }); + + it("Should have added a IFRAME with role 'presentation' (if Preload is supported)", function() { + if (!t.isPreloadSupported()) { + return this.skip(); + } + + var iframe = t.findBoomerangLoaderFrame(); + + assert.equal(iframe.role, "presentation"); + }); + + it("Should have added a IFRAME with width 0 (if Preload is supported)", function() { + if (!t.isPreloadSupported()) { + return this.skip(); + } + + var iframe = t.findBoomerangLoaderFrame(); + + assert.equal(iframe.width, 0); + }); + + it("Should have added a IFRAME with height 0 (if Preload is supported)", function() { + if (!t.isPreloadSupported()) { + return this.skip(); + } + + var iframe = t.findBoomerangLoaderFrame(); + + assert.equal(iframe.height, 0); + }); + + it("Should have added a IFRAME with border '0px' (if Preload is supported)", function() { + if (!t.isPreloadSupported()) { + return this.skip(); + } + + var iframe = t.findBoomerangLoaderFrame(); + + assert.include(iframe.style.border, "0px"); + }); + + it("Should have added a IFRAME with display 'none' (if Preload is supported)", function() { + if (!t.isPreloadSupported()) { + return this.skip(); + } + + var iframe = t.findBoomerangLoaderFrame(); + + assert.equal(iframe.style.display, "none"); + }); +}); diff --git a/tests/page-templates/28-rt/00-hash-string-nu-cookie-param.html b/tests/page-templates/28-rt/00-hash-string-nu-cookie-param.html new file mode 100644 index 000000000..8e2ae728d --- /dev/null +++ b/tests/page-templates/28-rt/00-hash-string-nu-cookie-param.html @@ -0,0 +1,48 @@ +<%= header %> + + +<%= boomerangScript %> + +Test Click + +<%= footer %> \ No newline at end of file diff --git a/tests/page-templates/28-rt/00-hash-string-nu-cookie-param.js b/tests/page-templates/28-rt/00-hash-string-nu-cookie-param.js new file mode 100644 index 000000000..f3409b508 --- /dev/null +++ b/tests/page-templates/28-rt/00-hash-string-nu-cookie-param.js @@ -0,0 +1,17 @@ +/* eslint-env mocha */ +/* global chai */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["clickEvent", "lastNav"]); + +describe("e2e/28-rt/00-hash-string-nu-cookie-param", function() { + var assert = chai.assert; + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should return hashed url", function() { + var subcookies = BOOMR.plugins.RT.getCookie(); + + assert.equal(subcookies.nu, "3z8grme7" /* FNV hashed: https://www.example.com/test-click.html */); + }); +}); diff --git a/tests/page-templates/28-rt/01-session-length-not-zero.html b/tests/page-templates/28-rt/01-session-length-not-zero.html new file mode 100644 index 000000000..80434cc3d --- /dev/null +++ b/tests/page-templates/28-rt/01-session-length-not-zero.html @@ -0,0 +1,33 @@ +<%= header %> +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/28-rt/01-session-length-not-zero.js b/tests/page-templates/28-rt/01-session-length-not-zero.js new file mode 100644 index 000000000..7edacd077 --- /dev/null +++ b/tests/page-templates/28-rt/01-session-length-not-zero.js @@ -0,0 +1,18 @@ +/* eslint-env mocha */ +/* global chai */ + +describe("e2e/28-rt/01-session-length-not-zero", function() { + var assert = chai.assert; + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent one beacon", function(done) { + t.ensureBeaconCount(done, 1); + }); + + it("Should not have sent a session length of zero", function() { + var b = tf.lastBeacon(); + + assert.equal(b["rt.sl"], 1); + }); +}); diff --git a/tests/page-templates/28-rt/02-keep-beacon-url-on-load.html b/tests/page-templates/28-rt/02-keep-beacon-url-on-load.html new file mode 100644 index 000000000..5aa1a584b --- /dev/null +++ b/tests/page-templates/28-rt/02-keep-beacon-url-on-load.html @@ -0,0 +1,33 @@ +<%= header %> + +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/28-rt/02-keep-beacon-url-on-load.js b/tests/page-templates/28-rt/02-keep-beacon-url-on-load.js new file mode 100644 index 000000000..b43f93737 --- /dev/null +++ b/tests/page-templates/28-rt/02-keep-beacon-url-on-load.js @@ -0,0 +1,19 @@ +/* eslint-env mocha */ +/* global chai */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["lastNav"]); + +describe("e2e/28-rt/02-keep-beacon-url-on-load", function() { + var assert = chai.assert; + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent one beacon", function(done) { + t.ensureBeaconCount(done, 1); + }); + + it("Should have kept the original beacon_url", function() { + assert.equal(BOOMR.getBeaconURL(), "/beacon"); + }); +}); diff --git a/tests/page-templates/28-rt/03-prerendered-activated-while-prerendering.html b/tests/page-templates/28-rt/03-prerendered-activated-while-prerendering.html new file mode 100644 index 000000000..0cd7eea81 --- /dev/null +++ b/tests/page-templates/28-rt/03-prerendered-activated-while-prerendering.html @@ -0,0 +1,43 @@ +<%= header %> +

    03-prerendered-activated-while-prerendering

    + + + +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/28-rt/03-prerendered-activated-while-prerendering.js b/tests/page-templates/28-rt/03-prerendered-activated-while-prerendering.js new file mode 100644 index 000000000..d75e02ea1 --- /dev/null +++ b/tests/page-templates/28-rt/03-prerendered-activated-while-prerendering.js @@ -0,0 +1,71 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/28-rt/03-prerendered-activated-while-prerendering", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a beacon", function() { + // ensure we fired a beacon ('beacon') + assert.isTrue(tf.fired_onbeacon); + }); + + it("Should not have sent a beacon while prerendering", function() { + assert.isUndefined(BOOMR_test.sentWhilePrerendering); + }); + + it("Should have set nt_act_st on the beacon", function() { + if (!t.isNavigationTiming2Supported() || !t.isPrerenderingSupported()) { + return this.skip(); + } + + var navSt = parseInt(tf.lastBeacon().nt_nav_st, 10); + var actSt = parseInt(tf.lastBeacon().nt_act_st, 10); + + assert.equal(actSt - navSt, BOOMR_test.fakeActivationStartOffset); + }); + + it("Should have reduced the perceived Load Time (t_done) by the activation time", function() { + if (!t.isNavigationTiming2Supported() || !t.isPrerenderingSupported()) { + return this.skip(); + } + + var navSt = parseInt(tf.lastBeacon().nt_nav_st, 10); + var actSt = parseInt(tf.lastBeacon().nt_act_st, 10); + var loadEnd = parseInt(tf.lastBeacon().nt_load_end, 10); + + var loadTime = loadEnd - navSt; + var actTime = actSt - navSt; + + // allow for rounding + assert.closeTo(tf.lastBeacon().t_done, loadTime - actTime, 1); + }); + + it("Should have reduced the perceived Front-End Time (t_page) by the activation time", function() { + if (!t.isNavigationTiming2Supported() || !t.isPrerenderingSupported()) { + return this.skip(); + } + + var navSt = parseInt(tf.lastBeacon().nt_nav_st, 10); + var actSt = parseInt(tf.lastBeacon().nt_act_st, 10); + var loadEnd = parseInt(tf.lastBeacon().nt_load_end, 10); + + var loadTime = loadEnd - navSt; + var actTime = actSt - navSt; + + // allow for rounding + assert.closeTo(tf.lastBeacon().t_page, loadTime - actTime, 1); + }); + + it("Should have set the Back-End Time (t_resp) to 0", function() { + if (!t.isNavigationTiming2Supported() || !t.isPrerenderingSupported()) { + return this.skip(); + } + + assert.equal(tf.lastBeacon().t_resp, 0); + }); + + it("Should have set rt.end", function() { + assert.operator(tf.lastBeacon()["rt.end"], ">", 0); + }); +}); diff --git a/tests/page-templates/28-rt/04-prerendered-activated-after-page-load.html b/tests/page-templates/28-rt/04-prerendered-activated-after-page-load.html new file mode 100644 index 000000000..0d4c26f1d --- /dev/null +++ b/tests/page-templates/28-rt/04-prerendered-activated-after-page-load.html @@ -0,0 +1,43 @@ +<%= header %> +

    04-prerendered-activated-after-page-load

    + + + +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/28-rt/04-prerendered-activated-after-page-load.js b/tests/page-templates/28-rt/04-prerendered-activated-after-page-load.js new file mode 100644 index 000000000..cd34ff5f2 --- /dev/null +++ b/tests/page-templates/28-rt/04-prerendered-activated-after-page-load.js @@ -0,0 +1,51 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/28-rt/04-prerendered-activated-after-page-load", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a beacon", function() { + // ensure we fired a beacon ('beacon') + assert.isTrue(tf.fired_onbeacon); + }); + + it("Should not have sent a beacon while prerendering", function() { + assert.isUndefined(BOOMR_test.sentWhilePrerendering); + }); + + it("Should have set nt_act_st on the beacon", function() { + if (!t.isNavigationTiming2Supported() || !t.isPrerenderingSupported()) { + return this.skip(); + } + + var navSt = parseInt(tf.lastBeacon().nt_nav_st, 10); + var actSt = parseInt(tf.lastBeacon().nt_act_st, 10); + + assert.equal(actSt - navSt, BOOMR_test.fakeActivationStartOffset); + }); + + it("Should have set the perceived Load Time (t_done) to 1ms", function() { + if (!t.isNavigationTiming2Supported() || !t.isPrerenderingSupported()) { + return this.skip(); + } + + assert.equal(tf.lastBeacon().t_done, 1); + }); + + it("Should have set the Front-End Time (t_page) to 1 ms", function() { + if (!t.isNavigationTiming2Supported() || !t.isPrerenderingSupported()) { + return this.skip(); + } + + assert.equal(tf.lastBeacon().t_page, 1); + }); + + it("Should have set the Back-End Time (t_resp) to 0", function() { + if (!t.isNavigationTiming2Supported() || !t.isPrerenderingSupported()) { + return this.skip(); + } + + assert.equal(tf.lastBeacon().t_resp, 0); + }); +}); diff --git a/tests/page-templates/28-rt/05-prerendered-activated-while-prerendering-before-back-end-time.html b/tests/page-templates/28-rt/05-prerendered-activated-while-prerendering-before-back-end-time.html new file mode 100644 index 000000000..72d6e8ca0 --- /dev/null +++ b/tests/page-templates/28-rt/05-prerendered-activated-while-prerendering-before-back-end-time.html @@ -0,0 +1,43 @@ +<%= header %> +

    05-prerendered-activated-while-prerendering-before-back-end-time

    + + + +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/28-rt/05-prerendered-activated-while-prerendering-before-back-end-time.js b/tests/page-templates/28-rt/05-prerendered-activated-while-prerendering-before-back-end-time.js new file mode 100644 index 000000000..0b240e325 --- /dev/null +++ b/tests/page-templates/28-rt/05-prerendered-activated-while-prerendering-before-back-end-time.js @@ -0,0 +1,71 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/28-rt/05-prerendered-activated-while-prerendering-before-back-end-time", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a beacon", function() { + // ensure we fired a beacon ('beacon') + assert.isTrue(tf.fired_onbeacon); + }); + + it("Should not have sent a beacon while prerendering", function() { + assert.isUndefined(BOOMR_test.sentWhilePrerendering); + }); + + it("Should have set nt_act_st on the beacon", function() { + if (!t.isNavigationTiming2Supported() || !t.isPrerenderingSupported()) { + return this.skip(); + } + + var navSt = parseInt(tf.lastBeacon().nt_nav_st, 10); + var actSt = parseInt(tf.lastBeacon().nt_act_st, 10); + + assert.equal(actSt - navSt, BOOMR_test.fakeActivationStartOffset); + }); + + it("Should have NOT reduced the perceived Load Time (t_done)", function() { + if (!t.isNavigationTiming2Supported() || !t.isPrerenderingSupported()) { + return this.skip(); + } + + var navSt = parseInt(tf.lastBeacon().nt_nav_st, 10); + var loadEnd = parseInt(tf.lastBeacon().nt_load_end, 10); + + var loadTime = loadEnd - navSt; + + // allow for rounding + assert.closeTo(tf.lastBeacon().t_done, loadTime, 1); + }); + + it("Should have NOT reduced the perceived Front-End Time (t_page)", function() { + if (!t.isNavigationTiming2Supported() || !t.isPrerenderingSupported()) { + return this.skip(); + } + + var respSt = parseInt(tf.lastBeacon().nt_res_st, 10); + var loadEnd = parseInt(tf.lastBeacon().nt_load_end, 10); + + var pageTime = loadEnd - respSt; + + // allow for rounding + assert.closeTo(tf.lastBeacon().t_page, pageTime, 1); + }); + + it("Should have reduced the Back-End Time (t_resp) by the activation time", function() { + if (!t.isNavigationTiming2Supported() || !t.isPrerenderingSupported()) { + return this.skip(); + } + + var navSt = parseInt(tf.lastBeacon().nt_nav_st, 10); + var actSt = parseInt(tf.lastBeacon().nt_act_st, 10); + var respSt = parseInt(tf.lastBeacon().nt_res_st, 10); + + var respTime = respSt - navSt; + var actTime = actSt - navSt; + + // allow for rounding + assert.closeTo(tf.lastBeacon().t_resp, respTime - actTime, 1); + }); +}); diff --git a/tests/page-templates/28-rt/tests.json5 b/tests/page-templates/28-rt/tests.json5 new file mode 100644 index 000000000..292d8b480 --- /dev/null +++ b/tests/page-templates/28-rt/tests.json5 @@ -0,0 +1,5 @@ +{ + all: { + requires: ["rt"] + } +} diff --git a/tests/page-templates/29-opt-out-opt-in/00-opt-out-remove-default-rt-cookie.html b/tests/page-templates/29-opt-out-opt-in/00-opt-out-remove-default-rt-cookie.html new file mode 100644 index 000000000..3cc83b84d --- /dev/null +++ b/tests/page-templates/29-opt-out-opt-in/00-opt-out-remove-default-rt-cookie.html @@ -0,0 +1,20 @@ +<%= header %> + +<%= consentInlinePlugin %> +<%= boomerangScript %> + + +<%= footer %> \ No newline at end of file diff --git a/tests/page-templates/29-opt-out-opt-in/00-opt-out-remove-default-rt-cookie.js b/tests/page-templates/29-opt-out-opt-in/00-opt-out-remove-default-rt-cookie.js new file mode 100644 index 000000000..6b9d73085 --- /dev/null +++ b/tests/page-templates/29-opt-out-opt-in/00-opt-out-remove-default-rt-cookie.js @@ -0,0 +1,30 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["BOOMR_CONSENT_CONFIG", "BOOMR_OPT_OUT", "BOOMR_OPT_IN"]); + +describe("e2e/29-opt-out-opt-in/00-opt-out-remove-default-rt-cookie", function() { + var t = BOOMR_test; + var consentPluginDebug = BOOMR.plugins.ConsentInlinedPlugin.debug; + + it("Should pass Consent Inline Plugin validation", function(done) { + t.validateConsentInlinePluginState(done); + }); + + it("[After Opt-out] Should not have set RT cookie", function() { + assert.isFalse(document.cookie.indexOf("RT=") !== -1); + }); + + it("[After Opt-out] Should have set BOOMR_CONSENT=\"opted-out\" cookie", function() { + assert.isTrue(document.cookie.indexOf("BOOMR_CONSENT=\"opted-out\"") !== -1); + }); + + it("[After Opt-out] Should not have deferred opt-out state", function() { + assert.isFalse(consentPluginDebug.getDeferredOptOutFlag()); + }); + + it("[After Opt-out] Should not have deferred opt-in state", function() { + assert.isFalse(consentPluginDebug.getDeferredOptInFlag()); + }); +}); diff --git a/tests/page-templates/29-opt-out-opt-in/01-opt-in-origin-injected-loader-wrapper.html b/tests/page-templates/29-opt-out-opt-in/01-opt-in-origin-injected-loader-wrapper.html new file mode 100644 index 000000000..59e5f6d57 --- /dev/null +++ b/tests/page-templates/29-opt-out-opt-in/01-opt-in-origin-injected-loader-wrapper.html @@ -0,0 +1,18 @@ +<%= header %> + +<%= consentInlinePlugin %> + + + +<%= footer %> \ No newline at end of file diff --git a/tests/page-templates/29-opt-out-opt-in/01-opt-in-origin-injected-loader-wrapper.js b/tests/page-templates/29-opt-out-opt-in/01-opt-in-origin-injected-loader-wrapper.js new file mode 100644 index 000000000..71e33cfc8 --- /dev/null +++ b/tests/page-templates/29-opt-out-opt-in/01-opt-in-origin-injected-loader-wrapper.js @@ -0,0 +1,32 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["BOOMR_CONSENT_CONFIG", "BOOMR_OPT_OUT", "BOOMR_OPT_IN", "BOOMERANG_LOADER_WRAPPER"]); + +describe("e2e/29-opt-out-opt-in/01-opt-in-origin-injected-loader-wrapper", function() { + var snippetStart = window.BOOMR.snippetStart; + + // We need to do this because BOOMR TF is not initialized yet + var assert = window.chai.assert; + + it("[Before Opt-in] Should not have loaded BOOMR", function() { + assert.isUndefined(snippetStart); + }); + + BOOMERANG_LOADER_WRAPPER(); + + it("[After Opt-in] Should have loaded BOOMR", function() { + assert.isDefined(window.BOOMR.snippetStart); + }); + + var consentPluginDebug = BOOMR.plugins.ConsentInlinedPlugin.debug; + + it("[After Opt-in] Should not have deferred opt-out state", function() { + assert.isFalse(consentPluginDebug.getDeferredOptOutFlag()); + }); + + it("[After Opt-in] Should not have deferred opt-in state", function() { + assert.isFalse(consentPluginDebug.getDeferredOptInFlag()); + }); +}); diff --git a/tests/page-templates/29-opt-out-opt-in/02-opt-out-custom-rt-cookie.html b/tests/page-templates/29-opt-out-opt-in/02-opt-out-custom-rt-cookie.html new file mode 100644 index 000000000..13eafbb8c --- /dev/null +++ b/tests/page-templates/29-opt-out-opt-in/02-opt-out-custom-rt-cookie.html @@ -0,0 +1,23 @@ +<%= header %> + +<%= consentInlinePlugin %> +<%= boomerangScript %> + + +<%= footer %> \ No newline at end of file diff --git a/tests/page-templates/29-opt-out-opt-in/02-opt-out-custom-rt-cookie.js b/tests/page-templates/29-opt-out-opt-in/02-opt-out-custom-rt-cookie.js new file mode 100644 index 000000000..9afd5afc6 --- /dev/null +++ b/tests/page-templates/29-opt-out-opt-in/02-opt-out-custom-rt-cookie.js @@ -0,0 +1,25 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["BOOMR_CONSENT_CONFIG", "BOOMR_OPT_OUT", "BOOMR_OPT_IN"]); + +describe("e2e/29-opt-out-opt-in/02-opt-out-custom-rt-cookie", function() { + var t = BOOMR_test; + + it("Should pass Consent Inline Plugin validation", function(done) { + t.validateConsentInlinePluginState(done); + }); + + it("[After Opt-out] Should not have CUSTOM RT cookie", function() { + assert.isFalse(document.cookie.indexOf("CUSTOM=") !== -1); + }); + + it("[After Opt-out] Should not have set RT cookie", function() { + assert.isFalse(document.cookie.indexOf("RT=") !== -1); + }); + + it("[After Opt-out] Should have set BOOMR_CONSENT=\"opted-out\" cookie", function() { + assert.isTrue(document.cookie.indexOf("BOOMR_CONSENT=\"opted-out\"") !== -1); + }); +}); diff --git a/tests/page-templates/29-opt-out-opt-in/03-opt-in-before-page-ready.html b/tests/page-templates/29-opt-out-opt-in/03-opt-in-before-page-ready.html new file mode 100644 index 000000000..b5d977928 --- /dev/null +++ b/tests/page-templates/29-opt-out-opt-in/03-opt-in-before-page-ready.html @@ -0,0 +1,22 @@ +<%= header %> + +<%= consentInlinePlugin %> +<%= boomerangScript %> + + + +<%= footer %> \ No newline at end of file diff --git a/tests/page-templates/29-opt-out-opt-in/03-opt-in-before-page-ready.js b/tests/page-templates/29-opt-out-opt-in/03-opt-in-before-page-ready.js new file mode 100644 index 000000000..cc566d41c --- /dev/null +++ b/tests/page-templates/29-opt-out-opt-in/03-opt-in-before-page-ready.js @@ -0,0 +1,27 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["BOOMR_CONSENT_CONFIG", "BOOMR_OPT_OUT", "BOOMR_OPT_IN"]); + +describe("e2e/29-opt-out-opt-in/03-opt-in-before-page-ready", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + var consentPluginDebug = BOOMR.plugins.ConsentInlinedPlugin.debug; + + it("Should pass Consent Inline Plugin validation", function(done) { + t.validateConsentInlinePluginState(done); + }); + + it("[After Opt-in] Should at least on beacon sent", function() { + assert.isTrue(tf.beaconCount() >= 1); + }); + + it("[After Opt-in] Should not have deferred opt-out state", function() { + assert.isFalse(consentPluginDebug.getDeferredOptOutFlag()); + }); + + it("[After Opt-in] Should not have deferred opt-in state", function() { + assert.isFalse(consentPluginDebug.getDeferredOptInFlag()); + }); +}); diff --git a/tests/page-templates/29-opt-out-opt-in/04-opt-in-after-page-ready.html b/tests/page-templates/29-opt-out-opt-in/04-opt-in-after-page-ready.html new file mode 100644 index 000000000..73e43a5de --- /dev/null +++ b/tests/page-templates/29-opt-out-opt-in/04-opt-in-after-page-ready.html @@ -0,0 +1,29 @@ +<%= header %> + +<%= consentInlinePlugin %> +<%= boomerangScript %> + + +<%= footer %> \ No newline at end of file diff --git a/tests/page-templates/29-opt-out-opt-in/04-opt-in-after-page-ready.js b/tests/page-templates/29-opt-out-opt-in/04-opt-in-after-page-ready.js new file mode 100644 index 000000000..aa63871d3 --- /dev/null +++ b/tests/page-templates/29-opt-out-opt-in/04-opt-in-after-page-ready.js @@ -0,0 +1,32 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["BOOMR_CONSENT_CONFIG", "BOOMR_OPT_OUT", "BOOMR_OPT_IN"]); + +describe("e2e/29-opt-out-opt-in/04-opt-in-after-page-ready", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + var consentPluginDebug = BOOMR.plugins.ConsentInlinedPlugin.debug; + + before("Give enough time to Boomerang to check if all plugins are ready", function(done) { + this.timeout(2500); + setTimeout(done, 2000); + }); + + it("Should pass Consent Inline Plugin validation", function(done) { + t.validateConsentInlinePluginState(done); + }); + + it("[After Opt-in] Should have at least one beacon sent", function() { + assert.isTrue(tf.beaconCount() >= 1); + }); + + it("[After Opt-in] Should not have deferred opt-out state", function() { + assert.isFalse(consentPluginDebug.getDeferredOptOutFlag()); + }); + + it("[After Opt-in] Should not have deferred opt-in state", function() { + assert.isFalse(consentPluginDebug.getDeferredOptInFlag()); + }); +}); diff --git a/tests/page-templates/29-opt-out-opt-in/05-opt-out-before-page-ready.html b/tests/page-templates/29-opt-out-opt-in/05-opt-out-before-page-ready.html new file mode 100644 index 000000000..fdda7b029 --- /dev/null +++ b/tests/page-templates/29-opt-out-opt-in/05-opt-out-before-page-ready.html @@ -0,0 +1,29 @@ +<%= header %> + +<%= consentInlinePlugin %> +<%= boomerangScript %> + + + +<%= footer %> \ No newline at end of file diff --git a/tests/page-templates/29-opt-out-opt-in/05-opt-out-before-page-ready.js b/tests/page-templates/29-opt-out-opt-in/05-opt-out-before-page-ready.js new file mode 100644 index 000000000..015442b55 --- /dev/null +++ b/tests/page-templates/29-opt-out-opt-in/05-opt-out-before-page-ready.js @@ -0,0 +1,35 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["BOOMR_CONSENT_CONFIG", "BOOMR_OPT_OUT", "BOOMR_OPT_IN"]); + +describe("e2e/29-opt-out-opt-in/06-opt-out-before-page-ready", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + var consentPluginDebug = BOOMR.plugins.ConsentInlinedPlugin.debug; + + it("Should pass Consent Inline Plugin validation", function(done) { + t.validateConsentInlinePluginState(done); + }); + + it("[Opt-out before Boomerang loaded] Should have 0 beacons sent", function() { + assert.isTrue(tf.beaconCount() === 0); + }); + + it("[Opt-out before Boomerang loaded] Should have set BOOMR_CONSENT=\"opted-out\" cookie", function() { + assert.isTrue(document.cookie.indexOf("BOOMR_CONSENT=\"opted-out\"") !== -1); + }); + + it("[Opt-out before Boomerang loaded] Should not have BOOMR_CONSENT=\"opted-in\" cookie", function() { + assert.isTrue(document.cookie.indexOf("BOOMR_CONSENT=\"opted-in\"") === -1); + }); + + it("[After Opt-in] Should not have deferred opt-out state", function() { + assert.isFalse(consentPluginDebug.getDeferredOptOutFlag()); + }); + + it("[After Opt-in] Should not have deferred opt-in state", function() { + assert.isFalse(consentPluginDebug.getDeferredOptInFlag()); + }); +}); diff --git a/tests/page-templates/29-opt-out-opt-in/06-opt-out-after-previous-opt-in.html b/tests/page-templates/29-opt-out-opt-in/06-opt-out-after-previous-opt-in.html new file mode 100644 index 000000000..8907adebc --- /dev/null +++ b/tests/page-templates/29-opt-out-opt-in/06-opt-out-after-previous-opt-in.html @@ -0,0 +1,25 @@ +<%= header %> + +<%= consentInlinePlugin %> +<%= boomerangScript %> + + +<%= footer %> \ No newline at end of file diff --git a/tests/page-templates/29-opt-out-opt-in/06-opt-out-after-previous-opt-in.js b/tests/page-templates/29-opt-out-opt-in/06-opt-out-after-previous-opt-in.js new file mode 100644 index 000000000..a1dacf250 --- /dev/null +++ b/tests/page-templates/29-opt-out-opt-in/06-opt-out-after-previous-opt-in.js @@ -0,0 +1,26 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["BOOMR_CONSENT_CONFIG", "BOOMR_OPT_OUT", "BOOMR_OPT_IN"]); + +describe("e2e/29-opt-out-opt-in/06-opt-out-after-previous-opt-in", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass Consent Inline Plugin validation", function(done) { + t.validateConsentInlinePluginState(done); + }); + + it("[After Opt-out] Should have set BOOMR_CONSENT=\"opted-out\" cookie", function() { + assert.isTrue(document.cookie.indexOf("BOOMR_CONSENT=\"opted-out\"") !== -1); + }); + + it("[Opt-out before Boomerang loaded] Should not have BOOMR_CONSENT=\"opted-in\" cookie", function() { + assert.isTrue(document.cookie.indexOf("BOOMR_CONSENT=\"opted-in\"") === -1); + }); + + it("[After Opt-out] Should have sent exactly 1 beacon because the rest were blocked because of Opt-out", function() { + assert.isTrue(tf.beaconCount() === 1); + }); +}); diff --git a/tests/page-templates/29-opt-out-opt-in/07-opt-in-after-previous-opt-out.html b/tests/page-templates/29-opt-out-opt-in/07-opt-in-after-previous-opt-out.html new file mode 100644 index 000000000..aa3d14af0 --- /dev/null +++ b/tests/page-templates/29-opt-out-opt-in/07-opt-in-after-previous-opt-out.html @@ -0,0 +1,27 @@ +<%= header %> + +<%= consentInlinePlugin %> +<%= boomerangScript %> + + +<%= footer %> \ No newline at end of file diff --git a/tests/page-templates/29-opt-out-opt-in/07-opt-in-after-previous-opt-out.js b/tests/page-templates/29-opt-out-opt-in/07-opt-in-after-previous-opt-out.js new file mode 100644 index 000000000..12b242ba0 --- /dev/null +++ b/tests/page-templates/29-opt-out-opt-in/07-opt-in-after-previous-opt-out.js @@ -0,0 +1,39 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["BOOMR_CONSENT_CONFIG", "BOOMR_OPT_OUT", "BOOMR_OPT_IN"]); + +describe("e2e/29-opt-out-opt-in/07-opt-in-after-previous-opt-out", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + var beaconCountBeforeOptIn = tf.beaconCount(); + + it("Should pass Consent Inline Plugin validation", function(done) { + t.validateConsentInlinePluginState(done); + }); + + it("[After Opt-out] Should have not sent beacons before visitor Opted In", function() { + assert.isTrue(beaconCountBeforeOptIn === 0); + }); + + BOOMR_OPT_IN(); + + before("Give enough time to Boomerang to check if all plugins are ready", function(done) { + this.timeout(2500); + setTimeout(done, 2000); + }); + + it("[After Opt-out] Should have have set BOOMR_CONSENT=\"opted-out\" cookie", function() { + assert.isTrue(document.cookie.indexOf("BOOMR_CONSENT=\"opted-out\"") === -1); + }); + + it("[Opt-out before Boomerang loaded] Should have set BOOMR_CONSENT=\"opted-in\" cookie", function() { + assert.isTrue(document.cookie.indexOf("BOOMR_CONSENT=\"opted-in\"") !== -1); + }); + + it("[After Opt-out] Should have sent exactly 1 beacon after visitor Opted In", function() { + assert.isTrue(tf.beaconCount() === 1); + }); +}); diff --git a/tests/page-templates/29-opt-out-opt-in/08-opt-in-delayed-iframe-snippet.html b/tests/page-templates/29-opt-out-opt-in/08-opt-in-delayed-iframe-snippet.html new file mode 100644 index 000000000..f668c3d3a --- /dev/null +++ b/tests/page-templates/29-opt-out-opt-in/08-opt-in-delayed-iframe-snippet.html @@ -0,0 +1,27 @@ +<%= header %> + +<%= consentInlinePlugin %> +<%= boomerangDelayedSnippet %> + + +<%= footer %> \ No newline at end of file diff --git a/tests/page-templates/29-opt-out-opt-in/08-opt-in-delayed-iframe-snippet.js b/tests/page-templates/29-opt-out-opt-in/08-opt-in-delayed-iframe-snippet.js new file mode 100644 index 000000000..96e114640 --- /dev/null +++ b/tests/page-templates/29-opt-out-opt-in/08-opt-in-delayed-iframe-snippet.js @@ -0,0 +1,39 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["BOOMR_CONSENT_CONFIG", "BOOMR_OPT_OUT", "BOOMR_OPT_IN", "BOOMR_script_delay"]); + +describe("e2e/29-opt-out-opt-in/08-opt-in-delayed-iframe-snippet", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + var beaconCountBforeOptIn = tf.beaconCount(); + + it("Should pass Consent Inline Plugin validation", function(done) { + t.validateConsentInlinePluginState(done); + }); + + it("[After Opt-out] Should have not sent beacons before visitor Opted In", function() { + assert.isTrue(beaconCountBforeOptIn === 0); + }); + + BOOMR_OPT_IN(); + + before("Give enough time to Boomerang to check if all plugins are ready", function(done) { + this.timeout(2500); + setTimeout(done, 2000); + }); + + it("[After Opt-out] Should have have set BOOMR_CONSENT=\"opted-out\" cookie", function() { + assert.isTrue(document.cookie.indexOf("BOOMR_CONSENT=\"opted-out\"") === -1); + }); + + it("[Opt-out before Boomerang loaded] Should have set BOOMR_CONSENT=\"opted-in\" cookie", function() { + assert.isTrue(document.cookie.indexOf("BOOMR_CONSENT=\"opted-in\"") !== -1); + }); + + it("[After Opt-out] Should have sent exactly 1 beacon after visitor Opted In", function() { + assert.isTrue(tf.beaconCount() === 1); + }); +}); diff --git a/tests/page-templates/29-opt-out-opt-in/09-opt-in-check-beacon-params.html b/tests/page-templates/29-opt-out-opt-in/09-opt-in-check-beacon-params.html new file mode 100644 index 000000000..cd8f651ac --- /dev/null +++ b/tests/page-templates/29-opt-out-opt-in/09-opt-in-check-beacon-params.html @@ -0,0 +1,23 @@ +<%= header %> + +<%= consentInlinePlugin %> +<%= boomerangScript %> + + +<%= footer %> \ No newline at end of file diff --git a/tests/page-templates/29-opt-out-opt-in/09-opt-in-check-beacon-params.js b/tests/page-templates/29-opt-out-opt-in/09-opt-in-check-beacon-params.js new file mode 100644 index 000000000..9a925cccb --- /dev/null +++ b/tests/page-templates/29-opt-out-opt-in/09-opt-in-check-beacon-params.js @@ -0,0 +1,28 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["BOOMR_CONSENT_CONFIG", "BOOMR_OPT_OUT", "BOOMR_OPT_IN"]); + +describe("e2e/29-opt-out-opt-in/09-opt-in-check-beacon-params", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass Consent Inline Plugin validation", function(done) { + t.validateConsentInlinePluginState(done); + }); + + it("Should have cip.in and cip.v on first beacon", function() { + var b = tf.beacons[0]; + + assert.equal(b["cip.in"], "1"); + assert.equal(b["cip.v"], "2"); + }); + + it("Should NOT have cip.in and cip.v on second beacon", function() { + var b = tf.beacons[1]; + + assert.isUndefined(b["cip.in"]); + assert.isUndefined(b["cip.v"]); + }); +}); diff --git a/tests/page-templates/29-opt-out-opt-in/10-no-opt-in-early-beacon-config-before-onload.html b/tests/page-templates/29-opt-out-opt-in/10-no-opt-in-early-beacon-config-before-onload.html new file mode 100644 index 000000000..9da4ebbab --- /dev/null +++ b/tests/page-templates/29-opt-out-opt-in/10-no-opt-in-early-beacon-config-before-onload.html @@ -0,0 +1,49 @@ +<%= header %> + +<%= consentInlinePlugin %> + + +<%= boomerangSnippet %> + + + + +<%= footer %> diff --git a/tests/page-templates/29-opt-out-opt-in/10-no-opt-in-early-beacon-config-before-onload.js b/tests/page-templates/29-opt-out-opt-in/10-no-opt-in-early-beacon-config-before-onload.js new file mode 100644 index 000000000..e8ac8e99a --- /dev/null +++ b/tests/page-templates/29-opt-out-opt-in/10-no-opt-in-early-beacon-config-before-onload.js @@ -0,0 +1,18 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["BOOMR_CONSENT_CONFIG", "BOOMR_OPT_OUT", "BOOMR_OPT_IN"]); + +describe("e2e/29-opt-out-opt-in/10-no-opt-in-config-before-onload", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass Consent Inline Plugin validation", function(done) { + t.validateConsentInlinePluginState(done); + }); + + it("Should not have sent beacon", function() { + assert.equal(tf.beaconCount(), 0); + }); +}); diff --git a/tests/page-templates/29-opt-out-opt-in/11-opt-out-osano-lib.html b/tests/page-templates/29-opt-out-opt-in/11-opt-out-osano-lib.html new file mode 100644 index 000000000..d3fcf01fb --- /dev/null +++ b/tests/page-templates/29-opt-out-opt-in/11-opt-out-osano-lib.html @@ -0,0 +1,64 @@ +<%= header %> + + + + + + + +<%= consentInlinePlugin %> +<%= boomerangScript %> + + +<%= footer %> \ No newline at end of file diff --git a/tests/page-templates/29-opt-out-opt-in/11-opt-out-osano-lib.js b/tests/page-templates/29-opt-out-opt-in/11-opt-out-osano-lib.js new file mode 100644 index 000000000..aaf85e50e --- /dev/null +++ b/tests/page-templates/29-opt-out-opt-in/11-opt-out-osano-lib.js @@ -0,0 +1,22 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["cookieconsent", "onCookieConsentChange", "BOOMR_CONSENT_CONFIG", "clickEvent", "BOOMR_OPT_OUT", "BOOMR_OPT_IN"]); + +describe("e2e/29-opt-out-opt-in/11-opt-out-osano-lib", function() { + var t = BOOMR_test; + + it("Should pass Consent Inline Plugin validation", function(done) { + t.validateConsentInlinePluginState(done); + }); + + it("[After Opt-out] Should not have set RT cookie", function() { + assert.isFalse(document.cookie.indexOf("RT=") !== -1); + }); + + it("[After Opt-out] Should have set BOOMR_CONSENT=\"opted-out\" cookie", function() { + assert.isTrue(document.cookie.indexOf("BOOMR_CONSENT=\"opted-out\"") !== -1); + }); +}); + diff --git a/tests/page-templates/29-opt-out-opt-in/12-opt-in-osano-lib.html b/tests/page-templates/29-opt-out-opt-in/12-opt-in-osano-lib.html new file mode 100644 index 000000000..ebe8cd727 --- /dev/null +++ b/tests/page-templates/29-opt-out-opt-in/12-opt-in-osano-lib.html @@ -0,0 +1,65 @@ +<%= header %> + + + + + + + +<%= consentInlinePlugin %> +<%= boomerangScript %> + + +<%= footer %> \ No newline at end of file diff --git a/tests/page-templates/29-opt-out-opt-in/12-opt-in-osano-lib.js b/tests/page-templates/29-opt-out-opt-in/12-opt-in-osano-lib.js new file mode 100644 index 000000000..23d2a187c --- /dev/null +++ b/tests/page-templates/29-opt-out-opt-in/12-opt-in-osano-lib.js @@ -0,0 +1,21 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["cookieconsent", "onCookieConsentChange", "BOOMR_CONSENT_CONFIG", "clickEvent", "BOOMR_OPT_OUT", "BOOMR_OPT_IN"]); + +describe("e2e/29-opt-out-opt-in/12-opt-in-osano-lib", function() { + var t = BOOMR_test; + + it("Should pass Consent Inline Plugin validation", function(done) { + t.validateConsentInlinePluginState(done); + }); + + it("[After Opt-out] Should not have set RT cookie", function() { + assert.isTrue(document.cookie.indexOf("RT=") !== -1); + }); + + it("[After Opt-out] Should have set BOOMR_CONSENT=\"opted-in\" cookie", function() { + assert.isTrue(document.cookie.indexOf("BOOMR_CONSENT=\"opted-in\"") !== -1); + }); +}); diff --git a/tests/page-templates/29-opt-out-opt-in/13-opt-out-no-cookies-after-init-reload.html b/tests/page-templates/29-opt-out-opt-in/13-opt-out-no-cookies-after-init-reload.html new file mode 100644 index 000000000..cfd4a0190 --- /dev/null +++ b/tests/page-templates/29-opt-out-opt-in/13-opt-out-no-cookies-after-init-reload.html @@ -0,0 +1,31 @@ +<%= header %> + +<%= consentInlinePlugin %> +<%= boomerangScript %> + + +<%= footer %> \ No newline at end of file diff --git a/tests/page-templates/29-opt-out-opt-in/13-opt-out-no-cookies-after-init-reload.js b/tests/page-templates/29-opt-out-opt-in/13-opt-out-no-cookies-after-init-reload.js new file mode 100644 index 000000000..8aefbec76 --- /dev/null +++ b/tests/page-templates/29-opt-out-opt-in/13-opt-out-no-cookies-after-init-reload.js @@ -0,0 +1,22 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["BOOMR_CONSENT_CONFIG", "BOOMR_OPT_OUT", "BOOMR_OPT_IN"]); + +describe("e2e/29-opt-out-opt-in/13-opt-out-no-cookies-after-init-reload", function() { + var t = BOOMR_test; + + it("Should pass Consent Inline Plugin validation", function(done) { + t.validateConsentInlinePluginState(done); + }); + + it("[After Opt-out] Should not have set RT cookie", function() { + assert.isFalse(document.cookie.indexOf("RT=") !== -1); + }); + + it("[After Opt-out] Should have set BOOMR_CONSENT=\"opted-out\" cookie", function() { + assert.isTrue(document.cookie.indexOf("BOOMR_CONSENT=\"opted-out\"") !== -1); + }); +}); + diff --git a/tests/page-templates/29-opt-out-opt-in/14-spa-opt-in-after-hard-nav.html b/tests/page-templates/29-opt-out-opt-in/14-spa-opt-in-after-hard-nav.html new file mode 100644 index 000000000..c5da14efc --- /dev/null +++ b/tests/page-templates/29-opt-out-opt-in/14-spa-opt-in-after-hard-nav.html @@ -0,0 +1,35 @@ +<%= header %> + +<%= consentInlinePlugin %> +<%= boomerangScript %> + + + +<%= footer %> diff --git a/tests/page-templates/29-opt-out-opt-in/14-spa-opt-in-after-hard-nav.js b/tests/page-templates/29-opt-out-opt-in/14-spa-opt-in-after-hard-nav.js new file mode 100644 index 000000000..ad7a7b773 --- /dev/null +++ b/tests/page-templates/29-opt-out-opt-in/14-spa-opt-in-after-hard-nav.js @@ -0,0 +1,31 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,describe,it */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["BOOMR_CONSENT_CONFIG", "BOOMR_OPT_OUT", "BOOMR_OPT_IN"]); + +describe("e2e/29-opt-out-opt-in/14-spa-opt-in-after-hard-nav.js", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should pass Consent Inline Plugin validation", function(done) { + t.validateConsentInlinePluginState(done); + }); + + describe("Beacon 1", function() { + it("Should have http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + + it("Should have cip.in and cip.v on first beacon", function() { + var b = tf.beacons[0]; + + assert.equal(b["cip.in"], "1"); + assert.equal(b["cip.v"], "2"); + }); + }); +}); diff --git a/tests/page-templates/29-opt-out-opt-in/15-spa-opt-in-after-route-change-click-xhr-routechange.html b/tests/page-templates/29-opt-out-opt-in/15-spa-opt-in-after-route-change-click-xhr-routechange.html new file mode 100644 index 000000000..317924a44 --- /dev/null +++ b/tests/page-templates/29-opt-out-opt-in/15-spa-opt-in-after-route-change-click-xhr-routechange.html @@ -0,0 +1,67 @@ +<%= header %> + +<%= consentInlinePlugin %> +<%= boomerangScript %> + + + +<%= footer %> \ No newline at end of file diff --git a/tests/page-templates/29-opt-out-opt-in/15-spa-opt-in-after-route-change-click-xhr-routechange.js b/tests/page-templates/29-opt-out-opt-in/15-spa-opt-in-after-route-change-click-xhr-routechange.js new file mode 100644 index 000000000..94e16e1ce --- /dev/null +++ b/tests/page-templates/29-opt-out-opt-in/15-spa-opt-in-after-route-change-click-xhr-routechange.js @@ -0,0 +1,77 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,describe,it */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["BOOMR_CONSENT_CONFIG", "BOOMR_OPT_OUT", "BOOMR_OPT_IN", "testRunOnce", "eventFired", "i"]); + +describe("e2e/29-opt-out-opt-in/15-spa-opt-in-after-route-change-click-xhr-routechange.js", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should pass Consent Inline Plugin validation", function(done) { + t.validateConsentInlinePluginState(done); + }); + + it("Should have sent 2 beacons (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 2); + }, + this.skip.bind(this)); + }); + + describe("Beacon 1", function() { + it("Should have http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + + it("Should have cip.in and cip.v on first beacon", function() { + var b = tf.beacons[0]; + + assert.equal(b["cip.in"], "1"); + assert.equal(b["cip.v"], "2"); + }); + }); + + describe("Beacon 2", function() { + it("Should have http.initiator = spa", function() { + assert.equal(tf.beacons[1]["http.initiator"], "spa"); + }); + + it("Should NOT have cip.in and cip.v on second beacon", function() { + assert.equal(tf.beacons[1]["cip.in"], undefined); + assert.equal(tf.beacons[1]["cip.v"], undefined); + }); + + it("Should have URL with #newstate", function() { + assert.include(tf.beacons[1].u, "#newstate"); + }); + + it("Should have rt.tstart around the time of the route change", function() { + assert.closeTo(tf.beacons[1]["rt.tstart"], t.routeChangeTimes[0], 25); + }); + + it("Should have Page Load Time ~2010ms", function() { + assert.closeTo(tf.beacons[1].t_done, t.imgTimes.img1.duration + 10, 100); + }); + + it("Should have Page Load Time >= 2010ms", function() { + assert.operator(tf.beacons[1].t_done, ">=", 2000 + 10); + }); + + it("Should have Back End Time = 0ms", function() { + assert.equal(tf.beacons[1].t_resp, 0); + }); + + it("Should have Front End Time ~2010ms", function() { + assert.closeTo(tf.beacons[1].t_page, t.imgTimes.img1.duration + 10, 100); + }); + }); +}); diff --git a/tests/page-templates/29-opt-out-opt-in/16-spa-opt-in-after-previous-opt-out.html b/tests/page-templates/29-opt-out-opt-in/16-spa-opt-in-after-previous-opt-out.html new file mode 100644 index 000000000..e7defd4ab --- /dev/null +++ b/tests/page-templates/29-opt-out-opt-in/16-spa-opt-in-after-previous-opt-out.html @@ -0,0 +1,79 @@ +<%= header %> + +<%= consentInlinePlugin %> +<%= boomerangScript %> + + +<%= footer %> \ No newline at end of file diff --git a/tests/page-templates/29-opt-out-opt-in/16-spa-opt-in-after-previous-opt-out.js b/tests/page-templates/29-opt-out-opt-in/16-spa-opt-in-after-previous-opt-out.js new file mode 100644 index 000000000..e4f6c722b --- /dev/null +++ b/tests/page-templates/29-opt-out-opt-in/16-spa-opt-in-after-previous-opt-out.js @@ -0,0 +1,26 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["BOOMR_CONSENT_CONFIG", "BOOMR_OPT_OUT", "BOOMR_OPT_IN", "testRunOnce", "eventFired", "i"]); + +describe("e2e/29-opt-out-opt-in/16-spa-opt-in-after-previous-opt-out", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass Consent Inline Plugin validation", function(done) { + t.validateConsentInlinePluginState(done); + }); + + it("[After Opt-out] Should have set BOOMR_CONSENT=\"opted-in\" cookie", function() { + assert.isTrue(document.cookie.indexOf("BOOMR_CONSENT=\"opted-in\"") !== -1); + }); + + it("[Opt-out before Boomerang loaded] Should not have BOOMR_CONSENT=\"opted-out\" cookie", function() { + assert.isTrue(document.cookie.indexOf("BOOMR_CONSENT=\"opted-out\"") === -1); + }); + + it("[After Opt-in] Should have sent exactly 2 beacon because these 2 beacons were queued from previous Opt-out", function() { + assert.equal(tf.beaconCount(), 2); + }); +}); diff --git a/tests/page-templates/29-opt-out-opt-in/17-spa-opted-out-in-previous-visit-2-beacons-not-sent.html b/tests/page-templates/29-opt-out-opt-in/17-spa-opted-out-in-previous-visit-2-beacons-not-sent.html new file mode 100644 index 000000000..4c96d1378 --- /dev/null +++ b/tests/page-templates/29-opt-out-opt-in/17-spa-opted-out-in-previous-visit-2-beacons-not-sent.html @@ -0,0 +1,73 @@ +<%= header %> + +<%= consentInlinePlugin %> +<%= boomerangScript %> + + +<%= footer %> \ No newline at end of file diff --git a/tests/page-templates/29-opt-out-opt-in/17-spa-opted-out-in-previous-visit-2-beacons-not-sent.js b/tests/page-templates/29-opt-out-opt-in/17-spa-opted-out-in-previous-visit-2-beacons-not-sent.js new file mode 100644 index 000000000..b5ac3e2da --- /dev/null +++ b/tests/page-templates/29-opt-out-opt-in/17-spa-opted-out-in-previous-visit-2-beacons-not-sent.js @@ -0,0 +1,26 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["BOOMR_CONSENT_CONFIG", "BOOMR_OPT_OUT", "BOOMR_OPT_IN", "testRunOnce", "eventFired"]); + +describe("e2e/29-opt-out-opt-in/17-spa-opted-out-in-previous-visit-2-beacons-not-sent", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass Consent Inline Plugin validation", function(done) { + t.validateConsentInlinePluginState(done); + }); + + it("[After Opt-out] Should have set BOOMR_CONSENT=\"opted-in\" cookie", function() { + assert.isTrue(document.cookie.indexOf("BOOMR_CONSENT=\"opted-in\"") === -1); + }); + + it("[Opt-out before Boomerang loaded] Should not have BOOMR_CONSENT=\"opted-out\" cookie", function() { + assert.isTrue(document.cookie.indexOf("BOOMR_CONSENT=\"opted-out\"") !== -1); + }); + + it("[After Opt-in] Should have sent exactly 0 beacons because Opt-out", function() { + assert.equal(tf.beaconCount(), 0); + }); +}); diff --git a/tests/page-templates/29-opt-out-opt-in/18-spa-opt-out-after-previous-opt-in.html b/tests/page-templates/29-opt-out-opt-in/18-spa-opt-out-after-previous-opt-in.html new file mode 100644 index 000000000..d1e37a115 --- /dev/null +++ b/tests/page-templates/29-opt-out-opt-in/18-spa-opt-out-after-previous-opt-in.html @@ -0,0 +1,80 @@ +<%= header %> + +<%= consentInlinePlugin %> +<%= boomerangScript %> + + +<%= footer %> \ No newline at end of file diff --git a/tests/page-templates/29-opt-out-opt-in/18-spa-opt-out-after-previous-opt-in.js b/tests/page-templates/29-opt-out-opt-in/18-spa-opt-out-after-previous-opt-in.js new file mode 100644 index 000000000..ef41f308c --- /dev/null +++ b/tests/page-templates/29-opt-out-opt-in/18-spa-opt-out-after-previous-opt-in.js @@ -0,0 +1,26 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["BOOMR_CONSENT_CONFIG", "BOOMR_OPT_OUT", "BOOMR_OPT_IN", "testRunOnce", "eventFired"]); + +describe("e2e/29-opt-out-opt-in/18-spa-opt-out-after-previous-opt-in", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass Consent Inline Plugin validation", function(done) { + t.validateConsentInlinePluginState(done); + }); + + it("[After Opt-out] Should have set BOOMR_CONSENT=\"opted-out\" cookie", function() { + assert.isTrue(document.cookie.indexOf("BOOMR_CONSENT=\"opted-out\"") !== -1); + }); + + it("[Opt-out before Boomerang loaded] Should not have BOOMR_CONSENT=\"opted-in\" cookie", function() { + assert.isTrue(document.cookie.indexOf("BOOMR_CONSENT=\"opted-in\"") === -1); + }); + + it("[After Opt-out] Should have sent exactly 1 beacon because the rest were blocked because of Opt-out", function() { + assert.equal(tf.beaconCount(), 1); + }); +}); diff --git a/tests/page-templates/29-opt-out-opt-in/19-spa-opt-out-before-page-ready.html b/tests/page-templates/29-opt-out-opt-in/19-spa-opt-out-before-page-ready.html new file mode 100644 index 000000000..20c1a4267 --- /dev/null +++ b/tests/page-templates/29-opt-out-opt-in/19-spa-opt-out-before-page-ready.html @@ -0,0 +1,73 @@ +<%= header %> + +<%= consentInlinePlugin %> + +<%= boomerangScript %> + + +<%= footer %> \ No newline at end of file diff --git a/tests/page-templates/29-opt-out-opt-in/19-spa-opt-out-before-page-ready.js b/tests/page-templates/29-opt-out-opt-in/19-spa-opt-out-before-page-ready.js new file mode 100644 index 000000000..e735aeec4 --- /dev/null +++ b/tests/page-templates/29-opt-out-opt-in/19-spa-opt-out-before-page-ready.js @@ -0,0 +1,26 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["BOOMR_CONSENT_CONFIG", "BOOMR_OPT_OUT", "BOOMR_OPT_IN", "testRunOnce", "eventFired"]); + +describe("e2e/29-opt-out-opt-in/19-spa-opt-out-before-page-ready", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass Consent Inline Plugin validation", function(done) { + t.validateConsentInlinePluginState(done); + }); + + it("[After Opt-out] Should have set BOOMR_CONSENT=\"opted-out\" cookie", function() { + assert.isTrue(document.cookie.indexOf("BOOMR_CONSENT=\"opted-out\"") !== -1); + }); + + it("[Opt-out before Boomerang loaded] Should not have BOOMR_CONSENT=\"opted-in\" cookie", function() { + assert.isTrue(document.cookie.indexOf("BOOMR_CONSENT=\"opted-in\"") === -1); + }); + + it("[After Opt-out] Should have sent exactly 0 beacons because Opt-out before Boomerang was loaded.", function() { + assert.equal(tf.beaconCount(), 0); + }); +}); diff --git a/tests/page-templates/29-opt-out-opt-in/20-spa-opt-in-before-page-ready.html b/tests/page-templates/29-opt-out-opt-in/20-spa-opt-in-before-page-ready.html new file mode 100644 index 000000000..d61274c70 --- /dev/null +++ b/tests/page-templates/29-opt-out-opt-in/20-spa-opt-in-before-page-ready.html @@ -0,0 +1,74 @@ +<%= header %> + +<%= consentInlinePlugin %> + +<%= boomerangScript %> + + +<%= footer %> \ No newline at end of file diff --git a/tests/page-templates/29-opt-out-opt-in/20-spa-opt-in-before-page-ready.js b/tests/page-templates/29-opt-out-opt-in/20-spa-opt-in-before-page-ready.js new file mode 100644 index 000000000..e5ee949a4 --- /dev/null +++ b/tests/page-templates/29-opt-out-opt-in/20-spa-opt-in-before-page-ready.js @@ -0,0 +1,26 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["BOOMR_CONSENT_CONFIG", "BOOMR_OPT_OUT", "BOOMR_OPT_IN", "testRunOnce", "eventFired", "i"]); + +describe("e2e/29-opt-out-opt-in/20-spa-opt-in-before-page-ready", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass Consent Inline Plugin validation", function(done) { + t.validateConsentInlinePluginState(done); + }); + + it("[After Opt-in] Should not have set BOOMR_CONSENT=\"opted-out\" cookie", function() { + assert.isTrue(document.cookie.indexOf("BOOMR_CONSENT=\"opted-out\"") === -1); + }); + + it("[Opt-in before Boomerang loaded] Should have BOOMR_CONSENT=\"opted-in\" cookie", function() { + assert.isTrue(document.cookie.indexOf("BOOMR_CONSENT=\"opted-in\"") !== -1); + }); + + it("[After Opt-in] Should have sent exactly 2 beacons because Opt-in before Boomerang was loaded.", function() { + assert.equal(tf.beaconCount(), 2); + }); +}); diff --git a/tests/page-templates/30-same-site-cookie/01-none-in-current-window.html b/tests/page-templates/30-same-site-cookie/01-none-in-current-window.html new file mode 100644 index 000000000..ab9a94d98 --- /dev/null +++ b/tests/page-templates/30-same-site-cookie/01-none-in-current-window.html @@ -0,0 +1,11 @@ +<%= header %> +<%= boomerangScript %> + + +<%= footer %> \ No newline at end of file diff --git a/tests/page-templates/30-same-site-cookie/01-none-in-current-window.js b/tests/page-templates/30-same-site-cookie/01-none-in-current-window.js new file mode 100644 index 000000000..ed488886e --- /dev/null +++ b/tests/page-templates/30-same-site-cookie/01-none-in-current-window.js @@ -0,0 +1,27 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/30-same-site-cookie/01-none-in-current-window", function() { + it("Created RT Cookie with SameSite=None", function() { + /* Note: + * Over NOT SECURE connection/HTTP a cookie will be created but: + * 1. with SameSite=Lax because SameSite=None can't be created on NOT SECURE CONNECTION + * 2. Secure flag will be absent because this flag is valid only on SECURE CONNECTION + */ + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isDefined(cookie.si, "Session id read"); + }); + + it("Should have cookie attributes SameSite=None; Secure", function() { + if (window.location.protocol !== "https:") { + this.skip(); + } + + var SameSiteAttributeParts = BOOMR.utils.getSameSiteAttributeParts(); + + assert.equal(SameSiteAttributeParts.length, 2); + assert.equal(SameSiteAttributeParts[0], "SameSite=None"); + assert.equal(SameSiteAttributeParts[1], "Secure"); + }); +}); diff --git a/tests/page-templates/30-same-site-cookie/02-lax-in-current-window.html b/tests/page-templates/30-same-site-cookie/02-lax-in-current-window.html new file mode 100644 index 000000000..007e04f04 --- /dev/null +++ b/tests/page-templates/30-same-site-cookie/02-lax-in-current-window.html @@ -0,0 +1,11 @@ +<%= header %> +<%= boomerangScript %> + + +<%= footer %> \ No newline at end of file diff --git a/tests/page-templates/30-same-site-cookie/02-lax-in-current-window.js b/tests/page-templates/30-same-site-cookie/02-lax-in-current-window.js new file mode 100644 index 000000000..cdafe2709 --- /dev/null +++ b/tests/page-templates/30-same-site-cookie/02-lax-in-current-window.js @@ -0,0 +1,17 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/30-same-site-cookie/02-lax-in-current-window", function() { + it("Created RT Cookie with SameSite=Lax", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isDefined(cookie.si, "Session id read"); + }); + + it("Should have cookie attributes SameSite=Lax", function() { + var SameSiteAttributeParts = BOOMR.utils.getSameSiteAttributeParts(); + + assert.equal(SameSiteAttributeParts.length, 1); + assert.equal(SameSiteAttributeParts[0], "SameSite=Lax"); + }); +}); diff --git a/tests/page-templates/30-same-site-cookie/03-strict-in-current-window.html b/tests/page-templates/30-same-site-cookie/03-strict-in-current-window.html new file mode 100644 index 000000000..1140ea93b --- /dev/null +++ b/tests/page-templates/30-same-site-cookie/03-strict-in-current-window.html @@ -0,0 +1,11 @@ +<%= header %> +<%= boomerangScript %> + + +<%= footer %> \ No newline at end of file diff --git a/tests/page-templates/30-same-site-cookie/03-strict-in-current-window.js b/tests/page-templates/30-same-site-cookie/03-strict-in-current-window.js new file mode 100644 index 000000000..fe5e9f4df --- /dev/null +++ b/tests/page-templates/30-same-site-cookie/03-strict-in-current-window.js @@ -0,0 +1,17 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/30-same-site-cookie/03-strict-in-current-window", function() { + it("Created RT Cookie with SameSite=Strict", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isDefined(cookie.si, "Session id read"); + }); + + it("Should have cookie attributes SameSite=Strict", function() { + var SameSiteAttributeParts = BOOMR.utils.getSameSiteAttributeParts(); + + assert.equal(SameSiteAttributeParts.length, 1); + assert.equal(SameSiteAttributeParts[0], "SameSite=Strict"); + }); +}); diff --git a/tests/page-templates/30-same-site-cookie/04-omitted-in-current-window.html b/tests/page-templates/30-same-site-cookie/04-omitted-in-current-window.html new file mode 100644 index 000000000..9fff4ba76 --- /dev/null +++ b/tests/page-templates/30-same-site-cookie/04-omitted-in-current-window.html @@ -0,0 +1,10 @@ +<%= header %> +<%= boomerangScript %> + + +<%= footer %> \ No newline at end of file diff --git a/tests/page-templates/30-same-site-cookie/04-omitted-in-current-window.js b/tests/page-templates/30-same-site-cookie/04-omitted-in-current-window.js new file mode 100644 index 000000000..a975e6885 --- /dev/null +++ b/tests/page-templates/30-same-site-cookie/04-omitted-in-current-window.js @@ -0,0 +1,17 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/30-same-site-cookie/04-omitted-in-current-window", function() { + it("Created RT Cookie when SameSite config is omitted", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isDefined(cookie.si, "Session id read"); + }); + + it("Should have cookie attributes SameSite=Lax", function() { + var SameSiteAttributeParts = BOOMR.utils.getSameSiteAttributeParts(); + + assert.equal(SameSiteAttributeParts.length, 1); + assert.equal(SameSiteAttributeParts[0], "SameSite=Lax"); + }); +}); diff --git a/tests/page-templates/30-same-site-cookie/05-bogus-in-current-window.html b/tests/page-templates/30-same-site-cookie/05-bogus-in-current-window.html new file mode 100644 index 000000000..21081feee --- /dev/null +++ b/tests/page-templates/30-same-site-cookie/05-bogus-in-current-window.html @@ -0,0 +1,11 @@ +<%= header %> +<%= boomerangScript %> + + +<%= footer %> \ No newline at end of file diff --git a/tests/page-templates/30-same-site-cookie/05-bogus-in-current-window.js b/tests/page-templates/30-same-site-cookie/05-bogus-in-current-window.js new file mode 100644 index 000000000..961f17a35 --- /dev/null +++ b/tests/page-templates/30-same-site-cookie/05-bogus-in-current-window.js @@ -0,0 +1,17 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/30-same-site-cookie/05-bogus-in-current-window", function() { + it("Created RT Cookie when SameSite config is bogus", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isDefined(cookie.si, "Session id read"); + }); + + it("Should have cookie attributes SameSite=Lax", function() { + var SameSiteAttributeParts = BOOMR.utils.getSameSiteAttributeParts(); + + assert.equal(SameSiteAttributeParts.length, 1); + assert.equal(SameSiteAttributeParts[0], "SameSite=Lax"); + }); +}); diff --git a/tests/page-templates/30-same-site-cookie/06-none-lowercase-in-current-window.html b/tests/page-templates/30-same-site-cookie/06-none-lowercase-in-current-window.html new file mode 100644 index 000000000..6563a38b0 --- /dev/null +++ b/tests/page-templates/30-same-site-cookie/06-none-lowercase-in-current-window.html @@ -0,0 +1,11 @@ +<%= header %> +<%= boomerangScript %> + + +<%= footer %> \ No newline at end of file diff --git a/tests/page-templates/30-same-site-cookie/06-none-lowercase-in-current-window.js b/tests/page-templates/30-same-site-cookie/06-none-lowercase-in-current-window.js new file mode 100644 index 000000000..2b744cfbf --- /dev/null +++ b/tests/page-templates/30-same-site-cookie/06-none-lowercase-in-current-window.js @@ -0,0 +1,27 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/30-same-site-cookie/06-none-lowercase-in-current-window", function() { + it("Created RT Cookie with SameSite=None", function() { + /* Note: + * Over NOT SECURE connection/HTTP a cookie will be created but: + * 1. with SameSite=Lax because SameSite=None can't be created on NOT SECURE CONNECTION + * 2. Secure flag will be absent because this flag is valid only on SECURE CONNECTION + */ + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isDefined(cookie.si, "Session id read"); + }); + + it("Should have cookie attributes SameSite=None; Secure", function() { + if (window.location.protocol !== "https:") { + this.skip(); + } + + var SameSiteAttributeParts = BOOMR.utils.getSameSiteAttributeParts(); + + assert.equal(SameSiteAttributeParts.length, 2); + assert.equal(SameSiteAttributeParts[0], "SameSite=None"); + assert.equal(SameSiteAttributeParts[1], "Secure"); + }); +}); diff --git a/tests/page-templates/30-same-site-cookie/07-lax-lowercase-in-current-window.html b/tests/page-templates/30-same-site-cookie/07-lax-lowercase-in-current-window.html new file mode 100644 index 000000000..fd385fbb3 --- /dev/null +++ b/tests/page-templates/30-same-site-cookie/07-lax-lowercase-in-current-window.html @@ -0,0 +1,11 @@ +<%= header %> +<%= boomerangScript %> + + +<%= footer %> \ No newline at end of file diff --git a/tests/page-templates/30-same-site-cookie/07-lax-lowercase-in-current-window.js b/tests/page-templates/30-same-site-cookie/07-lax-lowercase-in-current-window.js new file mode 100644 index 000000000..5bffff99f --- /dev/null +++ b/tests/page-templates/30-same-site-cookie/07-lax-lowercase-in-current-window.js @@ -0,0 +1,17 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/30-same-site-cookie/07-lax-lowercase-in-current-window", function() { + it("Created RT Cookie with SameSite=Lax", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isDefined(cookie.si, "Session id read"); + }); + + it("Should have cookie attributes SameSite=Lax", function() { + var SameSiteAttributeParts = BOOMR.utils.getSameSiteAttributeParts(); + + assert.equal(SameSiteAttributeParts.length, 1); + assert.equal(SameSiteAttributeParts[0], "SameSite=Lax"); + }); +}); diff --git a/tests/page-templates/30-same-site-cookie/08-strict-lowercase-in-current-window.html b/tests/page-templates/30-same-site-cookie/08-strict-lowercase-in-current-window.html new file mode 100644 index 000000000..7f158fcb9 --- /dev/null +++ b/tests/page-templates/30-same-site-cookie/08-strict-lowercase-in-current-window.html @@ -0,0 +1,11 @@ +<%= header %> +<%= boomerangScript %> + + +<%= footer %> \ No newline at end of file diff --git a/tests/page-templates/30-same-site-cookie/08-strict-lowercase-in-current-window.js b/tests/page-templates/30-same-site-cookie/08-strict-lowercase-in-current-window.js new file mode 100644 index 000000000..51af40bca --- /dev/null +++ b/tests/page-templates/30-same-site-cookie/08-strict-lowercase-in-current-window.js @@ -0,0 +1,17 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/30-same-site-cookie/08-strict-lowercase-in-current-window", function() { + it("Created RT Cookie with SameSite=Strict", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isDefined(cookie.si, "Session id read"); + }); + + it("Should have cookie attributes SameSite=Strict", function() { + var SameSiteAttributeParts = BOOMR.utils.getSameSiteAttributeParts(); + + assert.equal(SameSiteAttributeParts.length, 1); + assert.equal(SameSiteAttributeParts[0], "SameSite=Strict"); + }); +}); diff --git a/tests/page-templates/30-same-site-cookie/09-none-secure-attribute-in-current-window.html b/tests/page-templates/30-same-site-cookie/09-none-secure-attribute-in-current-window.html new file mode 100644 index 000000000..1c5c84755 --- /dev/null +++ b/tests/page-templates/30-same-site-cookie/09-none-secure-attribute-in-current-window.html @@ -0,0 +1,12 @@ +<%= header %> +<%= boomerangScript %> + + +<%= footer %> \ No newline at end of file diff --git a/tests/page-templates/30-same-site-cookie/09-none-secure-attribute-in-current-window.js b/tests/page-templates/30-same-site-cookie/09-none-secure-attribute-in-current-window.js new file mode 100644 index 000000000..56cafa382 --- /dev/null +++ b/tests/page-templates/30-same-site-cookie/09-none-secure-attribute-in-current-window.js @@ -0,0 +1,10 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/30-same-site-cookie/09-none-secure-attribute-in-current-window", function() { + it("Created RT Cookie with SameSite=None", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isDefined(cookie.si, "Session id read"); + }); +}); diff --git a/tests/page-templates/30-same-site-cookie/10-strict-secure-attribute-in-current-window.html b/tests/page-templates/30-same-site-cookie/10-strict-secure-attribute-in-current-window.html new file mode 100644 index 000000000..693d333c0 --- /dev/null +++ b/tests/page-templates/30-same-site-cookie/10-strict-secure-attribute-in-current-window.html @@ -0,0 +1,12 @@ +<%= header %> +<%= boomerangScript %> + + +<%= footer %> \ No newline at end of file diff --git a/tests/page-templates/30-same-site-cookie/10-strict-secure-attribute-in-current-window.js b/tests/page-templates/30-same-site-cookie/10-strict-secure-attribute-in-current-window.js new file mode 100644 index 000000000..4e4f7826c --- /dev/null +++ b/tests/page-templates/30-same-site-cookie/10-strict-secure-attribute-in-current-window.js @@ -0,0 +1,10 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/30-same-site-cookie/10-strict-secure-attribute-in-current-window", function() { + it("Created RT Cookie with SameSite=Strict", function() { + var cookie = BOOMR.utils.getSubCookies(BOOMR.utils.getCookie("RT")); + + assert.isDefined(cookie.si, "Session id read"); + }); +}); diff --git a/tests/page-templates/31-eventtiming/00-basic.html b/tests/page-templates/31-eventtiming/00-basic.html new file mode 100644 index 000000000..c28a0555a --- /dev/null +++ b/tests/page-templates/31-eventtiming/00-basic.html @@ -0,0 +1,101 @@ +<%= header %> + +<%= boomerangScript %> + + + +<%= footer %> diff --git a/tests/page-templates/31-eventtiming/00-basic.js b/tests/page-templates/31-eventtiming/00-basic.js new file mode 100644 index 000000000..ccdf51379 --- /dev/null +++ b/tests/page-templates/31-eventtiming/00-basic.js @@ -0,0 +1,44 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["PerformanceEventTiming", "POs", "EVENT_TYPES"]); + +describe("e2e/31-eventtiming/00-basic", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a beacon", function() { + assert.isTrue(tf.fired_onbeacon); + }); + + it("Should have included raw events (et.e) on the beacon", function() { + if (BOOMR.utils.Compression && BOOMR.utils.Compression.jsUrl) { + // ensure a few items are there + + // ensure FID is there + assert.include(tf.lastBeacon()["et.e"], "~(c~0~d~'p0~fi~1~i~'a~p~'nm~s~'2s)"); + + // ensure click is there + assert.include(tf.lastBeacon()["et.e"], "~(c~0~d~'1f~i~'2t~n~0~p~'1e~s~'rt)"); + } + else { + assert.isDefined(tf.lastBeacon()["et.e"]); + } + }); + + it("Should have included a map for each event in the events list (et.e) on the beacon", function() { + if (BOOMR.utils.Compression && BOOMR.utils.Compression.jsUrl) { + for (var eventName in window.EVENT_TYPES) { + assert.include(tf.lastBeacon()["et.e"], "n~" + window.EVENT_TYPES[eventName]); + } + } + else { + assert.isDefined(tf.lastBeacon()["et.e"]); + } + }); + + it("Should have included First Input Delay (et.fid) on the beacon", function() { + assert.equal(tf.lastBeacon()["et.fid"], 50); + }); +}); diff --git a/tests/page-templates/31-eventtiming/01-iframe-loader.html b/tests/page-templates/31-eventtiming/01-iframe-loader.html new file mode 100644 index 000000000..212bbfa17 --- /dev/null +++ b/tests/page-templates/31-eventtiming/01-iframe-loader.html @@ -0,0 +1,45 @@ +<%= header %> + + +<%= boomerangSnippet %> + + + +<%= footer %> \ No newline at end of file diff --git a/tests/page-templates/31-eventtiming/01-iframe-loader.js b/tests/page-templates/31-eventtiming/01-iframe-loader.js new file mode 100644 index 000000000..69836de01 --- /dev/null +++ b/tests/page-templates/31-eventtiming/01-iframe-loader.js @@ -0,0 +1,27 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["PerformanceEventTiming", "POs"]); + +describe("e2e/31-eventtiming/01-iframe-loader", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a beacon", function() { + assert.isTrue(tf.fired_onbeacon); + }); + + it("Should have included raw events (et.e) on the beacon", function() { + if (BOOMR.utils.Compression && BOOMR.utils.Compression.jsUrl) { + assert.equal(tf.lastBeacon()["et.e"], "~(~(c~0~d~'p0~fi~1~p~'nm~s~'2s)~(c~0~d~'1e~n~0~p~'1e~s~'rs))"); + } + else { + assert.isDefined(tf.lastBeacon()["et.e"]); + } + }); + + it("Should have included First Input Delay (et.fid) on the beacon", function() { + assert.equal(tf.lastBeacon()["et.fid"], 50); + }); +}); diff --git a/tests/page-templates/31-eventtiming/02-inp.html b/tests/page-templates/31-eventtiming/02-inp.html new file mode 100644 index 000000000..715b3a856 --- /dev/null +++ b/tests/page-templates/31-eventtiming/02-inp.html @@ -0,0 +1,60 @@ +<%= header %> + + +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/31-eventtiming/02-inp.js b/tests/page-templates/31-eventtiming/02-inp.js new file mode 100644 index 000000000..10a5d100a --- /dev/null +++ b/tests/page-templates/31-eventtiming/02-inp.js @@ -0,0 +1,74 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["PerformanceEventTiming", "POs", "curStartTime", "generateEvent"]); + +describe("e2e/31-eventtiming/02-inp", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a beacon", function() { + assert.isTrue(tf.fired_onbeacon); + }); + + describe("Page Load beacon", function() { + it("Should not have included Interaction to Next Paint (et.inp) on the Page Load beacon", function() { + assert.isUndefined(tf.beacons[0]["et.inp"]); + }); + + it("Should not have included Interaction to Next Paint target (et.inp.e) on the Page Load beacon", function() { + assert.isUndefined(tf.beacons[0]["et.inp.e"]); + }); + + it("Should not have included Interaction to Next Paint timestamp (et.inp.t) on the Page Load beacon", function() { + assert.isUndefined(tf.beacons[0]["et.inp.t"]); + }); + + it("Should have included Incremental Interaction to Next Paint (et.inp.inc) on the Page Load beacon", function() { + assert.equal(parseInt(tf.beacons[0]["et.inp.inc"], 10), 100); + }); + + it("Should have included Incremental Interaction to Next Paint target (et.inp.inc.e) on the Page Load beacon", function() { + assert.equal(tf.beacons[0]["et.inp.inc.e"], "span#interaction-target"); + }); + + it("Should have included Incremental Interaction to Next Paint timestamp (et.inp.inc.t) on the Page Load beacon", function() { + assert.operator(parseInt(tf.beacons[0]["et.inp.inc.t"], 10), ">=", 0); + }); + + it("Should have included Incremental Interaction to Next Paint timestamp (et.inp.inc.t) and it should be rounded on the Page Load beacon", function() { + assert.equal(parseFloat(tf.beacons[0]["et.inp.inc.t"]), parseInt(tf.beacons[0]["et.inp.inc.t"], 10)); + }); + }); + + describe("Unload beacon", function() { + it("Should have included Interaction to Next Paint (et.inp) on the Unload beacon", function() { + assert.equal(parseInt(tf.beacons[1]["et.inp"], 10), 100); + }); + + it("Should have included Interaction to Next Paint target (et.inp.e) on the Unload beacon", function() { + assert.equal(tf.beacons[1]["et.inp.e"], "span#interaction-target"); + }); + + it("Should have included Interaction to Next Paint timestamp (et.inp.t) on the Unload beacon", function() { + assert.operator(parseInt(tf.beacons[1]["et.inp.t"], 10), ">=", 0); + }); + + it("Should have included Interaction to Next Paint timestamp (et.inp.t) and it should be rounded on the Unload beacon", function() { + assert.equal(parseFloat(tf.beacons[1]["et.inp.t"]), parseInt(tf.beacons[1]["et.inp.t"], 10)); + }); + + it("Should not have included Incremental Interaction to Next Paint (et.inp.inc) on the Unload beacon", function() { + assert.isUndefined(tf.beacons[1]["et.inc.inp"]); + }); + + it("Should not have included Incremental Interaction to Next Paint target (et.inp.inc.e) on the Unload beacon", function() { + assert.isUndefined(tf.beacons[1]["et.inp.inc.e"]); + }); + + it("Should not have included Incremental Interaction to Next Paint timestamp (et.inp.inc.t) on the Unload beacon", function() { + assert.isUndefined(tf.beacons[1]["et.inp.inc.t"]); + }); + }); +}); diff --git a/tests/page-templates/31-eventtiming/03-inp-counts.html b/tests/page-templates/31-eventtiming/03-inp-counts.html new file mode 100644 index 000000000..a93f6e9ba --- /dev/null +++ b/tests/page-templates/31-eventtiming/03-inp-counts.html @@ -0,0 +1,69 @@ +<%= header %> + + +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/31-eventtiming/03-inp-counts.js b/tests/page-templates/31-eventtiming/03-inp-counts.js new file mode 100644 index 000000000..1f82f647b --- /dev/null +++ b/tests/page-templates/31-eventtiming/03-inp-counts.js @@ -0,0 +1,66 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["PerformanceEventTiming", "POs", "curStartTime", "generateEvent"]); + +describe("e2e/31-eventtiming/03-inp-counts", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a beacon", function() { + assert.isTrue(tf.fired_onbeacon); + }); + + describe("Page Load beacon", function() { + it("Should not have included Interaction to Next Paint (et.inp) on the Page Load beacon", function() { + assert.isUndefined(tf.beacons[0]["et.inp"]); + }); + + it("Should not have included Interaction to Next Paint target (et.inp.e) on the Page Load beacon", function() { + assert.isUndefined(tf.beacons[0]["et.inp.e"]); + }); + + it("Should not have included Interaction to Next Paint timestamp (et.inp.t) on the Page Load beacon", function() { + assert.isUndefined(tf.beacons[0]["et.inp.t"]); + }); + + it("Should have set Incremental Interaction to Next Paint (et.inp.inc) to be the 2nd largest interaction on the Page Load beacon, based on 50+ interactions", function() { + assert.equal(parseInt(tf.beacons[0]["et.inp.inc"], 10), 200); + }); + + it("Should have included Incremental Interaction to Next Paint target (et.inp.inc.e) on the Page Load beacon", function() { + assert.equal(tf.beacons[0]["et.inp.inc.e"], "span#interaction-target"); + }); + + it("Should have included Incremental Interaction to Next Paint timestamp (et.inp.inc.t) on the Page Load beacon", function() { + assert.operator(parseInt(tf.beacons[0]["et.inp.inc.t"], 10), ">=", 0); + }); + }); + + describe("Unload beacon", function() { + it("Should have set Interaction to Next Paint (et.inp) to be the 2nd largest interaction on the Unload beacon, based on 50+ interactions", function() { + assert.equal(parseInt(tf.beacons[1]["et.inp"], 10), 200); + }); + + it("Should have included Interaction to Next Paint target (et.inp.e) on the Unload beacon", function() { + assert.equal(tf.beacons[1]["et.inp.e"], "span#interaction-target"); + }); + + it("Should have included Interaction to Next Paint timestamp (et.inp.t) on the Unload beacon", function() { + assert.operator(parseInt(tf.beacons[1]["et.inp.t"], 10), ">=", 0); + }); + + it("Should not have included Incremental Interaction to Next Paint (et.inp.inc) on the Unload beacon", function() { + assert.isUndefined(tf.beacons[1]["et.inc.inp"]); + }); + + it("Should not have included Incremental Interaction to Next Paint target (et.inp.inc.e) on the Unload beacon", function() { + assert.isUndefined(tf.beacons[1]["et.inp.inc.e"]); + }); + + it("Should not have included Incremental Interaction to Next Paint timestamp (et.inp.inc.t) on the Unload beacon", function() { + assert.isUndefined(tf.beacons[1]["et.inp.inc.t"]); + }); + }); +}); diff --git a/tests/page-templates/31-eventtiming/04-inp-counts-many-interactions.html b/tests/page-templates/31-eventtiming/04-inp-counts-many-interactions.html new file mode 100644 index 000000000..6e6b58a63 --- /dev/null +++ b/tests/page-templates/31-eventtiming/04-inp-counts-many-interactions.html @@ -0,0 +1,70 @@ +<%= header %> + + +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/31-eventtiming/04-inp-counts-many-interactions.js b/tests/page-templates/31-eventtiming/04-inp-counts-many-interactions.js new file mode 100644 index 000000000..1b9f6f6e4 --- /dev/null +++ b/tests/page-templates/31-eventtiming/04-inp-counts-many-interactions.js @@ -0,0 +1,66 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["PerformanceEventTiming", "POs", "curStartTime", "generateEvent"]); + +describe("e2e/31-eventtiming/04-inp-counts-many-interactions", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a beacon", function() { + assert.isTrue(tf.fired_onbeacon); + }); + + describe("Page Load beacon", function() { + it("Should not have included Interaction to Next Paint (et.inp) on the Page Load beacon", function() { + assert.isUndefined(tf.beacons[0]["et.inp"]); + }); + + it("Should not have included Interaction to Next Paint target (et.inp.e) on the Page Load beacon", function() { + assert.isUndefined(tf.beacons[0]["et.inp.e"]); + }); + + it("Should not have included Interaction to Next Paint timestamp (et.inp.t) on the Page Load beacon", function() { + assert.isUndefined(tf.beacons[0]["et.inp.t"]); + }); + + it("Should have set Incremental Interaction to Next Paint (et.inp.inc) to be the 4th largest interaction on the Page Loads beacon, based on 500+ interaction, if when we only have 4 long interactions", function() { + assert.equal(parseInt(tf.beacons[0]["et.inp.inc"], 10), 50); + }); + + it("Should have included Incremental Interaction to Next Paint target (et.inp.inc.e) on the Page Load beacon", function() { + assert.equal(tf.beacons[0]["et.inp.inc.e"], "span#interaction-target"); + }); + + it("Should have included Incremental Interaction to Next Paint timestamp (et.inp.inc.t) on the Page Load beacon", function() { + assert.operator(parseInt(tf.beacons[0]["et.inp.inc.t"], 10), ">=", 0); + }); + }); + + describe("Unload beacon", function() { + it("Should have set Interaction to Next Paint (et.inp) to be the 4th largest interaction on the Unload beacon, based on 500+ interaction, if when we only have 4 long interactions", function() { + assert.equal(parseInt(tf.beacons[1]["et.inp"], 10), 50); + }); + + it("Should have included Interaction to Next Paint target (et.inp.e) on the Unload beacon", function() { + assert.equal(tf.beacons[1]["et.inp.e"], "span#interaction-target"); + }); + + it("Should have included Interaction to Next Paint timestamp (et.inp.t) on the Unload beacon", function() { + assert.operator(parseInt(tf.beacons[1]["et.inp.t"], 10), ">=", 0); + }); + + it("Should not have included Incremental Interaction to Next Paint (et.inp.inc) on the Unload beacon", function() { + assert.isUndefined(tf.beacons[1]["et.inc.inp"]); + }); + + it("Should not have included Incremental Interaction to Next Paint target (et.inp.inc.e) on the Unload beacon", function() { + assert.isUndefined(tf.beacons[1]["et.inp.inc.e"]); + }); + + it("Should not have included Incremental Interaction to Next Paint timestamp (et.inp.inc.t) on the Unload beacon", function() { + assert.isUndefined(tf.beacons[1]["et.inp.inc.t"]); + }); + }); +}); diff --git a/tests/page-templates/31-eventtiming/05-inp-just-fid.html b/tests/page-templates/31-eventtiming/05-inp-just-fid.html new file mode 100644 index 000000000..f3ebdb9b6 --- /dev/null +++ b/tests/page-templates/31-eventtiming/05-inp-just-fid.html @@ -0,0 +1,41 @@ +<%= header %> + + +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/31-eventtiming/05-inp-just-fid.js b/tests/page-templates/31-eventtiming/05-inp-just-fid.js new file mode 100644 index 000000000..963fa172b --- /dev/null +++ b/tests/page-templates/31-eventtiming/05-inp-just-fid.js @@ -0,0 +1,66 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["PerformanceEventTiming", "POs"]); + +describe("e2e/31-eventtiming/05-inp-just-fid.js", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a beacon", function() { + assert.isTrue(tf.fired_onbeacon); + }); + + describe("Page Load beacon", function() { + it("Should not have included Interaction to Next Paint (et.inp) on the Page Load beacon", function() { + assert.isUndefined(tf.beacons[0]["et.inp"]); + }); + + it("Should not have included Interaction to Next Paint target (et.inp.e) on the Page Load beacon", function() { + assert.isUndefined(tf.beacons[0]["et.inp.e"]); + }); + + it("Should not have included Interaction to Next Paint timestamp (et.inp.t) on the Page Load beacon", function() { + assert.isUndefined(tf.beacons[0]["et.inp.t"]); + }); + + it("Should have set Incremental Interaction to Next Paint (et.inp.inc) to be the FID on the Page Load beacon", function() { + assert.equal(parseInt(tf.beacons[0]["et.inp.inc"], 10), 900); + }); + + it("Should have included Incremental Interaction to Next Paint target (et.inp.inc.e) on the Page Load beacon", function() { + assert.equal(tf.beacons[0]["et.inp.inc.e"], "span#interaction-target"); + }); + + it("Should have included Incremental Interaction to Next Paint timestamp (et.inp.inc.t) on the Page Load beacon", function() { + assert.operator(parseInt(tf.beacons[0]["et.inp.inc.t"], 10), ">=", 0); + }); + }); + + describe("Unload beacon", function() { + it("Should have set Interaction to Next Paint (et.inp) to be the FID on the Unload beacon", function() { + assert.equal(parseInt(tf.beacons[1]["et.inp"], 10), 900); + }); + + it("Should have included Interaction to Next Paint target (et.inp.e) on the Unload beacon", function() { + assert.equal(tf.beacons[1]["et.inp.e"], "span#interaction-target"); + }); + + it("Should have included Interaction to Next Paint timestamp (et.inp.t) on the Unload beacon", function() { + assert.operator(parseInt(tf.beacons[1]["et.inp.t"], 10), ">=", 0); + }); + + it("Should not have included Incremental Interaction to Next Paint (et.inp.inc) on the Unload beacon", function() { + assert.isUndefined(tf.beacons[1]["et.inc.inp"]); + }); + + it("Should not have included Incremental Interaction to Next Paint target (et.inp.inc.e) on the Unload beacon", function() { + assert.isUndefined(tf.beacons[1]["et.inp.inc.e"]); + }); + + it("Should not have included Incremental Interaction to Next Paint timestamp (et.inp.inc.t) on the Unload beacon", function() { + assert.isUndefined(tf.beacons[1]["et.inp.inc.t"]); + }); + }); +}); diff --git a/tests/page-templates/31-eventtiming/06-inp-counts-many-interactions-2.html b/tests/page-templates/31-eventtiming/06-inp-counts-many-interactions-2.html new file mode 100644 index 000000000..9e5002834 --- /dev/null +++ b/tests/page-templates/31-eventtiming/06-inp-counts-many-interactions-2.html @@ -0,0 +1,73 @@ +<%= header %> + + +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/31-eventtiming/06-inp-counts-many-interactions-2.js b/tests/page-templates/31-eventtiming/06-inp-counts-many-interactions-2.js new file mode 100644 index 000000000..82da0e976 --- /dev/null +++ b/tests/page-templates/31-eventtiming/06-inp-counts-many-interactions-2.js @@ -0,0 +1,66 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["PerformanceEventTiming", "POs", "curStartTime", "generateEvent"]); + +describe("e2e/31-eventtiming/06-inp-counts-many-interactions-2", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a beacon", function() { + assert.isTrue(tf.fired_onbeacon); + }); + + describe("Page Load beacon", function() { + it("Should not have included Interaction to Next Paint (et.inp) on the Page Load beacon", function() { + assert.isUndefined(tf.beacons[0]["et.inp"]); + }); + + it("Should not have included Interaction to Next Paint target (et.inp.e) on the Page Load beacon", function() { + assert.isUndefined(tf.beacons[0]["et.inp.e"]); + }); + + it("Should not have included Interaction to Next Paint timestamp (et.inp.t) on the Page Load beacon", function() { + assert.isUndefined(tf.beacons[0]["et.inp.t"]); + }); + + it("Should have set Incremental Interaction to Next Paint (et.inp.inc) to be the 11th largest interaction on the Unload beacon, based on 500+ interactions", function() { + assert.equal(parseInt(tf.beacons[0]["et.inp.inc"], 10), 100); + }); + + it("Should have included Incremental Interaction to Next Paint target (et.inp.inc.e) on the Page Load beacon", function() { + assert.equal(tf.beacons[0]["et.inp.inc.e"], "span#interaction-target"); + }); + + it("Should have included Incremental Interaction to Next Paint timestamp (et.inp.inc.t) on the Page Load beacon", function() { + assert.operator(parseInt(tf.beacons[0]["et.inp.inc.t"], 10), ">=", 0); + }); + }); + + describe("Unload beacon", function() { + it("Should have set Interaction to Next Paint (et.inp) to be the 11th largest interaction on the Unload beacon, based on 500+ interactions", function() { + assert.equal(parseInt(tf.beacons[1]["et.inp"], 10), 100); + }); + + it("Should have included Interaction to Next Paint target (et.inp.e) on the Unload beacon", function() { + assert.equal(tf.beacons[1]["et.inp.e"], "span#interaction-target"); + }); + + it("Should have included Interaction to Next Paint timestamp (et.inp.t) on the Unload beacon", function() { + assert.operator(parseInt(tf.beacons[1]["et.inp.t"], 10), ">=", 0); + }); + + it("Should not have included Incremental Interaction to Next Paint (et.inp.inc) on the Unload beacon", function() { + assert.isUndefined(tf.beacons[1]["et.inc.inp"]); + }); + + it("Should not have included Incremental Interaction to Next Paint target (et.inp.inc.e) on the Unload beacon", function() { + assert.isUndefined(tf.beacons[1]["et.inp.inc.e"]); + }); + + it("Should not have included Incremental Interaction to Next Paint timestamp (et.inp.inc.t) on the Unload beacon", function() { + assert.isUndefined(tf.beacons[1]["et.inp.inc.t"]); + }); + }); +}); diff --git a/tests/page-templates/31-eventtiming/07-inp-after-page-load.html b/tests/page-templates/31-eventtiming/07-inp-after-page-load.html new file mode 100644 index 000000000..92e940a98 --- /dev/null +++ b/tests/page-templates/31-eventtiming/07-inp-after-page-load.html @@ -0,0 +1,66 @@ +<%= header %> + + +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/31-eventtiming/07-inp-after-page-load.js b/tests/page-templates/31-eventtiming/07-inp-after-page-load.js new file mode 100644 index 000000000..23f02b6ab --- /dev/null +++ b/tests/page-templates/31-eventtiming/07-inp-after-page-load.js @@ -0,0 +1,70 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["PerformanceEventTiming", "POs", "curStartTime", "generateEvent"]); + +describe("e2e/31-eventtiming/07-inp-after-page-load", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a beacon", function() { + assert.isTrue(tf.fired_onbeacon); + }); + + describe("Page Load beacon", function() { + it("Should not have included Interaction to Next Paint (et.inp) on the Page Load beacon", function() { + assert.isUndefined(tf.beacons[0]["et.inp"]); + }); + + it("Should not have included Interaction to Next Paint target (et.inp.e) on the Page Load beacon", function() { + assert.isUndefined(tf.beacons[0]["et.inp.e"]); + }); + + it("Should not have included Interaction to Next Paint timestamp (et.inp.t) on the Page Load beacon", function() { + assert.isUndefined(tf.beacons[0]["et.inp.t"]); + }); + + it("Should have included Incremental Interaction to Next Paint (et.inp.inc) on the Page Load beacon", function() { + assert.equal(parseInt(tf.beacons[0]["et.inp.inc"], 10), 100); + }); + + it("Should have included Incremental Interaction to Next Paint target (et.inp.inc.e) on the Page Load beacon", function() { + assert.equal(tf.beacons[0]["et.inp.inc.e"], "span#interaction-target"); + }); + + it("Should have included Incremental Interaction to Next Paint timestamp (et.inp.inc.t) on the Page Load beacon", function() { + assert.operator(parseInt(tf.beacons[0]["et.inp.inc.t"], 10), ">=", 0); + }); + + it("Should have included the EventTiming timeline (et.e) on the Page Load beacon", function() { + assert.operator(tf.beacons[0]["et.e"].length, ">", 0); + }); + }); + + describe("Unload beacon", function() { + it("Should have included Interaction to Next Paint (et.inp) on the Unload beacon", function() { + assert.equal(parseInt(tf.beacons[1]["et.inp"], 10), 1000); + }); + + it("Should have included Interaction to Next Paint target (et.inp.e) on the Unload beacon", function() { + assert.equal(tf.beacons[1]["et.inp.e"], "span#interaction-target"); + }); + + it("Should have included Interaction to Next Paint timestamp (et.inp.t) on the Unload beacon", function() { + assert.operator(parseInt(tf.beacons[1]["et.inp.t"], 10), ">=", 0); + }); + + it("Should have included Incremental Interaction to Next Paint (et.inp.inc) on the Unload beacon", function() { + assert.equal(parseInt(tf.beacons[1]["et.inp.inc"], 10), 1000); + }); + + it("Should have included Incremental Interaction to Next Paint target (et.inp.inc.e) on the Unload beacon", function() { + assert.equal(tf.beacons[1]["et.inp.inc.e"], "span#interaction-target"); + }); + + it("Should have included Incremental Interaction to Next Paint timestamp (et.inp.inc.t) on the Unload beacon", function() { + assert.operator(parseInt(tf.beacons[1]["et.inp.inc.t"], 10), ">=", 0); + }); + }); +}); diff --git a/tests/page-templates/31-eventtiming/08-et-conditional-target.html b/tests/page-templates/31-eventtiming/08-et-conditional-target.html new file mode 100644 index 000000000..e08787c30 --- /dev/null +++ b/tests/page-templates/31-eventtiming/08-et-conditional-target.html @@ -0,0 +1,61 @@ +<%= header %> + +<%= boomerangSnippet %> + + + +<%= footer %> \ No newline at end of file diff --git a/tests/page-templates/31-eventtiming/08-et-conditional-target.js b/tests/page-templates/31-eventtiming/08-et-conditional-target.js new file mode 100644 index 000000000..3e91f084a --- /dev/null +++ b/tests/page-templates/31-eventtiming/08-et-conditional-target.js @@ -0,0 +1,19 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["PerformanceEventTiming", "testSelector", "POs"]); + +describe("e2e/31-eventtiming/08-et-conditional-target", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + // target information on event that has target + it("Should have included target information for the event", function() { + assert.include(tf.beacons[0]["et.e"], "~(c~0~d~'1e~n~0~p~'1e~s~'rs~t~'div.test)"); + }); + // no empty target information should be sent + it("Should not have included empty target information", function() { + assert.include(tf.beacons[0]["et.e"], "~(c~0~d~'1e~n~3~p~'1e~s~'1jk)~(c~0~d~'28~n~1~p~'2s~s~'2bc))"); + }); +}); diff --git a/tests/page-templates/31-eventtiming/08-inp-api.html b/tests/page-templates/31-eventtiming/08-inp-api.html new file mode 100644 index 000000000..289629fc8 --- /dev/null +++ b/tests/page-templates/31-eventtiming/08-inp-api.html @@ -0,0 +1,61 @@ +<%= header %> + + +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/31-eventtiming/08-inp-api.js b/tests/page-templates/31-eventtiming/08-inp-api.js new file mode 100644 index 000000000..8615c49a5 --- /dev/null +++ b/tests/page-templates/31-eventtiming/08-inp-api.js @@ -0,0 +1,50 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["PerformanceEventTiming", "POs", "curStartTime", "generateEvent"]); + +describe("e2e/31-eventtiming/07-inp-after-page-load", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent a beacon", function() { + assert.isTrue(tf.fired_onbeacon); + }); + + describe("Page Load beacon", function() { + it("Should not have included Interaction to Next Paint (et.inp) on the Page Load beacon", function() { + assert.isUndefined(tf.beacons[0]["et.inp"]); + }); + + it("Should not have included Interaction to Next Paint target (et.inp.e) on the Page Load beacon", function() { + assert.isUndefined(tf.beacons[0]["et.inp.e"]); + }); + + it("Should not have included Interaction to Next Paint timestamp (et.inp.t) on the Page Load beacon", function() { + assert.isUndefined(tf.beacons[0]["et.inp.t"]); + }); + + it("Should have included Incremental Interaction to Next Paint (et.inp.inc) on the Page Load beacon", function() { + assert.equal(parseInt(tf.beacons[0]["et.inp.inc"], 10), 100); + }); + + it("Should have included Incremental Interaction to Next Paint target (et.inp.inc.e) on the Page Load beacon", function() { + assert.equal(tf.beacons[0]["et.inp.inc.e"], "span#interaction-target"); + }); + + it("Should have included Incremental Interaction to Next Paint timestamp (et.inp.inc.t) on the Page Load beacon", function() { + assert.operator(parseInt(tf.beacons[0]["et.inp.inc.t"], 10), ">=", 0); + }); + + it("Should have included the EventTiming timeline (et.e) on the Page Load beacon", function() { + assert.operator(tf.beacons[0]["et.e"].length, ">", 0); + }); + }); + + describe("After Page Load Beacon", function() { + it("Should have BOOMR.plugins.EventTiming.metrics.interactionToNextPaint() return 1000", function() { + assert.equal(BOOMR.plugins.EventTiming.metrics.interactionToNextPaint(), 1000); + }); + }); +}); diff --git a/tests/page-templates/31-eventtiming/tests.json5 b/tests/page-templates/31-eventtiming/tests.json5 new file mode 100644 index 000000000..56782bf1b --- /dev/null +++ b/tests/page-templates/31-eventtiming/tests.json5 @@ -0,0 +1,5 @@ +{ + all: { + requires: ["eventtiming"] + } +} diff --git a/tests/page-templates/32-autoxhr-spa/01-click-xhr-routechange-img.html b/tests/page-templates/32-autoxhr-spa/01-click-xhr-routechange-img.html new file mode 100644 index 000000000..05b5556e6 --- /dev/null +++ b/tests/page-templates/32-autoxhr-spa/01-click-xhr-routechange-img.html @@ -0,0 +1,43 @@ +<%= header %> +<%= boomerangScript %> + + + + +<%= footer %> diff --git a/tests/page-templates/32-autoxhr-spa/01-click-xhr-routechange-img.js b/tests/page-templates/32-autoxhr-spa/01-click-xhr-routechange-img.js new file mode 100644 index 000000000..68914802a --- /dev/null +++ b/tests/page-templates/32-autoxhr-spa/01-click-xhr-routechange-img.js @@ -0,0 +1,61 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,describe,it */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["eventFired", "i"]); + +describe("e2e/32-autoxhr-spa/01-click-xhr-routechange-img.js", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent 2 beacons (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 2); + }, + this.skip.bind(this)); + }); + + describe("Beacon 1", function() { + it("Should have http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + }); + + describe("Beacon 2", function() { + it("Should have http.initiator = spa", function() { + assert.equal(tf.beacons[1]["http.initiator"], "spa"); + }); + + it("Should have URL with #newstate", function() { + assert.include(tf.beacons[1].u, "#newstate"); + }); + + it("Should have rt.tstart around the time of the route change", function() { + assert.closeTo(tf.beacons[1]["rt.tstart"], t.routeChangeTimes[0], 25); + }); + + it("Should have Page Load Time ~2010ms", function() { + assert.closeTo(tf.beacons[1].t_done, t.imgTimes.img1.duration + 10, 100); + }); + + it("Should have Page Load Time >= 2010ms", function() { + assert.operator(tf.beacons[1].t_done, ">=", 2000 + 10); + }); + + it("Should have Back End Time = 0ms", function() { + assert.equal(tf.beacons[1].t_resp, 0); + }); + + it("Should have Front End Time ~2010ms", function() { + assert.closeTo(tf.beacons[1].t_page, t.imgTimes.img1.duration + 10, 100); + }); + }); +}); diff --git a/tests/page-templates/32-autoxhr-spa/02-click-multiple-xhr-routechange-img.html b/tests/page-templates/32-autoxhr-spa/02-click-multiple-xhr-routechange-img.html new file mode 100644 index 000000000..4dd529f35 --- /dev/null +++ b/tests/page-templates/32-autoxhr-spa/02-click-multiple-xhr-routechange-img.html @@ -0,0 +1,46 @@ +<%= header %> +<%= boomerangScript %> + + + + +<%= footer %> diff --git a/tests/page-templates/32-autoxhr-spa/02-click-multiple-xhr-routechange-img.js b/tests/page-templates/32-autoxhr-spa/02-click-multiple-xhr-routechange-img.js new file mode 100644 index 000000000..9eb4af751 --- /dev/null +++ b/tests/page-templates/32-autoxhr-spa/02-click-multiple-xhr-routechange-img.js @@ -0,0 +1,91 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,describe,it */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["eventFired", "i"]); + +describe("e2e/32-autoxhr-spa/02-click-multiple-xhr-routechange-img.js", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent 3 beacons (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 3); + }, + this.skip.bind(this)); + }); + + describe("Beacon 1", function() { + it("Should have http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + }); + + describe("Beacon 2", function() { + it("Should have http.initiator = xhr", function() { + assert.equal(tf.beacons[1]["http.initiator"], "xhr"); + }); + + it("Should have URL with xhr1", function() { + assert.include(tf.beacons[1].u, "xhr1"); + }); + + it("Should have rt.tstart around the time of the XHR", function() { + assert.closeTo(tf.beacons[1]["rt.tstart"], t.xhrTimes.xhr1.start, 5); + }); + + it("Should have Page Load Time ~4000ms", function() { + assert.closeTo(tf.beacons[1].t_done, t.xhrTimes.xhr1.duration + t.xhrTimes.xhr2.duration, 100); + }); + + it("Should have Page Load Time >= 4000ms", function() { + assert.operator(tf.beacons[1].t_done, ">=", 2000); + }); + + it("Should have Back End Time ~2000ms", function() { + assert.closeTo(tf.beacons[1].t_resp, t.xhrTimes.xhr1.duration, 100); + }); + + it("Should have Front End Time ~2000ms", function() { + assert.closeTo(tf.beacons[1].t_page, t.xhrTimes.xhr2.duration, 100); + }); + }); + + describe("Beacon 3", function() { + it("Should have http.initiator = spa", function() { + assert.equal(tf.beacons[2]["http.initiator"], "spa"); + }); + + it("Should have URL with #newstate", function() { + assert.include(tf.beacons[2].u, "#newstate"); + }); + + it("Should have rt.tstart around the time of the route change", function() { + assert.closeTo(tf.beacons[2]["rt.tstart"], t.routeChangeTimes[0], 25); + }); + + it("Should have Page Load Time ~2010ms", function() { + assert.closeTo(tf.beacons[2].t_done, t.imgTimes.img1.duration + 10, 100); + }); + + it("Should have Page Load Time >= 2010ms", function() { + assert.operator(tf.beacons[2].t_done, ">=", 2010); + }); + + it("Should have Back End Time = 0ms", function() { + assert.equal(tf.beacons[2].t_resp, 0); + }); + + it("Should have Front End Time ~2010ms", function() { + assert.closeTo(tf.beacons[2].t_page, t.imgTimes.img1.duration + 10, 100); + }); + }); +}); diff --git a/tests/page-templates/32-autoxhr-spa/03-click-routechange-img.html b/tests/page-templates/32-autoxhr-spa/03-click-routechange-img.html new file mode 100644 index 000000000..6ad0bac5a --- /dev/null +++ b/tests/page-templates/32-autoxhr-spa/03-click-routechange-img.html @@ -0,0 +1,40 @@ +<%= header %> +<%= boomerangScript %> + + + + +<%= footer %> diff --git a/tests/page-templates/32-autoxhr-spa/03-click-routechange-img.js b/tests/page-templates/32-autoxhr-spa/03-click-routechange-img.js new file mode 100644 index 000000000..18c01dd2f --- /dev/null +++ b/tests/page-templates/32-autoxhr-spa/03-click-routechange-img.js @@ -0,0 +1,61 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,describe,it */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["eventFired", "i"]); + +describe("e2e/32-autoxhr-spa/03-click-routechange-img.js", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent 2 beacons (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 2); + }, + this.skip.bind(this)); + }); + + describe("Beacon 1", function() { + it("Should have http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + }); + + describe("Beacon 2", function() { + it("Should have http.initiator = spa", function() { + assert.equal(tf.beacons[1]["http.initiator"], "spa"); + }); + + it("Should have URL with #newstate", function() { + assert.include(tf.beacons[1].u, "#newstate"); + }); + + it("Should have rt.tstart around the time of the route change", function() { + assert.closeTo(tf.beacons[1]["rt.tstart"], t.routeChangeTimes[0], 25); + }); + + it("Should have Page Load Time ~2050ms", function() { + assert.closeTo(tf.beacons[1].t_done, t.imgTimes.img1.duration + 50, 100); + }); + + it("Should have Page Load Time >= 2050ms", function() { + assert.operator(tf.beacons[1].t_done, ">=", 2050); + }); + + it("Should have Back End Time = 0ms", function() { + assert.equal(tf.beacons[1].t_resp, 0); + }); + + it("Should have Front End Time ~2050ms", function() { + assert.closeTo(tf.beacons[1].t_page, t.imgTimes.img1.duration + 50, 100); + }); + }); +}); diff --git a/tests/page-templates/32-autoxhr-spa/04-click-delay-routechange-img.html b/tests/page-templates/32-autoxhr-spa/04-click-delay-routechange-img.html new file mode 100644 index 000000000..9e6ab9801 --- /dev/null +++ b/tests/page-templates/32-autoxhr-spa/04-click-delay-routechange-img.html @@ -0,0 +1,40 @@ +<%= header %> +<%= boomerangScript %> + + + + +<%= footer %> diff --git a/tests/page-templates/32-autoxhr-spa/04-click-delay-routechange-img.js b/tests/page-templates/32-autoxhr-spa/04-click-delay-routechange-img.js new file mode 100644 index 000000000..12a9964dc --- /dev/null +++ b/tests/page-templates/32-autoxhr-spa/04-click-delay-routechange-img.js @@ -0,0 +1,61 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,describe,it */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["eventFired", "i"]); + +describe("e2e/32-autoxhr-spa/04-click-delay-routechange-img.js", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent 2 beacons (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 2); + }, + this.skip.bind(this)); + }); + + describe("Beacon 1", function() { + it("Should have http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + }); + + describe("Beacon 2", function() { + it("Should have http.initiator = spa", function() { + assert.equal(tf.beacons[1]["http.initiator"], "spa"); + }); + + it("Should have URL with #newstate", function() { + assert.include(tf.beacons[1].u, "#newstate"); + }); + + it("Should have rt.tstart around the time of the route change", function() { + assert.closeTo(tf.beacons[1]["rt.tstart"], t.routeChangeTimes[0], 25); + }); + + it("Should have Page Load Time ~2050ms", function() { + assert.closeTo(tf.beacons[1].t_done, t.imgTimes.img1.duration + 50, 100); + }); + + it("Should have Page Load Time >= 2050ms", function() { + assert.operator(tf.beacons[1].t_done, ">=", 2050); + }); + + it("Should have Back End Time = 0ms", function() { + assert.equal(tf.beacons[1].t_resp, 0); + }); + + it("Should have Front End Time ~2050ms", function() { + assert.closeTo(tf.beacons[1].t_page, t.imgTimes.img1.duration + 50, 100); + }); + }); +}); diff --git a/tests/page-templates/32-autoxhr-spa/05-click-xhr-img.html b/tests/page-templates/32-autoxhr-spa/05-click-xhr-img.html new file mode 100644 index 000000000..c4a001648 --- /dev/null +++ b/tests/page-templates/32-autoxhr-spa/05-click-xhr-img.html @@ -0,0 +1,37 @@ +<%= header %> +<%= boomerangScript %> + + + + +<%= footer %> diff --git a/tests/page-templates/32-autoxhr-spa/05-click-xhr-img.js b/tests/page-templates/32-autoxhr-spa/05-click-xhr-img.js new file mode 100644 index 000000000..2a13d1205 --- /dev/null +++ b/tests/page-templates/32-autoxhr-spa/05-click-xhr-img.js @@ -0,0 +1,61 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,describe,it */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["eventFired"]); + +describe("e2e/32-autoxhr-spa/05-click-xhr-img.js", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent 2 beacons (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 2); + }, + this.skip.bind(this)); + }); + + describe("Beacon 1", function() { + it("Should have http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + }); + + describe("Beacon 2", function() { + it("Should have http.initiator = xhr", function() { + assert.equal(tf.beacons[1]["http.initiator"], "xhr"); + }); + + it("Should have URL with xhr1", function() { + assert.include(tf.beacons[1].u, "xhr1"); + }); + + it("Should have rt.tstart around the time of the XHR", function() { + assert.closeTo(tf.beacons[1]["rt.tstart"], t.xhrTimes.xhr1.start, 10); + }); + + it("Should have Page Load Time ~4000ms", function() { + assert.closeTo(tf.beacons[1].t_done, t.xhrTimes.xhr1.duration + t.imgTimes.img1.duration, 100); + }); + + it("Should have Page Load Time >= 4000ms", function() { + assert.operator(tf.beacons[1].t_done, ">=", 4000); + }); + + it("Should have Back End Time ~2000ms", function() { + assert.closeTo(tf.beacons[1].t_resp, t.xhrTimes.xhr1.duration, 100); + }); + + it("Should have Front End Time ~2000ms", function() { + assert.closeTo(tf.beacons[1].t_page, t.imgTimes.img1.duration, 100); + }); + }); +}); diff --git a/tests/page-templates/32-autoxhr-spa/06-click-click-xhr-img.html b/tests/page-templates/32-autoxhr-spa/06-click-click-xhr-img.html new file mode 100644 index 000000000..eec94ee88 --- /dev/null +++ b/tests/page-templates/32-autoxhr-spa/06-click-click-xhr-img.html @@ -0,0 +1,43 @@ +<%= header %> +<%= boomerangScript %> + + + + +<%= footer %> diff --git a/tests/page-templates/32-autoxhr-spa/06-click-click-xhr-img.js b/tests/page-templates/32-autoxhr-spa/06-click-click-xhr-img.js new file mode 100644 index 000000000..70b4eda79 --- /dev/null +++ b/tests/page-templates/32-autoxhr-spa/06-click-click-xhr-img.js @@ -0,0 +1,61 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,describe,it */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["eventFired"]); + +describe("e2e/32-autoxhr-spa/06-click-click-xhr-img.js", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent 2 beacons (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 2); + }, + this.skip.bind(this)); + }); + + describe("Beacon 1", function() { + it("Should have http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + }); + + describe("Beacon 2", function() { + it("Should have http.initiator = xhr", function() { + assert.equal(tf.beacons[1]["http.initiator"], "xhr"); + }); + + it("Should have URL with xhr1", function() { + assert.include(tf.beacons[1].u, "xhr1"); + }); + + it("Should have rt.tstart around the time of the XHR", function() { + assert.closeTo(tf.beacons[1]["rt.tstart"], t.xhrTimes.xhr1.start, 5); + }); + + it("Should have Page Load Time ~4000ms", function() { + assert.closeTo(tf.beacons[1].t_done, t.xhrTimes.xhr1.duration + t.imgTimes.img1.duration, 100); + }); + + it("Should have Page Load Time >= 4000ms", function() { + assert.operator(tf.beacons[1].t_done, ">=", 4000); + }); + + it("Should have Back End Time ~2000ms", function() { + assert.closeTo(tf.beacons[1].t_resp, t.xhrTimes.xhr1.duration, 100); + }); + + it("Should have Front End Time ~2000ms", function() { + assert.closeTo(tf.beacons[1].t_page, t.imgTimes.img1.duration, 100); + }); + }); +}); diff --git a/tests/page-templates/32-autoxhr-spa/07-click-xhr-no-dom.html b/tests/page-templates/32-autoxhr-spa/07-click-xhr-no-dom.html new file mode 100644 index 000000000..254231554 --- /dev/null +++ b/tests/page-templates/32-autoxhr-spa/07-click-xhr-no-dom.html @@ -0,0 +1,37 @@ +<%= header %> +<%= boomerangScript %> + + + + +<%= footer %> diff --git a/tests/page-templates/32-autoxhr-spa/07-click-xhr-no-dom.js b/tests/page-templates/32-autoxhr-spa/07-click-xhr-no-dom.js new file mode 100644 index 000000000..15c18e0bb --- /dev/null +++ b/tests/page-templates/32-autoxhr-spa/07-click-xhr-no-dom.js @@ -0,0 +1,31 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,describe,it */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["eventFired"]); + +describe("e2e/32-autoxhr-spa/07-click-xhr-no-dom.js", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent 1 beacon (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 1); + }, + this.skip.bind(this)); + }); + + describe("Beacon 1", function() { + it("Should have http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + }); +}); diff --git a/tests/page-templates/32-autoxhr-spa/08-click-routechange-no-dom.html b/tests/page-templates/32-autoxhr-spa/08-click-routechange-no-dom.html new file mode 100644 index 000000000..7cba8f3cc --- /dev/null +++ b/tests/page-templates/32-autoxhr-spa/08-click-routechange-no-dom.html @@ -0,0 +1,35 @@ +<%= header %> +<%= boomerangScript %> + + + + +<%= footer %> diff --git a/tests/page-templates/32-autoxhr-spa/08-click-routechange-no-dom.js b/tests/page-templates/32-autoxhr-spa/08-click-routechange-no-dom.js new file mode 100644 index 000000000..0fe199cc7 --- /dev/null +++ b/tests/page-templates/32-autoxhr-spa/08-click-routechange-no-dom.js @@ -0,0 +1,57 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,describe,it */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["eventFired", "i"]); + +describe("e2e/32-autoxhr-spa/08-click-routechange-no-dom.js", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent 2 beacons (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 2); + }, + this.skip.bind(this)); + }); + + describe("Beacon 1", function() { + it("Should have http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + }); + + describe("Beacon 2", function() { + it("Should have http.initiator = spa", function() { + assert.equal(tf.beacons[1]["http.initiator"], "spa"); + }); + + it("Should have URL with #newstate", function() { + assert.include(tf.beacons[1].u, "#newstate"); + }); + + it("Should have rt.tstart around the time of the route change", function() { + assert.closeTo(tf.beacons[1]["rt.tstart"], t.routeChangeTimes[0], 25); + }); + + it("Should have Page Load Time = 1ms", function() { + assert.equal(tf.beacons[1].t_done, 1); + }); + + it("Should have Back End Time = 0ms", function() { + assert.equal(tf.beacons[1].t_resp, 0); + }); + + it("Should have Front End Time = 1ms", function() { + assert.equal(tf.beacons[1].t_page, 1); + }); + }); +}); diff --git a/tests/page-templates/32-autoxhr-spa/09-click-no-dom.html b/tests/page-templates/32-autoxhr-spa/09-click-no-dom.html new file mode 100644 index 000000000..f1151ea4e --- /dev/null +++ b/tests/page-templates/32-autoxhr-spa/09-click-no-dom.html @@ -0,0 +1,31 @@ +<%= header %> +<%= boomerangScript %> + + + + +<%= footer %> diff --git a/tests/page-templates/32-autoxhr-spa/09-click-no-dom.js b/tests/page-templates/32-autoxhr-spa/09-click-no-dom.js new file mode 100644 index 000000000..c2c47f837 --- /dev/null +++ b/tests/page-templates/32-autoxhr-spa/09-click-no-dom.js @@ -0,0 +1,31 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,describe,it */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["eventFired"]); + +describe("e2e/32-autoxhr-spa/09-click-no-dom.js", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent 1 beacon (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 1); + }, + this.skip.bind(this)); + }); + + describe("Beacon 1", function() { + it("Should have http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + }); +}); diff --git a/tests/page-templates/32-autoxhr-spa/10-click-img.html b/tests/page-templates/32-autoxhr-spa/10-click-img.html new file mode 100644 index 000000000..2f43696fd --- /dev/null +++ b/tests/page-templates/32-autoxhr-spa/10-click-img.html @@ -0,0 +1,38 @@ +<%= header %> +<%= boomerangScript %> + + + + +<%= footer %> diff --git a/tests/page-templates/32-autoxhr-spa/10-click-img.js b/tests/page-templates/32-autoxhr-spa/10-click-img.js new file mode 100644 index 000000000..13046f0d3 --- /dev/null +++ b/tests/page-templates/32-autoxhr-spa/10-click-img.js @@ -0,0 +1,31 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,describe,it */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["eventFired"]); + +describe("e2e/32-autoxhr-spa/10-click-img.js", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent 1 beacon (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 1); + }, + this.skip.bind(this)); + }); + + describe("Beacon 1", function() { + it("Should have http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + }); +}); diff --git a/tests/page-templates/32-autoxhr-spa/11-xhr-img.html b/tests/page-templates/32-autoxhr-spa/11-xhr-img.html new file mode 100644 index 000000000..4595b3c79 --- /dev/null +++ b/tests/page-templates/32-autoxhr-spa/11-xhr-img.html @@ -0,0 +1,31 @@ +<%= header %> +<%= boomerangScript %> + + + + +<%= footer %> diff --git a/tests/page-templates/32-autoxhr-spa/11-xhr-img.js b/tests/page-templates/32-autoxhr-spa/11-xhr-img.js new file mode 100644 index 000000000..9cdafe535 --- /dev/null +++ b/tests/page-templates/32-autoxhr-spa/11-xhr-img.js @@ -0,0 +1,58 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,describe,it */ + +describe("e2e/32-autoxhr-spa/11-xhr-img.js", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent 2 beacons (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 2); + }, + this.skip.bind(this)); + }); + + describe("Beacon 1", function() { + it("Should have http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + }); + + describe("Beacon 2", function() { + it("Should have http.initiator = xhr", function() { + assert.equal(tf.beacons[1]["http.initiator"], "xhr"); + }); + + it("Should have URL with xhr1", function() { + assert.include(tf.beacons[1].u, "xhr1"); + }); + + it("Should have rt.tstart around the time of the XHR", function() { + assert.closeTo(tf.beacons[1]["rt.tstart"], t.xhrTimes.xhr1.start, 5); + }); + + it("Should have Page Load Time ~4000ms", function() { + assert.closeTo(tf.beacons[1].t_done, t.xhrTimes.xhr1.duration + t.imgTimes.img1.duration, 100); + }); + + it("Should have Page Load Time >= 4000ms", function() { + assert.operator(tf.beacons[1].t_done, ">=", 4000); + }); + + it("Should have Back End Time ~2000ms", function() { + assert.closeTo(tf.beacons[1].t_resp, t.xhrTimes.xhr1.duration, 100); + }); + + it("Should have Front End Time ~2000ms", function() { + assert.closeTo(tf.beacons[1].t_page, t.imgTimes.img1.duration, 100); + }); + }); +}); diff --git a/tests/page-templates/32-autoxhr-spa/12-click-img-xhr.html b/tests/page-templates/32-autoxhr-spa/12-click-img-xhr.html new file mode 100644 index 000000000..1a4a96640 --- /dev/null +++ b/tests/page-templates/32-autoxhr-spa/12-click-img-xhr.html @@ -0,0 +1,44 @@ +<%= header %> +<%= boomerangScript %> + + + + +<%= footer %> diff --git a/tests/page-templates/32-autoxhr-spa/12-click-img-xhr.js b/tests/page-templates/32-autoxhr-spa/12-click-img-xhr.js new file mode 100644 index 000000000..58dda81c4 --- /dev/null +++ b/tests/page-templates/32-autoxhr-spa/12-click-img-xhr.js @@ -0,0 +1,31 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,describe,it */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["eventFired"]); + +describe("e2e/32-autoxhr-spa/12-click-img-xhr.js", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent 1 beacons (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 1); + }, + this.skip.bind(this)); + }); + + describe("Beacon 1", function() { + it("Should have http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + }); +}); diff --git a/tests/page-templates/32-autoxhr-spa/13-routechange-no-dom.html b/tests/page-templates/32-autoxhr-spa/13-routechange-no-dom.html new file mode 100644 index 000000000..2efcc1e4c --- /dev/null +++ b/tests/page-templates/32-autoxhr-spa/13-routechange-no-dom.html @@ -0,0 +1,31 @@ +<%= header %> +<%= boomerangScript %> + + + + +<%= footer %> diff --git a/tests/page-templates/32-autoxhr-spa/13-routechange-no-dom.js b/tests/page-templates/32-autoxhr-spa/13-routechange-no-dom.js new file mode 100644 index 000000000..8f957a112 --- /dev/null +++ b/tests/page-templates/32-autoxhr-spa/13-routechange-no-dom.js @@ -0,0 +1,57 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,describe,it */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["i"]); + +describe("e2e/32-autoxhr-spa/13-routechange-no-dom.js", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent 2 beacons (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 2); + }, + this.skip.bind(this)); + }); + + describe("Beacon 1", function() { + it("Should have http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + }); + + describe("Beacon 2", function() { + it("Should have http.initiator = spa", function() { + assert.equal(tf.beacons[1]["http.initiator"], "spa"); + }); + + it("Should have URL with #newstate", function() { + assert.include(tf.beacons[1].u, "#newstate"); + }); + + it("Should have rt.tstart around the time of the route change", function() { + assert.closeTo(tf.beacons[1]["rt.tstart"], t.routeChangeTimes[0], 25); + }); + + it("Should have Page Load Time = 1ms", function() { + assert.equal(tf.beacons[1].t_done, 1); + }); + + it("Should have Back End Time = 0ms", function() { + assert.equal(tf.beacons[1].t_resp, 0); + }); + + it("Should have Front End Time = 1ms", function() { + assert.equal(tf.beacons[1].t_page, 1); + }); + }); +}); diff --git a/tests/page-templates/32-autoxhr-spa/14-xhr-no-dom.html b/tests/page-templates/32-autoxhr-spa/14-xhr-no-dom.html new file mode 100644 index 000000000..c9ea0baed --- /dev/null +++ b/tests/page-templates/32-autoxhr-spa/14-xhr-no-dom.html @@ -0,0 +1,31 @@ +<%= header %> +<%= boomerangScript %> + + + + +<%= footer %> diff --git a/tests/page-templates/32-autoxhr-spa/14-xhr-no-dom.js b/tests/page-templates/32-autoxhr-spa/14-xhr-no-dom.js new file mode 100644 index 000000000..a051162f6 --- /dev/null +++ b/tests/page-templates/32-autoxhr-spa/14-xhr-no-dom.js @@ -0,0 +1,28 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,describe,it */ + +describe("e2e/32-autoxhr-spa/14-xhr-no-dom.js", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent 1 beacon (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 1); + }, + this.skip.bind(this)); + }); + + describe("Beacon 1", function() { + it("Should have http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + }); +}); diff --git a/tests/page-templates/32-autoxhr-spa/15-click-xhr-routechange-no-dom.html b/tests/page-templates/32-autoxhr-spa/15-click-xhr-routechange-no-dom.html new file mode 100644 index 000000000..f263365df --- /dev/null +++ b/tests/page-templates/32-autoxhr-spa/15-click-xhr-routechange-no-dom.html @@ -0,0 +1,38 @@ +<%= header %> +<%= boomerangScript %> + + + + +<%= footer %> diff --git a/tests/page-templates/32-autoxhr-spa/15-click-xhr-routechange-no-dom.js b/tests/page-templates/32-autoxhr-spa/15-click-xhr-routechange-no-dom.js new file mode 100644 index 000000000..1aacc709f --- /dev/null +++ b/tests/page-templates/32-autoxhr-spa/15-click-xhr-routechange-no-dom.js @@ -0,0 +1,57 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,describe,it */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["eventFired", "i"]); + +describe("e2e/32-autoxhr-spa/15-click-xhr-routechange-no-dom.js", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent 2 beacons (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 2); + }, + this.skip.bind(this)); + }); + + describe("Beacon 1", function() { + it("Should have http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + }); + + describe("Beacon 2", function() { + it("Should have http.initiator = spa", function() { + assert.equal(tf.beacons[1]["http.initiator"], "spa"); + }); + + it("Should have URL with #newstate", function() { + assert.include(tf.beacons[1].u, "#newstate"); + }); + + it("Should have rt.tstart around the time of the route change", function() { + assert.closeTo(tf.beacons[1]["rt.tstart"], t.routeChangeTimes[0], 25); + }); + + it("Should have Page Load Time = 1ms", function() { + assert.equal(tf.beacons[1].t_done, 1); + }); + + it("Should have Back End Time = 0ms", function() { + assert.equal(tf.beacons[1].t_resp, 0); + }); + + it("Should have Front End Time = 1ms", function() { + assert.equal(tf.beacons[1].t_page, 1); + }); + }); +}); diff --git a/tests/page-templates/32-autoxhr-spa/16-click-routechange-xhr-img.html b/tests/page-templates/32-autoxhr-spa/16-click-routechange-xhr-img.html new file mode 100644 index 000000000..614b56162 --- /dev/null +++ b/tests/page-templates/32-autoxhr-spa/16-click-routechange-xhr-img.html @@ -0,0 +1,46 @@ +<%= header %> +<%= boomerangScript %> + + + + +<%= footer %> diff --git a/tests/page-templates/32-autoxhr-spa/16-click-routechange-xhr-img.js b/tests/page-templates/32-autoxhr-spa/16-click-routechange-xhr-img.js new file mode 100644 index 000000000..4206346cb --- /dev/null +++ b/tests/page-templates/32-autoxhr-spa/16-click-routechange-xhr-img.js @@ -0,0 +1,61 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,describe,it */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["eventFired", "i"]); + +describe("e2e/32-autoxhr-spa/16-click-routechange-xhr-img.js", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent 2 beacons (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 2); + }, + this.skip.bind(this)); + }); + + describe("Beacon 1", function() { + it("Should have http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + }); + + describe("Beacon 2", function() { + it("Should have http.initiator = spa", function() { + assert.equal(tf.beacons[1]["http.initiator"], "spa"); + }); + + it("Should have URL with #newstate", function() { + assert.include(tf.beacons[1].u, "#newstate"); + }); + + it("Should have rt.tstart around the time of the route change", function() { + assert.closeTo(tf.beacons[1]["rt.tstart"], t.routeChangeTimes[0], 25); + }); + + it("Should have Page Load Time ~4040ms", function() { + assert.closeTo(tf.beacons[1].t_done, t.xhrTimes.xhr1.duration + t.imgTimes.img1.duration + 30 + 10, 100); + }); + + it("Should have Page Load Time >= 4040ms", function() { + assert.operator(tf.beacons[1].t_done, ">=", 4040); + }); + + it("Should have Back End Time ~2000ms", function() { + assert.closeTo(tf.beacons[1].t_resp, t.xhrTimes.xhr1.duration, 100); + }); + + it("Should have Front End Time ~2040ms", function() { + assert.closeTo(tf.beacons[1].t_page, t.imgTimes.img1.duration + 30 + 10, 100); + }); + }); +}); diff --git a/tests/page-templates/32-autoxhr-spa/17-click-routechange-xhr-no-dom.html b/tests/page-templates/32-autoxhr-spa/17-click-routechange-xhr-no-dom.html new file mode 100644 index 000000000..f81c73968 --- /dev/null +++ b/tests/page-templates/32-autoxhr-spa/17-click-routechange-xhr-no-dom.html @@ -0,0 +1,40 @@ +<%= header %> +<%= boomerangScript %> + + + + +<%= footer %> diff --git a/tests/page-templates/32-autoxhr-spa/17-click-routechange-xhr-no-dom.js b/tests/page-templates/32-autoxhr-spa/17-click-routechange-xhr-no-dom.js new file mode 100644 index 000000000..f2aef335c --- /dev/null +++ b/tests/page-templates/32-autoxhr-spa/17-click-routechange-xhr-no-dom.js @@ -0,0 +1,61 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,describe,it */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["eventFired", "i"]); + +describe("e2e/32-autoxhr-spa/17-click-routechange-xhr-no-dom.js", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent 2 beacons (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 2); + }, + this.skip.bind(this)); + }); + + describe("Beacon 1", function() { + it("Should have http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + }); + + describe("Beacon 2", function() { + it("Should have http.initiator = spa", function() { + assert.equal(tf.beacons[1]["http.initiator"], "spa"); + }); + + it("Should have URL with #newstate", function() { + assert.include(tf.beacons[1].u, "#newstate"); + }); + + it("Should have rt.tstart around the time of the route change", function() { + assert.closeTo(tf.beacons[1]["rt.tstart"], t.routeChangeTimes[0], 25); + }); + + it("Should have Page Load Time ~2030ms", function() { + assert.closeTo(tf.beacons[1].t_done, t.xhrTimes.xhr1.duration + 30, 100); + }); + + it("Should have Page Load Time >= 2030ms", function() { + assert.operator(tf.beacons[1].t_done, ">=", 2030); + }); + + it("Should have Back End Time ~2000ms", function() { + assert.closeTo(tf.beacons[1].t_resp, t.xhrTimes.xhr1.duration, 100); + }); + + it("Should have Front End Time ~30ms", function() { + assert.closeTo(tf.beacons[1].t_page, 30, 50); + }); + }); +}); diff --git a/tests/page-templates/32-autoxhr-spa/18-xhr-img-click.html b/tests/page-templates/32-autoxhr-spa/18-xhr-img-click.html new file mode 100644 index 000000000..ed5cc8d0f --- /dev/null +++ b/tests/page-templates/32-autoxhr-spa/18-xhr-img-click.html @@ -0,0 +1,44 @@ +<%= header %> +<%= boomerangScript %> + + + + +<%= footer %> diff --git a/tests/page-templates/32-autoxhr-spa/18-xhr-img-click.js b/tests/page-templates/32-autoxhr-spa/18-xhr-img-click.js new file mode 100644 index 000000000..82beeaeb9 --- /dev/null +++ b/tests/page-templates/32-autoxhr-spa/18-xhr-img-click.js @@ -0,0 +1,61 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,describe,it */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["eventFired"]); + +describe("e2e/32-autoxhr-spa/18-xhr-img-click.js", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent 2 beacons (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 2); + }, + this.skip.bind(this)); + }); + + describe("Beacon 1", function() { + it("Should have http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + }); + + describe("Beacon 2", function() { + it("Should have http.initiator = xhr", function() { + assert.equal(tf.beacons[1]["http.initiator"], "xhr"); + }); + + it("Should have URL with xhr1", function() { + assert.include(tf.beacons[1].u, "xhr1"); + }); + + it("Should have rt.tstart around the time of the XHR", function() { + assert.closeTo(tf.beacons[1]["rt.tstart"], t.xhrTimes.xhr1.start, 5); + }); + + it("Should have Page Load Time ~4000ms", function() { + assert.closeTo(tf.beacons[1].t_done, t.xhrTimes.xhr1.duration + t.imgTimes.img1.duration, 100); + }); + + it("Should have Page Load Time >= 4000ms", function() { + assert.operator(tf.beacons[1].t_done, ">=", 4000); + }); + + it("Should have Back End Time ~2000ms", function() { + assert.closeTo(tf.beacons[1].t_resp, t.xhrTimes.xhr1.duration, 100); + }); + + it("Should have Front End Time ~2000ms", function() { + assert.closeTo(tf.beacons[1].t_page, t.imgTimes.img1.duration, 100); + }); + }); +}); diff --git a/tests/page-templates/32-autoxhr-spa/19-click-img-click-xhr-img.html b/tests/page-templates/32-autoxhr-spa/19-click-img-click-xhr-img.html new file mode 100644 index 000000000..7c75f4e19 --- /dev/null +++ b/tests/page-templates/32-autoxhr-spa/19-click-img-click-xhr-img.html @@ -0,0 +1,48 @@ +<%= header %> +<%= boomerangScript %> + + + + +<%= footer %> diff --git a/tests/page-templates/32-autoxhr-spa/19-click-img-click-xhr-img.js b/tests/page-templates/32-autoxhr-spa/19-click-img-click-xhr-img.js new file mode 100644 index 000000000..d61514297 --- /dev/null +++ b/tests/page-templates/32-autoxhr-spa/19-click-img-click-xhr-img.js @@ -0,0 +1,61 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,describe,it */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["eventFired"]); + +describe("e2e/32-autoxhr-spa/19-click-img-click-xhr-img.js", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent 2 beacons (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 2); + }, + this.skip.bind(this)); + }); + + describe("Beacon 1", function() { + it("Should have http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + }); + + describe("Beacon 2", function() { + it("Should have http.initiator = xhr", function() { + assert.equal(tf.beacons[1]["http.initiator"], "xhr"); + }); + + it("Should have URL with xhr1", function() { + assert.include(tf.beacons[1].u, "xhr1"); + }); + + it("Should have rt.tstart around the time of the XHR", function() { + assert.closeTo(tf.beacons[1]["rt.tstart"], t.xhrTimes.xhr1.start, 5); + }); + + it("Should have Page Load Time ~4000ms", function() { + assert.closeTo(tf.beacons[1].t_done, t.xhrTimes.xhr1.duration + t.imgTimes.img2.duration, 100); + }); + + it("Should have Page Load Time >= 4000ms", function() { + assert.operator(tf.beacons[1].t_done, ">=", 4000); + }); + + it("Should have Back End Time ~2000ms", function() { + assert.closeTo(tf.beacons[1].t_resp, t.xhrTimes.xhr1.duration, 100); + }); + + it("Should have Front End Time ~2000ms", function() { + assert.closeTo(tf.beacons[1].t_page, t.imgTimes.img2.duration, 100); + }); + }); +}); diff --git a/tests/page-templates/32-autoxhr-spa/20-xhr-img-routechange-img.html b/tests/page-templates/32-autoxhr-spa/20-xhr-img-routechange-img.html new file mode 100644 index 000000000..365f72f59 --- /dev/null +++ b/tests/page-templates/32-autoxhr-spa/20-xhr-img-routechange-img.html @@ -0,0 +1,45 @@ +<%= header %> +<%= boomerangScript %> + + + + +<%= footer %> diff --git a/tests/page-templates/32-autoxhr-spa/20-xhr-img-routechange-img.js b/tests/page-templates/32-autoxhr-spa/20-xhr-img-routechange-img.js new file mode 100644 index 000000000..e789f8528 --- /dev/null +++ b/tests/page-templates/32-autoxhr-spa/20-xhr-img-routechange-img.js @@ -0,0 +1,91 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,describe,it */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["i"]); + +describe("e2e/32-autoxhr-spa/20-xhr-img-routechange-img.js", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent 3 beacons (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 3); + }, + this.skip.bind(this)); + }); + + describe("Beacon 1", function() { + it("Should have http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + }); + + describe("Beacon 2", function() { + it("Should have http.initiator = xhr", function() { + assert.equal(tf.beacons[1]["http.initiator"], "xhr"); + }); + + it("Should have URL with xhr1", function() { + assert.include(tf.beacons[1].u, "xhr1"); + }); + + it("Should have rt.tstart around the time of the first XHR", function() { + assert.closeTo(tf.beacons[1]["rt.tstart"], t.xhrTimes.xhr1.start, 25); + }); + + it("Should have Page Load Time ~4030ms", function() { + assert.closeTo(tf.beacons[1].t_done, t.xhrTimes.xhr1.duration + t.imgTimes.img1.duration + 30, 100); + }); + + it("Should have Page Load Time >= 4030ms", function() { + assert.operator(tf.beacons[1].t_done, ">=", 4030); + }); + + it("Should have Back End Time ~2000ms", function() { + assert.closeTo(tf.beacons[1].t_resp, t.xhrTimes.xhr1.duration, 100); + }); + + it("Should have Front End Time ~2030ms", function() { + assert.closeTo(tf.beacons[1].t_page, t.imgTimes.img1.duration + 30, 100); + }); + }); + + describe("Beacon 3", function() { + it("Should have http.initiator = spa", function() { + assert.equal(tf.beacons[2]["http.initiator"], "spa"); + }); + + it("Should have URL with #newstate", function() { + assert.include(tf.beacons[2].u, "#newstate"); + }); + + it("Should have rt.tstart around the time of the route change", function() { + assert.closeTo(tf.beacons[2]["rt.tstart"], t.routeChangeTimes[0], 25); + }); + + it("Should have Page Load Time ~2030ms", function() { + assert.closeTo(tf.beacons[2].t_done, t.imgTimes.img2.duration + 30, 100); + }); + + it("Should have Page Load Time >= 2030ms", function() { + assert.operator(tf.beacons[2].t_done, ">=", 2030); + }); + + it("Should have Back End Time = 0ms", function() { + assert.equal(tf.beacons[2].t_resp, 0); + }); + + it("Should have Front End Time ~2030ms", function() { + assert.closeTo(tf.beacons[2].t_page, t.imgTimes.img2.duration + 30, 100); + }); + }); +}); diff --git a/tests/page-templates/32-autoxhr-spa/21-xhr-fetch-wrapp-reinit.html b/tests/page-templates/32-autoxhr-spa/21-xhr-fetch-wrapp-reinit.html new file mode 100644 index 000000000..bf7fbba54 --- /dev/null +++ b/tests/page-templates/32-autoxhr-spa/21-xhr-fetch-wrapp-reinit.html @@ -0,0 +1,42 @@ +<%= header %> +<%= boomerangScript %> + + + +<%= footer %> diff --git a/tests/page-templates/32-autoxhr-spa/21-xhr-fetch-wrapp-reinit.js b/tests/page-templates/32-autoxhr-spa/21-xhr-fetch-wrapp-reinit.js new file mode 100644 index 000000000..3d1df3aa8 --- /dev/null +++ b/tests/page-templates/32-autoxhr-spa/21-xhr-fetch-wrapp-reinit.js @@ -0,0 +1,33 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,describe,it */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["orig_XHR", "XMLHttpRequest_called", "XMLHttpRequest_called_complete", "i"]); + +describe("e2e/32-autoxhr-spa/21-xhr-fetch-wrapp-reinit.js", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent 2 beacons (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 2); + }, + this.skip.bind(this)); + }); + + it("Should have called the XHR proxy function once", function() { + assert.equal(window.XMLHttpRequest_called, 1); + }); + + it("Should have finished the XHR proxy function callback", function() { + assert.isTrue(window.XMLHttpRequest_called_complete); + }); +}); diff --git a/tests/page-templates/33-autoxhr-spa-startfromclick/01-click-xhr-routechange-img.html b/tests/page-templates/33-autoxhr-spa-startfromclick/01-click-xhr-routechange-img.html new file mode 100644 index 000000000..3491824d6 --- /dev/null +++ b/tests/page-templates/33-autoxhr-spa-startfromclick/01-click-xhr-routechange-img.html @@ -0,0 +1,52 @@ +<%= header %> +<%= boomerangScript %> + + + + + + +<%= footer %> diff --git a/tests/page-templates/33-autoxhr-spa-startfromclick/01-click-xhr-routechange-img.js b/tests/page-templates/33-autoxhr-spa-startfromclick/01-click-xhr-routechange-img.js new file mode 100644 index 000000000..4207aa0d4 --- /dev/null +++ b/tests/page-templates/33-autoxhr-spa-startfromclick/01-click-xhr-routechange-img.js @@ -0,0 +1,77 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,describe,it */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["spaInitTiming", "spaNavigationTiming", "eventFired", "i"]); + +describe("e2e/33-autoxhr-spa-startfromclick/01-click-xhr-routechange-img.js", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent 2 beacons (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 2); + }, + this.skip.bind(this)); + }); + + describe("Beacon 1", function() { + it("Should have http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + }); + + describe("Beacon 2", function() { + it("Should have http.initiator = spa", function() { + assert.equal(tf.beacons[1]["http.initiator"], "spa"); + }); + + it("Should have URL with #newstate", function() { + assert.include(tf.beacons[1].u, "#newstate"); + }); + + it("Should have rt.tstart around the time of the click", function() { + assert.closeTo(tf.beacons[1]["rt.tstart"], t.mouseEventTimes[0], 100); + }); + + it("Should have Page Load Time ~4040ms", function() { + assert.closeTo(tf.beacons[1].t_done, t.xhrTimes.xhr1.duration + t.imgTimes.img1.duration + 30 + 10, 100); + }); + + it("Should have Page Load Time >= 4040ms", function() { + assert.operator(tf.beacons[1].t_done, ">=", 4040); + }); + + it("Should have Back End Time ~2000ms", function() { + assert.closeTo(tf.beacons[1].t_resp, t.xhrTimes.xhr1.duration, 100); + }); + + it("Should have Front End Time ~2040ms", function() { + assert.closeTo(tf.beacons[1].t_page, t.imgTimes.img1.duration + 30 + 10, 100); + }); + }); + + describe("event spa_init", function() { + it("Should have sent the timing in the callback", function() { + assert.isDefined(window.spaInitTiming); + + assert.equal(tf.beacons[1]["rt.tstart"], window.spaInitTiming.requestStart); + }); + }); + + describe("event spa_navigation", function() { + it("Should have sent the timing in the callback", function() { + assert.isDefined(window.spaNavigationTiming); + + assert.equal(tf.beacons[1].t_done, window.spaNavigationTiming.loadEventEnd - window.spaNavigationTiming.requestStart); + }); + }); +}); diff --git a/tests/page-templates/33-autoxhr-spa-startfromclick/02-click-multiple-xhr-routechange-img.html b/tests/page-templates/33-autoxhr-spa-startfromclick/02-click-multiple-xhr-routechange-img.html new file mode 100644 index 000000000..21b730dc3 --- /dev/null +++ b/tests/page-templates/33-autoxhr-spa-startfromclick/02-click-multiple-xhr-routechange-img.html @@ -0,0 +1,46 @@ +<%= header %> +<%= boomerangScript %> + + + + +<%= footer %> diff --git a/tests/page-templates/33-autoxhr-spa-startfromclick/02-click-multiple-xhr-routechange-img.js b/tests/page-templates/33-autoxhr-spa-startfromclick/02-click-multiple-xhr-routechange-img.js new file mode 100644 index 000000000..03ec23b6a --- /dev/null +++ b/tests/page-templates/33-autoxhr-spa-startfromclick/02-click-multiple-xhr-routechange-img.js @@ -0,0 +1,61 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,describe,it */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["eventFired", "i"]); + +describe("e2e/33-autoxhr-spa-startfromclick/02-click-multiple-xhr-routechange-img.js", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent 2 beacons (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 2); + }, + this.skip.bind(this)); + }); + + describe("Beacon 1", function() { + it("Should have http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + }); + + describe("Beacon 2", function() { + it("Should have http.initiator = spa", function() { + assert.equal(tf.beacons[1]["http.initiator"], "spa"); + }); + + it("Should have URL with #newstate", function() { + assert.include(tf.beacons[1].u, "#newstate"); + }); + + it("Should have rt.tstart around the time of the click", function() { + assert.closeTo(tf.beacons[1]["rt.tstart"], t.mouseEventTimes[0], 100); + }); + + it("Should have Page Load Time ~6040ms", function() { + assert.closeTo(tf.beacons[1].t_done, t.xhrTimes.xhr1.duration + t.xhrTimes.xhr2.duration + t.imgTimes.img1.duration + 30 + 10, 200); + }); + + it("Should have Page Load Time >= 6040ms", function() { + assert.operator(tf.beacons[1].t_done, ">=", 6040); + }); + + it("Should have Back End Time ~4000ms", function() { + assert.closeTo(tf.beacons[1].t_resp, t.xhrTimes.xhr1.duration + t.xhrTimes.xhr2.duration, 200); + }); + + it("Should have Front End Time ~2040ms", function() { + assert.closeTo(tf.beacons[1].t_page, t.imgTimes.img1.duration + 30 + 10, 200); + }); + }); +}); diff --git a/tests/page-templates/33-autoxhr-spa-startfromclick/03-click-routechange-img.html b/tests/page-templates/33-autoxhr-spa-startfromclick/03-click-routechange-img.html new file mode 100644 index 000000000..49d56bc83 --- /dev/null +++ b/tests/page-templates/33-autoxhr-spa-startfromclick/03-click-routechange-img.html @@ -0,0 +1,40 @@ +<%= header %> +<%= boomerangScript %> + + + + +<%= footer %> diff --git a/tests/page-templates/33-autoxhr-spa-startfromclick/03-click-routechange-img.js b/tests/page-templates/33-autoxhr-spa-startfromclick/03-click-routechange-img.js new file mode 100644 index 000000000..f05cb7884 --- /dev/null +++ b/tests/page-templates/33-autoxhr-spa-startfromclick/03-click-routechange-img.js @@ -0,0 +1,61 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,describe,it */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["eventFired", "i"]); + +describe("e2e/33-autoxhr-spa-startfromclick/03-click-routechange-img.js", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent 2 beacons (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 2); + }, + this.skip.bind(this)); + }); + + describe("Beacon 1", function() { + it("Should have http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + }); + + describe("Beacon 2", function() { + it("Should have http.initiator = spa", function() { + assert.equal(tf.beacons[1]["http.initiator"], "spa"); + }); + + it("Should have URL with #newstate", function() { + assert.include(tf.beacons[1].u, "#newstate"); + }); + + it("Should have rt.tstart around the time of the click", function() { + assert.closeTo(tf.beacons[1]["rt.tstart"], t.mouseEventTimes[0], 100); + }); + + it("Should have Page Load Time ~2080ms", function() { + assert.closeTo(tf.beacons[1].t_done, t.imgTimes.img1.duration + 50 + 30, 100); + }); + + it("Should have Page Load Time >= 2080ms", function() { + assert.operator(tf.beacons[1].t_done, ">=", 2080); + }); + + it("Should have Back End Time = 0ms", function() { + assert.equal(tf.beacons[1].t_resp, 0); + }); + + it("Should have Front End Time ~2080ms", function() { + assert.closeTo(tf.beacons[1].t_page, t.imgTimes.img1.duration + 50 + 30, 100); + }); + }); +}); diff --git a/tests/page-templates/33-autoxhr-spa-startfromclick/04-click-delay-routechange-img.html b/tests/page-templates/33-autoxhr-spa-startfromclick/04-click-delay-routechange-img.html new file mode 100644 index 000000000..ba0b580db --- /dev/null +++ b/tests/page-templates/33-autoxhr-spa-startfromclick/04-click-delay-routechange-img.html @@ -0,0 +1,40 @@ +<%= header %> +<%= boomerangScript %> + + + + +<%= footer %> diff --git a/tests/page-templates/33-autoxhr-spa-startfromclick/04-click-delay-routechange-img.js b/tests/page-templates/33-autoxhr-spa-startfromclick/04-click-delay-routechange-img.js new file mode 100644 index 000000000..2a503a937 --- /dev/null +++ b/tests/page-templates/33-autoxhr-spa-startfromclick/04-click-delay-routechange-img.js @@ -0,0 +1,57 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,describe,it */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["eventFired", "i"]); + +describe("e2e/33-autoxhr-spa-startfromclick/04-click-delay-routechange-img.js", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent 2 beacons (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 2); + }, + this.skip.bind(this)); + }); + + describe("Beacon 1", function() { + it("Should have http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + }); + + describe("Beacon 2", function() { + it("Should have http.initiator = spa", function() { + assert.equal(tf.beacons[1]["http.initiator"], "spa"); + }); + + it("Should have URL with #newstate", function() { + assert.include(tf.beacons[1].u, "#newstate"); + }); + + it("Should have rt.tstart around the time of the route change (not the click)", function() { + assert.closeTo(tf.beacons[1]["rt.tstart"], t.routeChangeTimes[0], 25); + }); + + it("Should have Page Load Time ~2050ms", function() { + assert.closeTo(tf.beacons[1].t_done, t.imgTimes.img1.duration + 50, 100); + }); + + it("Should have Back End Time = 0", function() { + assert.equal(tf.beacons[1].t_resp, 0); + }); + + it("Should have Front End Time ~2050ms", function() { + assert.closeTo(tf.beacons[1].t_page, t.imgTimes.img1.duration + 50, 100); + }); + }); +}); diff --git a/tests/page-templates/33-autoxhr-spa-startfromclick/05-click-xhr-img.html b/tests/page-templates/33-autoxhr-spa-startfromclick/05-click-xhr-img.html new file mode 100644 index 000000000..6977a49f5 --- /dev/null +++ b/tests/page-templates/33-autoxhr-spa-startfromclick/05-click-xhr-img.html @@ -0,0 +1,37 @@ +<%= header %> +<%= boomerangScript %> + + + + +<%= footer %> diff --git a/tests/page-templates/33-autoxhr-spa-startfromclick/05-click-xhr-img.js b/tests/page-templates/33-autoxhr-spa-startfromclick/05-click-xhr-img.js new file mode 100644 index 000000000..ec3908c45 --- /dev/null +++ b/tests/page-templates/33-autoxhr-spa-startfromclick/05-click-xhr-img.js @@ -0,0 +1,61 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,describe,it */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["eventFired"]); + +describe("e2e/33-autoxhr-spa-startfromclick/05-click-xhr-img.js", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent 2 beacons (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 2); + }, + this.skip.bind(this)); + }); + + describe("Beacon 1", function() { + it("Should have http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + }); + + describe("Beacon 2", function() { + it("Should have http.initiator = xhr", function() { + assert.equal(tf.beacons[1]["http.initiator"], "xhr"); + }); + + it("Should have URL with xhr1", function() { + assert.include(tf.beacons[1].u, "xhr1"); + }); + + it("Should have rt.tstart around the time of the click", function() { + assert.closeTo(tf.beacons[1]["rt.tstart"], t.mouseEventTimes[0], 100); + }); + + it("Should have Page Load Time ~4030ms", function() { + assert.closeTo(tf.beacons[1].t_done, t.xhrTimes.xhr1.duration + t.imgTimes.img1.duration + 30, 100); + }); + + it("Should have Page Load Time >= 4030ms", function() { + assert.operator(tf.beacons[1].t_done, ">=", 4030); + }); + + it("Should have Back End Time ~2000ms", function() { + assert.closeTo(tf.beacons[1].t_resp, t.xhrTimes.xhr1.duration, 100); + }); + + it("Should have Front End Time ~2030ms", function() { + assert.closeTo(tf.beacons[1].t_page, t.imgTimes.img1.duration + 30, 100); + }); + }); +}); diff --git a/tests/page-templates/33-autoxhr-spa-startfromclick/06-click-click-xhr-img.html b/tests/page-templates/33-autoxhr-spa-startfromclick/06-click-click-xhr-img.html new file mode 100644 index 000000000..0c686c062 --- /dev/null +++ b/tests/page-templates/33-autoxhr-spa-startfromclick/06-click-click-xhr-img.html @@ -0,0 +1,43 @@ +<%= header %> +<%= boomerangScript %> + + + + +<%= footer %> diff --git a/tests/page-templates/33-autoxhr-spa-startfromclick/06-click-click-xhr-img.js b/tests/page-templates/33-autoxhr-spa-startfromclick/06-click-click-xhr-img.js new file mode 100644 index 000000000..7d828db7a --- /dev/null +++ b/tests/page-templates/33-autoxhr-spa-startfromclick/06-click-click-xhr-img.js @@ -0,0 +1,61 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,describe,it */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["eventFired"]); + +describe("e2e/33-autoxhr-spa-startfromclick/06-click-click-xhr-img.js", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent 2 beacons (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 2); + }, + this.skip.bind(this)); + }); + + describe("Beacon 1", function() { + it("Should have http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + }); + + describe("Beacon 2", function() { + it("Should have http.initiator = xhr", function() { + assert.equal(tf.beacons[1]["http.initiator"], "xhr"); + }); + + it("Should have URL with xhr1", function() { + assert.include(tf.beacons[1].u, "xhr1"); + }); + + it("Should have rt.tstart around the time of the second click", function() { + assert.closeTo(tf.beacons[1]["rt.tstart"], t.mouseEventTimes[1], 10); + }); + + it("Should have Page Load Time ~4030ms", function() { + assert.closeTo(tf.beacons[1].t_done, t.xhrTimes.xhr1.duration + t.imgTimes.img1.duration + 30, 100); + }); + + it("Should have Page Load Time >= 4030ms", function() { + assert.operator(tf.beacons[1].t_done, ">=", 4030); + }); + + it("Should have Back End Time ~2000ms", function() { + assert.closeTo(tf.beacons[1].t_resp, t.xhrTimes.xhr1.duration, 100); + }); + + it("Should have Front End Time ~2030ms", function() { + assert.closeTo(tf.beacons[1].t_page, t.imgTimes.img1.duration + 30, 100); + }); + }); +}); diff --git a/tests/page-templates/33-autoxhr-spa-startfromclick/07-click-xhr-no-dom.html b/tests/page-templates/33-autoxhr-spa-startfromclick/07-click-xhr-no-dom.html new file mode 100644 index 000000000..706216d6a --- /dev/null +++ b/tests/page-templates/33-autoxhr-spa-startfromclick/07-click-xhr-no-dom.html @@ -0,0 +1,37 @@ +<%= header %> +<%= boomerangScript %> + + + + +<%= footer %> diff --git a/tests/page-templates/33-autoxhr-spa-startfromclick/07-click-xhr-no-dom.js b/tests/page-templates/33-autoxhr-spa-startfromclick/07-click-xhr-no-dom.js new file mode 100644 index 000000000..f627ece2e --- /dev/null +++ b/tests/page-templates/33-autoxhr-spa-startfromclick/07-click-xhr-no-dom.js @@ -0,0 +1,31 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,describe,it */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["eventFired"]); + +describe("e2e/33-autoxhr-spa-startfromclick/07-click-xhr-no-dom.js", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent 1 beacon (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 1); + }, + this.skip.bind(this)); + }); + + describe("Beacon 1", function() { + it("Should have http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + }); +}); diff --git a/tests/page-templates/33-autoxhr-spa-startfromclick/08-click-routechange-no-dom.html b/tests/page-templates/33-autoxhr-spa-startfromclick/08-click-routechange-no-dom.html new file mode 100644 index 000000000..c4ee1445d --- /dev/null +++ b/tests/page-templates/33-autoxhr-spa-startfromclick/08-click-routechange-no-dom.html @@ -0,0 +1,35 @@ +<%= header %> +<%= boomerangScript %> + + + + +<%= footer %> diff --git a/tests/page-templates/33-autoxhr-spa-startfromclick/08-click-routechange-no-dom.js b/tests/page-templates/33-autoxhr-spa-startfromclick/08-click-routechange-no-dom.js new file mode 100644 index 000000000..76a9d7945 --- /dev/null +++ b/tests/page-templates/33-autoxhr-spa-startfromclick/08-click-routechange-no-dom.js @@ -0,0 +1,57 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,describe,it */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["eventFired", "i"]); + +describe("e2e/33-autoxhr-spa-startfromclick/08-click-routechange-no-dom.js", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent 2 beacons (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 2); + }, + this.skip.bind(this)); + }); + + describe("Beacon 1", function() { + it("Should have http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + }); + + describe("Beacon 2", function() { + it("Should have http.initiator = spa", function() { + assert.equal(tf.beacons[1]["http.initiator"], "spa"); + }); + + it("Should have URL with #newstate", function() { + assert.include(tf.beacons[1].u, "#newstate"); + }); + + it("Should have rt.tstart around the time of the click", function() { + assert.closeTo(tf.beacons[1]["rt.tstart"], t.mouseEventTimes[0], 100); + }); + + it("Should have Page Load Time = 1ms", function() { + assert.equal(tf.beacons[1].t_done, 1); + }); + + it("Should have Back End Time = 0ms", function() { + assert.equal(tf.beacons[1].t_resp, 0); + }); + + it("Should have Front End Time = 1ms", function() { + assert.equal(tf.beacons[1].t_page, 1); + }); + }); +}); diff --git a/tests/page-templates/33-autoxhr-spa-startfromclick/09-click-no-dom.html b/tests/page-templates/33-autoxhr-spa-startfromclick/09-click-no-dom.html new file mode 100644 index 000000000..1e5673707 --- /dev/null +++ b/tests/page-templates/33-autoxhr-spa-startfromclick/09-click-no-dom.html @@ -0,0 +1,31 @@ +<%= header %> +<%= boomerangScript %> + + + + +<%= footer %> diff --git a/tests/page-templates/33-autoxhr-spa-startfromclick/09-click-no-dom.js b/tests/page-templates/33-autoxhr-spa-startfromclick/09-click-no-dom.js new file mode 100644 index 000000000..f48ea73d6 --- /dev/null +++ b/tests/page-templates/33-autoxhr-spa-startfromclick/09-click-no-dom.js @@ -0,0 +1,31 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,describe,it */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["eventFired"]); + +describe("e2e/33-autoxhr-spa-startfromclick/09-click-no-dom.js", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent 1 beacon (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 1); + }, + this.skip.bind(this)); + }); + + describe("Beacon 1", function() { + it("Should have http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + }); +}); diff --git a/tests/page-templates/33-autoxhr-spa-startfromclick/10-click-img.html b/tests/page-templates/33-autoxhr-spa-startfromclick/10-click-img.html new file mode 100644 index 000000000..5ba912908 --- /dev/null +++ b/tests/page-templates/33-autoxhr-spa-startfromclick/10-click-img.html @@ -0,0 +1,34 @@ +<%= header %> +<%= boomerangScript %> + + + + +<%= footer %> diff --git a/tests/page-templates/33-autoxhr-spa-startfromclick/10-click-img.js b/tests/page-templates/33-autoxhr-spa-startfromclick/10-click-img.js new file mode 100644 index 000000000..245a239ac --- /dev/null +++ b/tests/page-templates/33-autoxhr-spa-startfromclick/10-click-img.js @@ -0,0 +1,53 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,describe,it */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["eventFired"]); + +describe("e2e/33-autoxhr-spa-startfromclick/10-click-img.js", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent 2 beacons (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 2); + }, + this.skip.bind(this)); + }); + + describe("Beacon 1", function() { + it("Should have http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + }); + + describe("Beacon 2", function() { + it("Should have http.initiator = xhr", function() { + assert.equal(tf.beacons[1]["http.initiator"], "xhr"); + }); + + it("Should have URL with img1", function() { + assert.include(tf.beacons[1].u, "img1"); + }); + + it("Should have rt.tstart around the time of the click", function() { + assert.closeTo(tf.beacons[1]["rt.tstart"], t.mouseEventTimes[0], 100); + }); + + it("Should have Page Load Time ~2030ms", function() { + assert.closeTo(tf.beacons[1].t_done, t.imgTimes.img1.duration + 30, 100); + }); + + it("Should have Page Load Time >= 2030ms", function() { + assert.operator(tf.beacons[1].t_done, ">=", 2030); + }); + }); +}); diff --git a/tests/page-templates/33-autoxhr-spa-startfromclick/11-xhr-img.html b/tests/page-templates/33-autoxhr-spa-startfromclick/11-xhr-img.html new file mode 100644 index 000000000..31d77e4c9 --- /dev/null +++ b/tests/page-templates/33-autoxhr-spa-startfromclick/11-xhr-img.html @@ -0,0 +1,31 @@ +<%= header %> +<%= boomerangScript %> + + + + +<%= footer %> diff --git a/tests/page-templates/33-autoxhr-spa-startfromclick/11-xhr-img.js b/tests/page-templates/33-autoxhr-spa-startfromclick/11-xhr-img.js new file mode 100644 index 000000000..b81eef48b --- /dev/null +++ b/tests/page-templates/33-autoxhr-spa-startfromclick/11-xhr-img.js @@ -0,0 +1,58 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,describe,it */ + +describe("e2e/33-autoxhr-spa-startfromclick/11-xhr-img.js", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent 2 beacons (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 2); + }, + this.skip.bind(this)); + }); + + describe("Beacon 1", function() { + it("Should have http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + }); + + describe("Beacon 2", function() { + it("Should have http.initiator = xhr", function() { + assert.equal(tf.beacons[1]["http.initiator"], "xhr"); + }); + + it("Should have URL with xhr1", function() { + assert.include(tf.beacons[1].u, "xhr1"); + }); + + it("Should have rt.tstart around the time of the XHR", function() { + assert.closeTo(tf.beacons[1]["rt.tstart"], t.xhrTimes.xhr1.start, 5); + }); + + it("Should have Page Load Time ~4000ms", function() { + assert.closeTo(tf.beacons[1].t_done, t.xhrTimes.xhr1.duration + t.imgTimes.img1.duration, 100); + }); + + it("Should have Page Load Time >= 4000ms", function() { + assert.operator(tf.beacons[1].t_done, ">=", 4000); + }); + + it("Should have Back End Time ~2000ms", function() { + assert.closeTo(tf.beacons[1].t_resp, t.xhrTimes.xhr1.duration, 100); + }); + + it("Should have Front End Time ~2000ms", function() { + assert.closeTo(tf.beacons[1].t_page, t.imgTimes.img1.duration, 100); + }); + }); +}); diff --git a/tests/page-templates/33-autoxhr-spa-startfromclick/12-click-img-xhr.html b/tests/page-templates/33-autoxhr-spa-startfromclick/12-click-img-xhr.html new file mode 100644 index 000000000..d5c382871 --- /dev/null +++ b/tests/page-templates/33-autoxhr-spa-startfromclick/12-click-img-xhr.html @@ -0,0 +1,40 @@ +<%= header %> +<%= boomerangScript %> + + + + +<%= footer %> diff --git a/tests/page-templates/33-autoxhr-spa-startfromclick/12-click-img-xhr.js b/tests/page-templates/33-autoxhr-spa-startfromclick/12-click-img-xhr.js new file mode 100644 index 000000000..4bd61b8b8 --- /dev/null +++ b/tests/page-templates/33-autoxhr-spa-startfromclick/12-click-img-xhr.js @@ -0,0 +1,61 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,describe,it */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["eventFired"]); + +describe("e2e/33-autoxhr-spa-startfromclick/12-click-img-xhr.js", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent 2 beacons (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 2); + }, + this.skip.bind(this)); + }); + + describe("Beacon 1", function() { + it("Should have http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + }); + + describe("Beacon 2", function() { + it("Should have http.initiator = xhr", function() { + assert.equal(tf.beacons[1]["http.initiator"], "xhr"); + }); + + it("Should have URL with xhr1", function() { + assert.include(tf.beacons[1].u, "xhr1"); + }); + + it("Should have rt.tstart around the time of the click", function() { + assert.closeTo(tf.beacons[1]["rt.tstart"], t.mouseEventTimes[0], 200); + }); + + it("Should have Page Load Time ~4030ms", function() { + assert.closeTo(tf.beacons[1].t_done, t.xhrTimes.xhr1.duration + t.imgTimes.img1.duration + 30, 200); + }); + + it("Should have Page Load Time >= 4030ms", function() { + assert.operator(tf.beacons[1].t_done, ">=", 4030); + }); + + it("Should have Back End Time ~2000ms", function() { + assert.closeTo(tf.beacons[1].t_resp, t.xhrTimes.xhr1.duration, 200); + }); + + it("Should have Front End Time ~2030ms", function() { + assert.closeTo(tf.beacons[1].t_page, t.imgTimes.img1.duration + 30, 200); + }); + }); +}); diff --git a/tests/page-templates/33-autoxhr-spa-startfromclick/13-routechange-no-dom.html b/tests/page-templates/33-autoxhr-spa-startfromclick/13-routechange-no-dom.html new file mode 100644 index 000000000..53043aef0 --- /dev/null +++ b/tests/page-templates/33-autoxhr-spa-startfromclick/13-routechange-no-dom.html @@ -0,0 +1,31 @@ +<%= header %> +<%= boomerangScript %> + + + + +<%= footer %> diff --git a/tests/page-templates/33-autoxhr-spa-startfromclick/13-routechange-no-dom.js b/tests/page-templates/33-autoxhr-spa-startfromclick/13-routechange-no-dom.js new file mode 100644 index 000000000..87c8047c3 --- /dev/null +++ b/tests/page-templates/33-autoxhr-spa-startfromclick/13-routechange-no-dom.js @@ -0,0 +1,57 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,describe,it */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["i"]); + +describe("e2e/33-autoxhr-spa-startfromclick/13-routechange-no-dom.js", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent 2 beacons (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 2); + }, + this.skip.bind(this)); + }); + + describe("Beacon 1", function() { + it("Should have http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + }); + + describe("Beacon 2", function() { + it("Should have http.initiator = spa", function() { + assert.equal(tf.beacons[1]["http.initiator"], "spa"); + }); + + it("Should have URL with #newstate", function() { + assert.include(tf.beacons[1].u, "#newstate"); + }); + + it("Should have rt.tstart around the time of the route change", function() { + assert.closeTo(tf.beacons[1]["rt.tstart"], t.routeChangeTimes[0], 25); + }); + + it("Should have Page Load Time = 1ms", function() { + assert.equal(tf.beacons[1].t_done, 1); + }); + + it("Should have Back End Time = 0ms", function() { + assert.equal(tf.beacons[1].t_resp, 0); + }); + + it("Should have Front End Time = 1ms", function() { + assert.equal(tf.beacons[1].t_page, 1); + }); + }); +}); diff --git a/tests/page-templates/33-autoxhr-spa-startfromclick/14-xhr-no-dom.html b/tests/page-templates/33-autoxhr-spa-startfromclick/14-xhr-no-dom.html new file mode 100644 index 000000000..d4207d364 --- /dev/null +++ b/tests/page-templates/33-autoxhr-spa-startfromclick/14-xhr-no-dom.html @@ -0,0 +1,31 @@ +<%= header %> +<%= boomerangScript %> + + + + +<%= footer %> diff --git a/tests/page-templates/33-autoxhr-spa-startfromclick/14-xhr-no-dom.js b/tests/page-templates/33-autoxhr-spa-startfromclick/14-xhr-no-dom.js new file mode 100644 index 000000000..98ef3a648 --- /dev/null +++ b/tests/page-templates/33-autoxhr-spa-startfromclick/14-xhr-no-dom.js @@ -0,0 +1,28 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,describe,it */ + +describe("e2e/33-autoxhr-spa-startfromclick/14-xhr-no-dom.js", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent 1 beacon (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 1); + }, + this.skip.bind(this)); + }); + + describe("Beacon 1", function() { + it("Should have http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + }); +}); diff --git a/tests/page-templates/33-autoxhr-spa-startfromclick/15-click-xhr-routechange-no-dom.html b/tests/page-templates/33-autoxhr-spa-startfromclick/15-click-xhr-routechange-no-dom.html new file mode 100644 index 000000000..96d4ca43e --- /dev/null +++ b/tests/page-templates/33-autoxhr-spa-startfromclick/15-click-xhr-routechange-no-dom.html @@ -0,0 +1,38 @@ +<%= header %> +<%= boomerangScript %> + + + + +<%= footer %> diff --git a/tests/page-templates/33-autoxhr-spa-startfromclick/15-click-xhr-routechange-no-dom.js b/tests/page-templates/33-autoxhr-spa-startfromclick/15-click-xhr-routechange-no-dom.js new file mode 100644 index 000000000..7c7b6b737 --- /dev/null +++ b/tests/page-templates/33-autoxhr-spa-startfromclick/15-click-xhr-routechange-no-dom.js @@ -0,0 +1,65 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,describe,it */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["eventFired", "i"]); + +describe("e2e/33-autoxhr-spa-startfromclick/15-click-xhr-routechange-no-dom.js", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent 2 beacons (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 2); + }, + this.skip.bind(this)); + }); + + describe("Beacon 1", function() { + it("Should have http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + }); + + describe("Beacon 2", function() { + it("Should have http.initiator = spa", function() { + assert.equal(tf.beacons[1]["http.initiator"], "spa"); + }); + + it("Should have URL with #newstate", function() { + assert.include(tf.beacons[1].u, "#newstate"); + }); + + it("Should have rt.tstart around the time of the click", function() { + assert.closeTo(tf.beacons[1]["rt.tstart"], t.mouseEventTimes[0], 100); + }); + + it("Should have Page Load Time ~2030ms", function() { + assert.closeTo(tf.beacons[1].t_done, t.xhrTimes.xhr1.duration + 30, 100); + }); + + it("Should have Page Load Time >= 2030ms", function() { + assert.operator(tf.beacons[1].t_done, ">=", 2030); + }); + + it("Should have Back End Time ~2000ms", function() { + assert.closeTo(tf.beacons[1].t_resp, t.xhrTimes.xhr1.duration, 100); + }); + + it("Should have Front End Time >30ms", function() { + assert.operator(tf.beacons[1].t_page, ">=", 30); + }); + + it("Should have Front End Time <= 150ms", function() { + assert.operator(tf.beacons[1].t_page, "<=", 150); + }); + }); +}); diff --git a/tests/page-templates/33-autoxhr-spa-startfromclick/16-click-routechange-xhr-img.html b/tests/page-templates/33-autoxhr-spa-startfromclick/16-click-routechange-xhr-img.html new file mode 100644 index 000000000..52f9d15ac --- /dev/null +++ b/tests/page-templates/33-autoxhr-spa-startfromclick/16-click-routechange-xhr-img.html @@ -0,0 +1,46 @@ +<%= header %> +<%= boomerangScript %> + + + + +<%= footer %> diff --git a/tests/page-templates/33-autoxhr-spa-startfromclick/16-click-routechange-xhr-img.js b/tests/page-templates/33-autoxhr-spa-startfromclick/16-click-routechange-xhr-img.js new file mode 100644 index 000000000..d8eef2169 --- /dev/null +++ b/tests/page-templates/33-autoxhr-spa-startfromclick/16-click-routechange-xhr-img.js @@ -0,0 +1,61 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,describe,it */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["eventFired", "i"]); + +describe("e2e/33-autoxhr-spa-startfromclick/16-click-routechange-xhr-img.js", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent 2 beacons (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 2); + }, + this.skip.bind(this)); + }); + + describe("Beacon 1", function() { + it("Should have http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + }); + + describe("Beacon 2", function() { + it("Should have http.initiator = spa", function() { + assert.equal(tf.beacons[1]["http.initiator"], "spa"); + }); + + it("Should have URL with #newstate", function() { + assert.include(tf.beacons[1].u, "#newstate"); + }); + + it("Should have rt.tstart around the time of the click", function() { + assert.closeTo(tf.beacons[1]["rt.tstart"], t.mouseEventTimes[0], 100); + }); + + it("Should have Page Load Time ~4070ms", function() { + assert.closeTo(tf.beacons[1].t_done, t.xhrTimes.xhr1.duration + t.imgTimes.img1.duration + 30 + 30 + 10, 100); + }); + + it("Should have Page Load Time >= 4070ms", function() { + assert.operator(tf.beacons[1].t_done, ">=", 4070); + }); + + it("Should have Back End Time ~2000ms", function() { + assert.closeTo(tf.beacons[1].t_resp, t.xhrTimes.xhr1.duration, 100); + }); + + it("Should have Front End Time ~2070ms", function() { + assert.closeTo(tf.beacons[1].t_page, t.imgTimes.img1.duration + 30 + 30 + 10, 100); + }); + }); +}); diff --git a/tests/page-templates/33-autoxhr-spa-startfromclick/17-click-routechange-xhr-no-dom.html b/tests/page-templates/33-autoxhr-spa-startfromclick/17-click-routechange-xhr-no-dom.html new file mode 100644 index 000000000..75082d177 --- /dev/null +++ b/tests/page-templates/33-autoxhr-spa-startfromclick/17-click-routechange-xhr-no-dom.html @@ -0,0 +1,40 @@ +<%= header %> +<%= boomerangScript %> + + + + +<%= footer %> diff --git a/tests/page-templates/33-autoxhr-spa-startfromclick/17-click-routechange-xhr-no-dom.js b/tests/page-templates/33-autoxhr-spa-startfromclick/17-click-routechange-xhr-no-dom.js new file mode 100644 index 000000000..b60ee44bb --- /dev/null +++ b/tests/page-templates/33-autoxhr-spa-startfromclick/17-click-routechange-xhr-no-dom.js @@ -0,0 +1,65 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,describe,it */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["eventFired", "i"]); + +describe("e2e/33-autoxhr-spa-startfromclick/17-click-routechange-xhr-no-dom.js", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent 2 beacons (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 2); + }, + this.skip.bind(this)); + }); + + describe("Beacon 1", function() { + it("Should have http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + }); + + describe("Beacon 2", function() { + it("Should have http.initiator = spa", function() { + assert.equal(tf.beacons[1]["http.initiator"], "spa"); + }); + + it("Should have URL with #newstate", function() { + assert.include(tf.beacons[1].u, "#newstate"); + }); + + it("Should have rt.tstart around the time of the click", function() { + assert.closeTo(tf.beacons[1]["rt.tstart"], t.mouseEventTimes[0], 100); + }); + + it("Should have Page Load Time ~2060ms", function() { + assert.closeTo(tf.beacons[1].t_done, t.xhrTimes.xhr1.duration + 30 + 30, 100); + }); + + it("Should have Page Load Time >= 2060ms", function() { + assert.operator(tf.beacons[1].t_done, ">=", 2060); + }); + + it("Should have Back End Time ~2000ms", function() { + assert.closeTo(tf.beacons[1].t_resp, t.xhrTimes.xhr1.duration, 100); + }); + + it("Should have Front End Time >60ms", function() { + assert.operator(tf.beacons[1].t_page, ">=", 30 + 30); + }); + + it("Should have Front End Time <= 150ms", function() { + assert.operator(tf.beacons[1].t_page, "<=", 150); + }); + }); +}); diff --git a/tests/page-templates/33-autoxhr-spa-startfromclick/18-xhr-img-click.html b/tests/page-templates/33-autoxhr-spa-startfromclick/18-xhr-img-click.html new file mode 100644 index 000000000..5fa240733 --- /dev/null +++ b/tests/page-templates/33-autoxhr-spa-startfromclick/18-xhr-img-click.html @@ -0,0 +1,44 @@ +<%= header %> +<%= boomerangScript %> + + + + +<%= footer %> diff --git a/tests/page-templates/33-autoxhr-spa-startfromclick/18-xhr-img-click.js b/tests/page-templates/33-autoxhr-spa-startfromclick/18-xhr-img-click.js new file mode 100644 index 000000000..11786f7de --- /dev/null +++ b/tests/page-templates/33-autoxhr-spa-startfromclick/18-xhr-img-click.js @@ -0,0 +1,61 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,describe,it */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["eventFired"]); + +describe("e2e/33-autoxhr-spa-startfromclick/18-xhr-img-click.js", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent 2 beacons (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 2); + }, + this.skip.bind(this)); + }); + + describe("Beacon 1", function() { + it("Should have http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + }); + + describe("Beacon 2", function() { + it("Should have http.initiator = xhr", function() { + assert.equal(tf.beacons[1]["http.initiator"], "xhr"); + }); + + it("Should have URL with xhr1", function() { + assert.include(tf.beacons[1].u, "xhr1"); + }); + + it("Should have rt.tstart around the time of the XHR", function() { + assert.closeTo(tf.beacons[1]["rt.tstart"], t.xhrTimes.xhr1.start, 5); + }); + + it("Should have Page Load Time ~4000ms", function() { + assert.closeTo(tf.beacons[1].t_done, t.xhrTimes.xhr1.duration + t.imgTimes.img1.duration, 100); + }); + + it("Should have Page Load Time >= 4000ms", function() { + assert.operator(tf.beacons[1].t_done, ">=", 4000); + }); + + it("Should have Back End Time ~2000ms", function() { + assert.closeTo(tf.beacons[1].t_resp, t.xhrTimes.xhr1.duration, 100); + }); + + it("Should have Front End Time ~2000ms", function() { + assert.closeTo(tf.beacons[1].t_page, t.imgTimes.img1.duration, 100); + }); + }); +}); diff --git a/tests/page-templates/33-autoxhr-spa-startfromclick/19-click-img-click-xhr-img.html b/tests/page-templates/33-autoxhr-spa-startfromclick/19-click-img-click-xhr-img.html new file mode 100644 index 000000000..81f6cc550 --- /dev/null +++ b/tests/page-templates/33-autoxhr-spa-startfromclick/19-click-img-click-xhr-img.html @@ -0,0 +1,48 @@ +<%= header %> +<%= boomerangScript %> + + + + +<%= footer %> diff --git a/tests/page-templates/33-autoxhr-spa-startfromclick/19-click-img-click-xhr-img.js b/tests/page-templates/33-autoxhr-spa-startfromclick/19-click-img-click-xhr-img.js new file mode 100644 index 000000000..ffc00fe09 --- /dev/null +++ b/tests/page-templates/33-autoxhr-spa-startfromclick/19-click-img-click-xhr-img.js @@ -0,0 +1,83 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,describe,it */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["eventFired"]); + +describe("e2e/33-autoxhr-spa-startfromclick/19-click-img-click-xhr-img.js", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent 3 beacons (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 3); + }, + this.skip.bind(this)); + }); + + describe("Beacon 1", function() { + it("Should have http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + }); + + describe("Beacon 2", function() { + it("Should have http.initiator = xhr", function() { + assert.equal(tf.beacons[1]["http.initiator"], "xhr"); + }); + + it("Should have URL with img1", function() { + assert.include(tf.beacons[1].u, "img1"); + }); + + it("Should have rt.tstart around the time of the first click", function() { + assert.closeTo(tf.beacons[1]["rt.tstart"], t.mouseEventTimes[0], 10); + }); + + it("Should have Page Load Time ~2030ms", function() { + assert.closeTo(tf.beacons[1].t_done, t.imgTimes.img1.duration + 30, 100); + }); + + it("Should have Page Load Time >= 2030ms", function() { + assert.operator(tf.beacons[1].t_done, ">=", 2030); + }); + }); + + describe("Beacon 3", function() { + it("Should have http.initiator = xhr", function() { + assert.equal(tf.beacons[2]["http.initiator"], "xhr"); + }); + + it("Should have URL with xhr1", function() { + assert.include(tf.beacons[2].u, "xhr1"); + }); + + it("Should have rt.tstart around the time of the second click", function() { + assert.closeTo(tf.beacons[2]["rt.tstart"], t.mouseEventTimes[1], 10); + }); + + it("Should have Page Load Time ~4030ms", function() { + assert.closeTo(tf.beacons[2].t_done, t.xhrTimes.xhr1.duration + t.imgTimes.img2.duration + 30, 100); + }); + + it("Should have Page Load Time >= 4030ms", function() { + assert.operator(tf.beacons[2].t_done, ">=", 4030); + }); + + it("Should have Back End Time ~2000ms", function() { + assert.closeTo(tf.beacons[2].t_resp, t.xhrTimes.xhr1.duration, 100); + }); + + it("Should have Front End Time ~2030ms", function() { + assert.closeTo(tf.beacons[2].t_page, t.imgTimes.img2.duration + 30, 100); + }); + }); +}); diff --git a/tests/page-templates/33-autoxhr-spa-startfromclick/20-xhr-img-routechange-img.html b/tests/page-templates/33-autoxhr-spa-startfromclick/20-xhr-img-routechange-img.html new file mode 100644 index 000000000..efc4d3fa9 --- /dev/null +++ b/tests/page-templates/33-autoxhr-spa-startfromclick/20-xhr-img-routechange-img.html @@ -0,0 +1,45 @@ +<%= header %> +<%= boomerangScript %> + + + + +<%= footer %> diff --git a/tests/page-templates/33-autoxhr-spa-startfromclick/20-xhr-img-routechange-img.js b/tests/page-templates/33-autoxhr-spa-startfromclick/20-xhr-img-routechange-img.js new file mode 100644 index 000000000..ee233f914 --- /dev/null +++ b/tests/page-templates/33-autoxhr-spa-startfromclick/20-xhr-img-routechange-img.js @@ -0,0 +1,91 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,describe,it */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["i"]); + +describe("e2e/33-autoxhr-spa-startfromclick/20-xhr-img-routechange-img.js", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent 3 beacons (XMLHttpRequest !== null)", function(done) { + this.timeout(10000); + + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 3); + }, + this.skip.bind(this)); + }); + + describe("Beacon 1", function() { + it("Should have http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + }); + + describe("Beacon 2", function() { + it("Should have http.initiator = xhr", function() { + assert.equal(tf.beacons[1]["http.initiator"], "xhr"); + }); + + it("Should have URL with xhr1", function() { + assert.include(tf.beacons[1].u, "xhr1"); + }); + + it("Should have rt.tstart around the time of the first XHR", function() { + assert.closeTo(tf.beacons[1]["rt.tstart"], t.xhrTimes.xhr1.start, 25); + }); + + it("Should have Page Load Time ~4030ms", function() { + assert.closeTo(tf.beacons[1].t_done, t.xhrTimes.xhr1.duration + t.imgTimes.img1.duration + 30, 100); + }); + + it("Should have Page Load Time >= 4030ms", function() { + assert.operator(tf.beacons[1].t_done, ">=", 4030); + }); + + it("Should have Back End Time ~2000ms", function() { + assert.closeTo(tf.beacons[1].t_resp, t.xhrTimes.xhr1.duration, 100); + }); + + it("Should have Front End Time ~2030ms", function() { + assert.closeTo(tf.beacons[1].t_page, t.imgTimes.img1.duration + 30, 100); + }); + }); + + describe("Beacon 3", function() { + it("Should have http.initiator = spa", function() { + assert.equal(tf.beacons[2]["http.initiator"], "spa"); + }); + + it("Should have URL with #newstate", function() { + assert.include(tf.beacons[2].u, "#newstate"); + }); + + it("Should have rt.tstart around the time of the route change", function() { + assert.closeTo(tf.beacons[2]["rt.tstart"], t.routeChangeTimes[0], 25); + }); + + it("Should have Page Load Time ~2030ms", function() { + assert.closeTo(tf.beacons[2].t_done, t.imgTimes.img2.duration + 30, 100); + }); + + it("Should have Page Load Time >= 2030ms", function() { + assert.operator(tf.beacons[2].t_done, ">=", 2030); + }); + + it("Should have Back End Time = 0ms", function() { + assert.equal(tf.beacons[2].t_resp, 0); + }); + + it("Should have Front End Time ~2030ms", function() { + assert.closeTo(tf.beacons[2].t_page, t.imgTimes.img2.duration + 30, 100); + }); + }); +}); diff --git a/tests/page-templates/34-bfcache/00-basic.html b/tests/page-templates/34-bfcache/00-basic.html new file mode 100644 index 000000000..000b706d0 --- /dev/null +++ b/tests/page-templates/34-bfcache/00-basic.html @@ -0,0 +1,22 @@ +<%= header %> +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/34-bfcache/00-basic.js b/tests/page-templates/34-bfcache/00-basic.js new file mode 100644 index 000000000..5e7a8e7c6 --- /dev/null +++ b/tests/page-templates/34-bfcache/00-basic.js @@ -0,0 +1,105 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/34-bfcache/00-basic", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent two beacons", function(done) { + t.ensureBeaconCount(done, 2); + }); + + describe("Beacon 1 - Page Load", function() { + it("Should have been an Page Load beacon", function() { + var b = tf.beacons[0]; + + assert.isUndefined(b["http.initiator"]); + }); + }); + + describe("Beacon 1 - BFCache", function() { + it("Should have been a BFCache beacon", function() { + var b = tf.beacons[1]; + + assert.equal(b["http.initiator"], "bfcache"); + }); + + it("Should have set rt.start = manual", function() { + var b = tf.beacons[1]; + + assert.equal(b["rt.start"], "manual"); + }); + + it("Should have set t_done = 100", function() { + var b = tf.beacons[1]; + + assert.equal(b.t_done, "100"); + }); + + it("Should have set t_page = 100", function() { + var b = tf.beacons[1]; + + assert.equal(b.t_page, "100"); + }); + + it("Should have set t_resp = 0", function() { + var b = tf.beacons[1]; + + assert.equal(b.t_resp, "0"); + }); + + it("Should have set rt.tstart", function() { + var b = tf.beacons[1]; + + assert.isDefined(b["rt.start"]); + }); + + it("Should have set rt.end", function() { + var b = tf.beacons[1]; + + assert.isDefined(b["rt.end"]); + }); + + it("Should have set rt.end to be rt.tstart + t_done", function() { + var b = tf.beacons[1]; + + assert.equal(parseInt(b["rt.end"], 10), parseInt(b["rt.tstart"], 10) + parseInt(b.t_done)); + }); + + it("Should have set nt_nav_type = 2", function() { + var b = tf.beacons[1]; + + assert.equal(b.nt_nav_type, "2"); + }); + + it("Should have set pt.fcp > 0", function() { + var b = tf.beacons[1]; + + assert.operator(parseInt(b["pt.fcp"], 10), ">", 450); + }); + + it("Should have set pt.lcp > 0", function() { + var b = tf.beacons[1]; + + assert.operator(parseInt(b["pt.lcp"], 10), ">", 450); + }); + + it("Should have set u", function() { + var b = tf.beacons[1]; + + assert.equal(b.u, location.href); + }); + + it("Should have set rt.sl = 2", function() { + var b = tf.beacons[1]; + + assert.equal(b["rt.sl"], "2"); + }); + + it("Should have set n = 2", function() { + var b = tf.beacons[1]; + + assert.equal(b.n, "2"); + }); + }); +}); diff --git a/tests/page-templates/34-bfcache/01-nrr.html b/tests/page-templates/34-bfcache/01-nrr.html new file mode 100644 index 000000000..708e893a2 --- /dev/null +++ b/tests/page-templates/34-bfcache/01-nrr.html @@ -0,0 +1,64 @@ +<%= header %> + +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/34-bfcache/01-nrr.js b/tests/page-templates/34-bfcache/01-nrr.js new file mode 100644 index 000000000..02faef0ac --- /dev/null +++ b/tests/page-templates/34-bfcache/01-nrr.js @@ -0,0 +1,31 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/34-bfcache/01-nrr", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + var REASONS = "Unload handler,Unknown,id-id3,name-name4,frame-unknown"; + + it("Should have sent one beacon", function(done) { + t.ensureBeaconCount(done, 1); + }); + + describe("Beacon 1 - Page Load", function() { + it("Should have been an Page Load beacon", function() { + var b = tf.beacons[0]; + + assert.isUndefined(b["http.initiator"]); + }); + + it("Should have set the NotRestoredReason", function() { + var b = tf.beacons[0]; + + assert.equal(b["bfc.nrr"], REASONS); + }); + }); + + it("Should have set BOOMR.plugins.BFCache.notRestoredReasons", function() { + assert.equal(BOOMR.plugins.BFCache.notRestoredReasons(), REASONS); + }); +}); diff --git a/tests/page-templates/34-bfcache/02-dwell.html b/tests/page-templates/34-bfcache/02-dwell.html new file mode 100644 index 000000000..cf950e408 --- /dev/null +++ b/tests/page-templates/34-bfcache/02-dwell.html @@ -0,0 +1,26 @@ +<%= header %> +<%= boomerangScript %> + + +<%= footer %> diff --git a/tests/page-templates/34-bfcache/02-dwell.js b/tests/page-templates/34-bfcache/02-dwell.js new file mode 100644 index 000000000..b20fec308 --- /dev/null +++ b/tests/page-templates/34-bfcache/02-dwell.js @@ -0,0 +1,19 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/34-bfcache/02-dwell", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent 1 beacon", function(done) { + t.ensureBeaconCount(done, 1); + }); + + describe("Beacon 1 - Page Load", function() { + it("Should have been an Page Load beacon", function() { + var b = tf.beacons[0]; + + assert.isUndefined(b["http.initiator"]); + }); + }); +}); diff --git a/tests/page-templates/34-bfcache/tests.json5 b/tests/page-templates/34-bfcache/tests.json5 new file mode 100644 index 000000000..cd851e10d --- /dev/null +++ b/tests/page-templates/34-bfcache/tests.json5 @@ -0,0 +1,5 @@ +{ + all: { + requires: ["bfcache"] + } +} diff --git a/tests/page-templates/35-spa/00-delayed-boomerang.html b/tests/page-templates/35-spa/00-delayed-boomerang.html new file mode 100644 index 000000000..ccb712a67 --- /dev/null +++ b/tests/page-templates/35-spa/00-delayed-boomerang.html @@ -0,0 +1,28 @@ +<%= header %> + + +<%= footer %> diff --git a/tests/page-templates/35-spa/00-delayed-boomerang.js b/tests/page-templates/35-spa/00-delayed-boomerang.js new file mode 100644 index 000000000..9523a3ba8 --- /dev/null +++ b/tests/page-templates/35-spa/00-delayed-boomerang.js @@ -0,0 +1,18 @@ +/* eslint-env mocha */ +/* global BOOMR_test */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, [ "BOOMR_spa" ]); + +describe("e2e/35-spa/00-delayed-boomerang", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have fired the 'spa_navigation' event with the timestamp of the navigationStart", function() { + if (!t.isNavigationTimingSupported()) { + return this.skip(); + } + + assert.equal(window.BOOMR_spa[0].requestStart, window.performance.timing.navigationStart); + }); +}); diff --git a/tests/page-templates/35-spa/01-soft-nav.html b/tests/page-templates/35-spa/01-soft-nav.html new file mode 100644 index 000000000..296161366 --- /dev/null +++ b/tests/page-templates/35-spa/01-soft-nav.html @@ -0,0 +1,131 @@ +<%= header %> + + + +<%= boomerangScript %> + + + + + + + + + + +<%= footer %> diff --git a/tests/page-templates/35-spa/01-soft-nav.js b/tests/page-templates/35-spa/01-soft-nav.js new file mode 100644 index 000000000..014963ac5 --- /dev/null +++ b/tests/page-templates/35-spa/01-soft-nav.js @@ -0,0 +1,230 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,describe,it */ + +// globals from this test +Array.prototype.push.apply(BOOMR_test.addedGlobals, ["$", "jQuery", "imgSize", "imgLatest", "getLcpPoEntry"]); + +describe("e2e/35-spa/01-soft-nav.js", function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent 4 beacons (window.history !== null)", function(done) { + this.timeout(10000); + + if (!window.history) { + return this.skip(); + } + + t.ensureBeaconCount(done, 4); + }); + + describe("Beacon 1", function() { + it("Should have http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + + it("Should not have spa.snh.s", function() { + assert.isUndefined(tf.beacons[0]["spa.snh.s"]); + }); + + it("Should not have spa.snh.n", function() { + assert.isUndefined(tf.beacons[0]["spa.snh.n"]); + }); + + it("Should have set pt.lcp (if LargestContentfulPaint is supported and happened by load)", function(done) { + var observerWait, + that = this; + + if (!t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + var observer = new t.origPerformanceObserver(function(list) { + clearTimeout(observerWait); + + var entries = list.getEntries(); + + if (entries.length === 0) { + return that.skip(); + } + + var lcp = entries[entries.length - 1]; + var lcpTime = lcp.renderTime || lcp.loadTime; + + // validation of First Paint + assert.isNumber(tf.beacons[0]["pt.lcp"]); + assert.operator(parseInt(tf.beacons[0]["pt.lcp"], 10), ">=", 0); + + observer.disconnect(); + + done(); + }); + + observer.observe({ type: "largest-contentful-paint", buffered: true }); + + // wait for the LCP observer to fire, if not, skip the test + observerWait = setTimeout(function() { + // no LCP before load + return this.skip(); + }.bind(this), 10000); + }); + }); + + describe("Beacon 2", function() { + it("Should have http.initiator = spa", function() { + assert.equal(tf.beacons[1]["http.initiator"], "spa"); + }); + + it("Should have spa.snh.s", function() { + if (!t.isSoftNavHeuristicsSupported()) { + return this.skip(); + } + + assert.isDefined(tf.beacons[1]["spa.snh.s"]); + }); + + it("Should have spa.snh.n", function() { + if (!t.isSoftNavHeuristicsSupported()) { + return this.skip(); + } + + assert.isDefined(tf.beacons[1]["spa.snh.n"]); + }); + + it("Should have spa.snh.n=1", function() { + if (!t.isSoftNavHeuristicsSupported()) { + return this.skip(); + } + + assert.equal(tf.beacons[1]["spa.snh.n"], "1"); + }); + + it("Should have pt.lcp", function() { + if (!t.isSoftNavHeuristicsSupported() || + !t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + assert.isDefined(tf.beacons[1]["pt.lcp"]); + }); + + it("Should have pt.lcp >= 1", function() { + if (!t.isSoftNavHeuristicsSupported() || + !t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + assert.operator(parseInt(tf.beacons[1]["pt.lcp"], 10), ">=", 1); + }); + + it("Should have pt.lcp.src", function() { + if (!t.isSoftNavHeuristicsSupported()) { + return this.skip(); + } + + assert.isDefined(tf.beacons[1]["pt.lcp.src"]); + assert.include(tf.beacons[1]["pt.lcp.src"], "nav1"); + }); + }); + + describe("Beacon 3", function() { + it("Should have http.initiator = spa", function() { + assert.equal(tf.beacons[2]["http.initiator"], "spa"); + }); + + it("Should have spa.snh.s", function() { + if (!t.isSoftNavHeuristicsSupported()) { + return this.skip(); + } + + assert.isDefined(tf.beacons[2]["spa.snh.s"]); + }); + + it("Should have spa.snh.n", function() { + if (!t.isSoftNavHeuristicsSupported()) { + return this.skip(); + } + + assert.isDefined(tf.beacons[2]["spa.snh.n"]); + }); + + it("Should have spa.snh.n=4", function() { + if (!t.isSoftNavHeuristicsSupported()) { + return this.skip(); + } + + assert.equal(tf.beacons[2]["spa.snh.n"], "4"); + }); + + it("Should have pt.lcp", function() { + if (!t.isSoftNavHeuristicsSupported() || + !t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + assert.isDefined(tf.beacons[2]["pt.lcp"]); + }); + + it("Should have pt.lcp >= 1000", function() { + if (!t.isSoftNavHeuristicsSupported() || + !t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + assert.operator(parseInt(tf.beacons[2]["pt.lcp"], 10), ">=", 1000); + }); + + it("Should have pt.lcp.src", function() { + if (!t.isSoftNavHeuristicsSupported() || + !t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + assert.isDefined(tf.beacons[2]["pt.lcp.src"]); + assert.include(tf.beacons[2]["pt.lcp.src"], "nav2"); + }); + }); + + describe("Beacon 4", function() { + it("Should have http.initiator = spa", function() { + assert.equal(tf.beacons[3]["http.initiator"], "spa"); + }); + + it("Should have spa.snh.s", function() { + if (!t.isSoftNavHeuristicsSupported()) { + return this.skip(); + } + + assert.isDefined(tf.beacons[3]["spa.snh.s"]); + }); + + it("Should have spa.snh.n", function() { + if (!t.isSoftNavHeuristicsSupported()) { + return this.skip(); + } + + assert.isDefined(tf.beacons[3]["spa.snh.n"]); + }); + + it("Should have spa.snh.n=1", function() { + if (!t.isSoftNavHeuristicsSupported()) { + return this.skip(); + } + + assert.equal(tf.beacons[3]["spa.snh.n"], "1"); + }); + + it("Should have pt.lcp", function() { + if (!t.isSoftNavHeuristicsSupported() || + !t.isLargestContentfulPaintSupported()) { + return this.skip(); + } + + assert.isDefined(tf.beacons[3]["pt.lcp"]); + }); + }); +}); diff --git a/tests/page-templates/35-spa/tests.json5 b/tests/page-templates/35-spa/tests.json5 new file mode 100644 index 000000000..43cee0823 --- /dev/null +++ b/tests/page-templates/35-spa/tests.json5 @@ -0,0 +1,5 @@ +{ + all: { + requires: ["spa"] + } +} diff --git a/tests/page-templates/36-bw/00-bw.html b/tests/page-templates/36-bw/00-bw.html new file mode 100644 index 000000000..fda21158f --- /dev/null +++ b/tests/page-templates/36-bw/00-bw.html @@ -0,0 +1,14 @@ +<%= header %> +<%= boomerangSnippet %> + + +<%= footer %> diff --git a/tests/page-templates/36-bw/00-bw.js b/tests/page-templates/36-bw/00-bw.js new file mode 100644 index 000000000..c85e5787a --- /dev/null +++ b/tests/page-templates/36-bw/00-bw.js @@ -0,0 +1,23 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ + +describe("e2e/34-bw/00-bw.js", function() { + var t = BOOMR_test; + + var tf = BOOMR.plugins.TestFramework; + + it("Should return bandwidth in beacon", function() { + if (!t.isNetworkAPISupported()) { + return; + } + + var b = tf.beacons[0]; + + assert(b.bw_time); + assert(b.bw_time > 0); + assert(!isNaN(b.bw_err)); + assert(b.bw_err > 0); + assert(!isNaN(b.bw)); + assert(b.bw > 0); + }); +}); diff --git a/tests/perf/.eslintrc b/tests/perf/.eslintrc new file mode 100644 index 000000000..d67b0a288 --- /dev/null +++ b/tests/perf/.eslintrc @@ -0,0 +1,8 @@ +{ + env: { + es6: true + }, + parserOptions: { + ecmaVersion: 2017 + } +} diff --git a/tests/perf/browser-utils.js b/tests/perf/browser-utils.js new file mode 100644 index 000000000..379d8eadf --- /dev/null +++ b/tests/perf/browser-utils.js @@ -0,0 +1,57 @@ +// +// Imports +// +const cdp = require("chrome-remote-interface"); +const chromeLauncher = require("chrome-launcher"); +const config = require("./perf-tests.json5").config; + +// +// Functions +// + +/** + * Launches Chrome + * + * @returns {object} Chrome and CDP Client objects + */ +async function launchChrome() { + console.debug("browserUtils.launchChrome: Launching Chrome and CDP"); + + // launch Chrome + const chrome = await chromeLauncher.launch(config.chromeConfig); + + // Waits for the debug protocol + const client = await cdp(); + + client.on("error", function(e) { + console.error("browserUtils.launchChrome: CDP error", e); + }); + + client.on("disconnect", function(e) { + console.warn("browserUtils.launchChrome: CDP disconnected", e); + }); + + return { chrome, client }; +} + +/** + * Closes Chrome + * + * @param {object} client CDP Client + * @param {object} chrome Chrome + * + * @returns {undefined} + */ +async function closeChrome(client, chrome) { + console.debug("browserUtils.closeChrome: Closing Chrome"); + + // close and kill + await client.close(); + await chrome.kill(); +} + +// +// Exports +// +exports.launchChrome = launchChrome; +exports.closeChrome = closeChrome; diff --git a/tests/perf/page-templates/00-basic/00-empty.html b/tests/perf/page-templates/00-basic/00-empty.html new file mode 100644 index 000000000..8814dc4f5 --- /dev/null +++ b/tests/perf/page-templates/00-basic/00-empty.html @@ -0,0 +1,6 @@ +<%= headerPerf %> + +<%= boomerangScriptPerf %> +<%= footer %> diff --git a/tests/perf/page-templates/00-basic/01-all-plugins.html b/tests/perf/page-templates/00-basic/01-all-plugins.html new file mode 100644 index 000000000..8b9fac2e2 --- /dev/null +++ b/tests/perf/page-templates/00-basic/01-all-plugins.html @@ -0,0 +1,6 @@ +<%= headerPerf %> + +<%= boomerangScriptPerf %> +<%= footer %> diff --git a/tests/perf/page-templates/01-restiming/00-expensive.html b/tests/perf/page-templates/01-restiming/00-expensive.html new file mode 100644 index 000000000..d6860a0de --- /dev/null +++ b/tests/perf/page-templates/01-restiming/00-expensive.html @@ -0,0 +1,207 @@ +<%= headerPerf %> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +<%= boomerangScriptPerf %> +<%= footer %> diff --git a/tests/perf/perf-compare.js b/tests/perf/perf-compare.js new file mode 100644 index 000000000..15bc89eb9 --- /dev/null +++ b/tests/perf/perf-compare.js @@ -0,0 +1,77 @@ +// +// Imports +// +const path = require("path"); +const util = require("util"); +const fs = require("fs"); +const grunt = require("grunt"); +const chalk = require("chalk"); + +// promisify"d functions +const mkdir = util.promisify(fs.mkdir); +const exists = util.promisify(fs.exists); +const readFile = util.promisify(fs.readFile); +const writeFile = util.promisify(fs.writeFile); + +const resultsPath = path.join(__dirname, "results"); +const baselineFilePath = path.join(resultsPath, "baseline.json"); +const metricsFilePath = path.join(resultsPath, "metrics.json"); + +// +// Exports +// +module.exports = async function() { + const done = this.async(); + + // check files exist first + if (!await exists(baselineFilePath)) { + grunt.log.error(baselineFilePath + " does not exist! Creat it with 'grunt perf:baseline'"); + + return done(); + } + + if (!await exists(metricsFilePath)) { + grunt.log.error(metricsFilePath + " does not exist! Creat it with 'grunt perf'"); + + return done(); + } + + // load both metrics files + const baselineMetrics = JSON.parse(await readFile(baselineFilePath, "utf-8")); + const compareMetrics = JSON.parse(await readFile(metricsFilePath, "utf-8")); + + console.log("Results comparison to baseline:\n"); + + for (var scenarioName in compareMetrics) { + for (var testName in compareMetrics[scenarioName]) { + for (var metricName in compareMetrics[scenarioName][testName]) { + const base = baselineMetrics[scenarioName][testName][metricName]; + const cur = compareMetrics[scenarioName][testName][metricName]; + + // compare metrics to the baseline + const diff = cur - base; + const diffPct = Math.floor(diff / (base ? base : diff) * 100); + + // output display content + const metricNamePrefix = `${chalk.underline(scenarioName + "." + testName + "." + metricName).padEnd(80)}: `; + const diffPctSuffix = " (" + (diff > 0 ? "+" : "") + `${chalk.yellow(diffPct + "%")})`; + + // show diffs + if (!base) { + console.log(`${metricNamePrefix}${chalk.gray("[missing in baseline]")}`); + } + else if (diff === 0 && !grunt.option("diff-only")) { + console.log(`${metricNamePrefix}=${chalk.green(base)}`); + } + else if (diff > 0) { + console.log(`${metricNamePrefix} ${cur} ${chalk.yellow("+" + diff)}${diffPctSuffix}`); + } + else if (diff < 0) { + console.log(`${metricNamePrefix} ${cur} ${chalk.yellow(diff)}${diffPctSuffix}`); + } + } + } + } + + done(); +}; diff --git a/tests/perf/perf-tests.js b/tests/perf/perf-tests.js new file mode 100644 index 000000000..b6f74ad01 --- /dev/null +++ b/tests/perf/perf-tests.js @@ -0,0 +1,57 @@ +// +// Imports +// +const path = require("path"); +const util = require("util"); +const fs = require("fs"); + +const writeFile = util.promisify(fs.writeFile); + +// allow loading of JSON5 from require() +const JSON5 = require("json5"); + +require("json5/lib/register"); + +// +// Imports +// +const Profiler = require("./profiler"); +const Sizer = require("./sizer"); + +// +// Locals +// +const resultsPath = path.join(__dirname, "results"); +const allMetricsPath = path.join(resultsPath, "metrics.json"); + +// +// Exports +// +module.exports = async function() { + var done = this.async(); + + let allMetrics = {}; + + // + // Collect Browser Profiler data + // + console.debug("Running Profiler"); + + await Profiler.run(allMetrics); + + // + // Collect script sizes + // + console.debug("Running Sizer"); + + await Sizer.run(allMetrics); + + // + // Write out all metrics + // + console.debug("Writing out final metrics.json"); + + await writeFile(allMetricsPath, JSON.stringify(allMetrics, null, 2)); + + done(); +}; diff --git a/tests/perf/perf-tests.json5 b/tests/perf/perf-tests.json5 new file mode 100644 index 000000000..ed7f36cd9 --- /dev/null +++ b/tests/perf/perf-tests.json5 @@ -0,0 +1,38 @@ +{ + // Server locations + server: { + main: "boomerang-test.local", + second: "boomerang-test2.local" + }, + ports: { + main: 4002, + second: 4003 + }, + + // Profiler configuration + config: { + // How many iterations to run + iterations: 10, + + // Profiler sample interval + sampleInterval: 100, + + // How long to wait after onload before stopping the Profiler + postLoadWait: 1000, + + // How long to wait for a page to load before aborting + timeout: 60000, + + // Chrome launch configuration + chromeConfig: { + port: 9222, + chromeFlags: [ + "--headless", + "--disable-gpu", + "--timeout 60000", + "--no-sandbox", + "--disable-setuid-sandbox" + ] + } + } +} diff --git a/tests/perf/profiler.js b/tests/perf/profiler.js new file mode 100644 index 000000000..f327b4d2f --- /dev/null +++ b/tests/perf/profiler.js @@ -0,0 +1,365 @@ +// +// Imports +// +const path = require("path"); +const util = require("util"); +const fs = require("fs"); +const CpuProfileFilter = require("cpuprofile-filter"); +const now = require("performance-now"); +const Stats = require("fast-stats").Stats; + +// promisify'd functions +const mkdir = util.promisify(fs.mkdir); +const exists = util.promisify(fs.exists); +const readFile = util.promisify(fs.readFile); +const writeFile = util.promisify(fs.writeFile); + +// local modules +const BrowserUtils = require("./browser-utils"); + +// test information +const testsFile = path.join(__dirname, "perf-tests.json5"); +const perfTestConfig = require(testsFile); +const config = perfTestConfig.config; + +// scenario information +const scenariosFile = path.join(__dirname, "scenarios.json"); + +const resultsPath = path.join(__dirname, "results"); + +// Browser functions +let navTimingJavaScript; +let marksJavaScript; +let measuresJavaScript; + +// +// Functions +// +/** + * Gets the URL for a test + * + * @param {string} urlPath URL path + * + * @returns {string} URL for a test + */ +function getUrl(urlPath) { + return `http://${perfTestConfig.server.main}:${perfTestConfig.ports.main}${urlPath}`; +} + +/** + * Ensures the specified directory exists + * + * @param {string} dir Directory + */ +async function ensureDirectory(dir) { + if (!await exists(dir)) { + console.log(`Creating ${dir}`); + + await mkdir(dir); + } +} + +/** + * Runs the profiler + */ +async function run(allMetrics) { + const scenarios = require(scenariosFile).tests; + + return new Promise(async function(resolve, reject) { + // load JavaScript we'll be executing in the Chrome process + navTimingJavaScript = await readFile(path.join(__dirname, "utils", "browser-fetch-navtiming.js"), "utf-8"); + marksJavaScript = await readFile(path.join(__dirname, "utils", "browser-fetch-marks.js"), "utf-8"); + measuresJavaScript = await readFile(path.join(__dirname, "utils", "browser-fetch-measures.js"), "utf-8"); + + console.log(`Running ${scenarios.length} Profiler scenarios`); + + // make sure our results path exist + await ensureDirectory(resultsPath); + + // iterate through all scenarios + for (var i = 0; i < scenarios.length; i++) { + const scenario = scenarios[i]; + + // metrics for this scenario + let scenarioMetrics = {}; + + // name and path + const scenarioName = scenario.path + "." + scenario.file; + const scenarioUrl = `/perf/pages/${scenario.path}/${scenario.file}.html`; + + console.log(`Scenario: ${scenarioName}:`); + + // ensure the scenario results directory exists + let scenarioPath = path.join(resultsPath, scenarioName); + + await ensureDirectory(scenarioPath); + + // iterate the specific number of times from config + for (var j = 1; j <= config.iterations; j++) { + console.log(`Iteration #${j}:`); + + // gather Profiler data for this URL + const results = await loadAndGatherProfile(getUrl(scenarioUrl)); + + console.debug("Analyzing results..."); + + // analyze the results + const analysis = await analyzeResults(results); + + // create a [n].metrics.json for this iteration + let metricsPath = path.join(scenarioPath, `${j}.metrics.json`); + + await writeFile(metricsPath, JSON.stringify(analysis.metrics, null, 2)); + + // add to scenario overall metrics + for (var metricName in analysis.metrics) { + if (!scenarioMetrics[metricName]) { + scenarioMetrics[metricName] = new Stats(); + } + + scenarioMetrics[metricName].push(analysis.metrics[metricName]); + } + + // write raw results to disk + console.debug("Writing results to disk"); + for (var rawData in analysis.raw) { + let rawDataPath = path.join(scenarioPath, `${j}.${rawData}`); + + if (typeof analysis.raw[rawData] !== "undefined") { + await writeFile(rawDataPath, JSON.stringify(analysis.raw[rawData], null, 2)); + } + } + } + + // calculate stats on metrics + for (var metric in scenarioMetrics) { + // calculate median for this scenario + scenarioMetrics[metric] = scenarioMetrics[metric].median(); + + // copy to all metrics + allMetrics[scenario.path] = allMetrics[scenario.path] || {}; + allMetrics[scenario.path][scenario.file] = allMetrics[scenario.path][scenario.file] || {}; + allMetrics[scenario.path][scenario.file][metric] = scenarioMetrics[metric]; + } + + // write metrics for this test + let metricsPath = path.join(scenarioPath, "metrics.json"); + + await writeFile(metricsPath, JSON.stringify(scenarioMetrics, null, 2)); + } + + resolve(); + }); +} + +/** + * Sleeps for the specified number of milliseconds + * + * @param {number} n Milliseconds + */ +const sleep = n => new Promise(resolve => setTimeout(resolve, n)); + +/** + * Analyzes results + * + * @param {object} results Results + */ +async function analyzeResults(results) { + return new Promise(function(resolve, reject) { + console.debug("Analyzing Profile and writing to disk..."); + + // get filtered Profiler data to just boomerang + var filteredProfile = CpuProfileFilter.filter(results.profile, { + files: [ + "boomerang-latest-debug" + ] + }); + + // Metrics output + var metrics = { + // Metrics + metrics: { + // NavigationTiming metrics + page_load_time: results.navigationTiming ? + (results.navigationTiming.loadEventEnd - results.navigationTiming.navigationStart) : + 0, + + // JavaScript CPU time from Profiler + boomerang_javascript_time: Math.floor(filteredProfile.cpuTimeFiltered / 1000), + total_javascript_time: Math.floor(filteredProfile.cpuTime / 1000) + }, + // Raw data + raw: { + cpuprofile: results.profile, + "marks.json": results.marks, + "measures.json": results.measures, + "navigationTiming.json": results.navigationTiming + } + }; + + // + // Marks + // + + // find unique mark names (ignoring :end ones) + const markNames = [...new Set(results.marks.map(mark => mark.name))] + // remove all end calls + .filter(markName => markName.indexOf(":end") === -1); + + markNames.forEach(function(markName) { + const fixedName = markName + // strip boomr: prefix + .replace("boomr:", "") + + // swap all :s with _s + .replace(/:/g, "_") + + // only look at start calls + .replace("_start", ""); + + metrics.metrics["mark_" + fixedName + "_called"] = results.marks.filter(mark => mark.name === markName).length; + }); + + // + // Measures + // + + // find unique meausre names (ignoring :end ones) + const measureNames = [...new Set(results.measures.map(measure => measure.name))]; + + measureNames.forEach(function(measureName) { + const fixedName = measureName + // strip boomr: prefix + .replace("boomr:", "") + + // swap all :s with _s + .replace(/:/g, "_"); + + const measures = results.measures.filter(measure => measure.name === measureName); + + metrics.metrics["measure_" + fixedName + "_called"] = measures.length; + + var measureStat = new Stats(); + + measures.forEach(function(measure) { + measureStat.push(measure.duration); + }); + + metrics.metrics["measure_" + fixedName + "_median"] = Math.ceil(measureStat.median()); + metrics.metrics["measure_" + fixedName + "_sum"] = Math.ceil(measureStat.sum); + }); + + resolve(metrics); + }); +} + +/** + * Runs the Profiler on the specified web page + * + * @param {string} url URL + * @param {function} callback Completion callback + * + * @returns {undefined} + */ +async function loadAndGatherProfile(url) { + return new Promise(async function(resolve, reject) { + console.debug(`Loading ${url}...`); + + let startTime = 0; + let loadTime = 0; + + // launch Chrome + const { chrome, client } = await BrowserUtils.launchChrome(); + + // CDP objects we need to interact with + const { Profiler, Page, Runtime } = client; + + // enable domains to get events + await Page.enable(); + await Profiler.enable(); + + // Set JS profiler sampling resolution to 100 microsecond (default is 1000) + await Profiler.setSamplingInterval({ + interval: config.sampleInterval + }); + + // make sure we launch Chrome within timeout + var timeoutTimer = setTimeout(async function() { + BrowserUtils.closeChrome(client, chrome); + + return reject("Page Load did not complete after " + config.timeout + "ms"); + }, config.timeout); + + // gather profile on load event + client.on("Page.loadEventFired", async function runLoadEvent() { + try { + loadTime = now(); + + // total duration + const duration = Math.floor(loadTime - startTime); + + console.debug(`Page load complete (${duration}ms)`); + + clearTimeout(timeoutTimer); + + // wait a bit + if (config.postLoadWait) { + console.debug(`Sleeping for ${config.postLoadWait}ms...`); + await sleep(config.postLoadWait); + } + + console.debug("Page load complete, gathering profile"); + + // get data from the profiler + const profile = (await Profiler.stop()).profile; + + // + // get data from the page + // + const navigationTiming = (await Runtime.evaluate({ + expression: navTimingJavaScript, + returnByValue: true + })).result.value; + + const marks = (await Runtime.evaluate({ + expression: marksJavaScript, + returnByValue: true + })).result.value; + + const measures = (await Runtime.evaluate({ + expression: measuresJavaScript, + returnByValue: true + })).result.value; + + // close Chrome + await BrowserUtils.closeChrome(client, chrome); + + resolve({ + duration, + profile, + navigationTiming, + marks, + measures + }); + } + catch (e) { + reject(e); + } + }); + + // start the profiler + console.debug("Enabling profiler"); + await Profiler.start(); + + startTime = now(); + + // Navigate! + console.debug(`Navigating to ${url}`); + await Page.navigate({ url }); + }); +} + +// +// Exports +// +exports.run = run; diff --git a/tests/perf/sizer.js b/tests/perf/sizer.js new file mode 100644 index 000000000..2fc2679cf --- /dev/null +++ b/tests/perf/sizer.js @@ -0,0 +1,73 @@ +// +// Imports +// +const path = require("path"); +const util = require("util"); +const fs = require("fs"); + +// promisify'd functions +const stat = util.promisify(fs.stat); +const readdir = util.promisify(fs.readdir); + +// +// Locals +// +const resultsPath = path.join(__dirname, "results"); +const testsPath = path.join(__dirname, ".."); +const rootPath = path.join(testsPath, ".."); +const buildPath = path.join(rootPath, "build"); +const buildPluginsPath = path.join(buildPath, "plugins"); + +// +// Functions +// +/** + * Gathers size stats + * + * @param {object} metrics Metrics recording location + */ +async function run(metrics) { + metrics.size = {}; + + return new Promise(async function(resolve, reject) { + // Boomerang.js sizes + metrics.size.boomerang = {}; + metrics.size.boomerang.debug = await getSize(path.join(buildPath, "boomerang-1.0.0-debug.js")); + metrics.size.boomerang.min = await getSize(path.join(buildPath, "boomerang-1.0.0.min.js")); + metrics.size.boomerang.min_gz = await getSize(path.join(buildPath, "boomerang-1.0.0.min.js.gz")); + + // Plugin sizes + let plugins = (await readdir(buildPluginsPath)).filter(fileName => fileName.match(/\.js$/)); + + metrics.size.plugins = {}; + metrics.size.plugins.count = plugins.length; + + for (let i = 0; i < plugins.length; i++) { + const plugin = plugins[i]; + + metrics.size.plugins[plugin.replace(".min.js", "")] = await getSize(path.join(buildPluginsPath, plugin)); + } + + resolve(); + }); +} + +/** + * Get the size of a file + * + * @param {string} filePath File path + * + * @returns {number} File size (bytes) + */ +async function getSize(filePath) { + return new Promise(async function(resolve, reject) { + const fileStats = await stat(filePath); + + resolve(fileStats.size); + }); +} + +// +// Exports +// +exports.run = run; diff --git a/tests/perf/utils/browser-fetch-marks.js b/tests/perf/utils/browser-fetch-marks.js new file mode 100644 index 000000000..a7e4579a0 --- /dev/null +++ b/tests/perf/utils/browser-fetch-marks.js @@ -0,0 +1,8 @@ +(function() { + return performance.getEntriesByType("mark").map(function(mark) { + return { + name: mark.name, + startTime: mark.startTime + }; + }); +})(); diff --git a/tests/perf/utils/browser-fetch-measures.js b/tests/perf/utils/browser-fetch-measures.js new file mode 100644 index 000000000..dab250165 --- /dev/null +++ b/tests/perf/utils/browser-fetch-measures.js @@ -0,0 +1,9 @@ +(function() { + return performance.getEntriesByType("measure").map(function(measure) { + return { + name: measure.name, + startTime: measure.startTime, + duration: measure.duration + }; + }); +})(); diff --git a/tests/perf/utils/browser-fetch-navtiming.js b/tests/perf/utils/browser-fetch-navtiming.js new file mode 100644 index 000000000..247d8ce9b --- /dev/null +++ b/tests/perf/utils/browser-fetch-navtiming.js @@ -0,0 +1,3 @@ +(function() { + return JSON.parse(JSON.stringify(window.performance.timing)); +})(); diff --git a/tests/protractor-config/base.js b/tests/protractor-config/base.js new file mode 100644 index 000000000..e4fc4b780 --- /dev/null +++ b/tests/protractor-config/base.js @@ -0,0 +1,34 @@ +/*eslint-env node*/ +/*global jasmine*/ + +var config = { + capabilities: {}, + onPrepare: function() { + var reporters = require("jasmine-reporters"); + + jasmine.getEnv().addReporter(new jasmine.ConsoleReporter({ + print: console.log, + showColors: true + })); + + jasmine.getEnv().addReporter(new reporters.JUnitXmlReporter({ + savePath: "tests/results", + consolidate: true, + consolidateAll: true, + useDotNotation: true, + filePrefix: "e2e" + (process.env.BUILD_FLAVOR ? ("-" + process.env.BUILD_FLAVOR) : "") + })); + }, + jasmineNodeOpts: {defaultTimeoutInterval: 45000} +}; + +console.log(JSON.stringify(process.env)); +// webdriver capabilities https://github.com/SeleniumHQ/selenium/wiki/DesiredCapabilities +var capabilities = process.env.CAPABILITIES; + +if (capabilities) { + config.capabilities = JSON.parse(capabilities) || {}; +} + +config.capabilities.acceptInsecureCerts = true; +exports.config = config; diff --git a/tests/protractor-config/chrome.js b/tests/protractor-config/chrome.js new file mode 100644 index 000000000..871100a84 --- /dev/null +++ b/tests/protractor-config/chrome.js @@ -0,0 +1,15 @@ +/*eslint-env node*/ + +base = require("./base.js"); +var config = base.config; + +// List of Chromium capabilities http://chromedriver.chromium.org/capabilities +// List of Chrome cmd line args https://peter.sh/experiments/chromium-command-line-switches/ +var capabilities = { + browserName: "chrome" +}; + +// capabilities in this file will take precedence +config.capabilities = Object.assign({}, config.capabilities || {}, capabilities); + +exports.config = config; diff --git a/tests/protractor-config/chromeheadless.js b/tests/protractor-config/chromeheadless.js new file mode 100644 index 000000000..ebdb1e130 --- /dev/null +++ b/tests/protractor-config/chromeheadless.js @@ -0,0 +1,18 @@ +/*eslint-env node*/ + +base = require("./base.js"); +var config = base.config; + +// List of Chromium capabilities http://chromedriver.chromium.org/capabilities +// List of Chrome cmd line args https://peter.sh/experiments/chromium-command-line-switches/ +var capabilities = { + browserName: "chrome", + chromeOptions: { + args: [ "--headless", "--disable-gpu", "--window-size=1024,768", "--no-sandbox" ] + } +}; + +// capabilities in this file will take precedence +config.capabilities = Object.assign({}, config.capabilities || {}, capabilities); + +exports.config = config; diff --git a/tests/protractor-config/debug.js b/tests/protractor-config/debug.js new file mode 100644 index 000000000..5d0b06578 --- /dev/null +++ b/tests/protractor-config/debug.js @@ -0,0 +1,38 @@ +/*eslint-env node*/ +/*global jasmine*/ + +exports.config = { + onPrepare: function() { + var reporters = require("jasmine-reporters"); + + jasmine.getEnv().addReporter(new jasmine.ConsoleReporter({ + print: console.log, + showColors: true + })); + + jasmine.getEnv().addReporter(new reporters.JUnitXmlReporter({ + savePath: "tests/results", + consolidate: true, + consolidateAll: true, + useDotNotation: true, + filePrefix: "e2e-debug" + })); + }, + // needs to be specified here (instead of in Gruntfile.js) - grunt-protractor-runner seems + // to have an issue passing in args + capabilities: { + browserName: "chrome", + chromeOptions: { + args: [ + "--headless", + "--disable-gpu", + "--window-size=1024,768", + "--remote-debugging-port=9222" + ] + }, + loggingPrefs: { + "driver": "INFO", + "browser": "INFO" + } + } +}; diff --git a/tests/protractor-config/edge.js b/tests/protractor-config/edge.js new file mode 100644 index 000000000..62ce5ef01 --- /dev/null +++ b/tests/protractor-config/edge.js @@ -0,0 +1,14 @@ +/*eslint-env node*/ + +base = require("./base.js"); +var config = base.config; + +// Pre-Chromium Edge +var capabilities = { + browserName: "MicrosoftEdge" +}; + +// capabilities in this file will take precedence +config.capabilities = Object.assign({}, config.capabilities || {}, capabilities); + +exports.config = config; diff --git a/tests/protractor-config/firefox.js b/tests/protractor-config/firefox.js new file mode 100644 index 000000000..600030845 --- /dev/null +++ b/tests/protractor-config/firefox.js @@ -0,0 +1,17 @@ +/*eslint-env node*/ + +base = require("./base.js"); +var config = base.config; + +// List of Firefox capabilities https://developer.mozilla.org/en-US/docs/Web/WebDriver/Capabilities/firefoxOptions +var capabilities = { + browserName: "firefox" + // ,"moz:firefoxOptions": { + // args: [ ] + // } +}; + +// capabilities in this file will take precedence +config.capabilities = Object.assign({}, config.capabilities || {}, capabilities); + +exports.config = config; diff --git a/tests/protractor-config/firefoxheadless.js b/tests/protractor-config/firefoxheadless.js new file mode 100644 index 000000000..76959b956 --- /dev/null +++ b/tests/protractor-config/firefoxheadless.js @@ -0,0 +1,16 @@ +/*eslint-env node*/ + +base = require("./base.js"); +var config = base.config; + +var capabilities = { + browserName: "firefox", + "moz:firefoxOptions": { + args: [ "--headless" ] + } +}; + +// capabilities in this file will take precedence +config.capabilities = Object.assign({}, config.capabilities || {}, capabilities); + +exports.config = config; diff --git a/tests/protractor-config/ie.js b/tests/protractor-config/ie.js new file mode 100644 index 000000000..99494630e --- /dev/null +++ b/tests/protractor-config/ie.js @@ -0,0 +1,13 @@ +/*eslint-env node*/ + +base = require("./base.js"); +var config = base.config; + +var capabilities = { + browserName: "internet explorer" +}; + +// capabilities in this file will take precedence +config.capabilities = Object.assign({}, config.capabilities || {}, capabilities); + +exports.config = config; diff --git a/tests/protractor-config/multi.js b/tests/protractor-config/multi.js new file mode 100644 index 000000000..2873bcd1c --- /dev/null +++ b/tests/protractor-config/multi.js @@ -0,0 +1,33 @@ +/*eslint-env node*/ +/*global jasmine*/ + +exports.config = { + onPrepare: function() { + var reporters = require("jasmine-reporters"); + + jasmine.getEnv().addReporter(new jasmine.ConsoleReporter({ + print: console.log, + showColors: true + })); + + jasmine.getEnv().addReporter(new reporters.JUnitXmlReporter({ + savePath: "tests/results", + consolidate: true, + consolidateAll: true, + useDotNotation: true, + filePrefix: "e2e" + (process.env.BUILD_FLAVOR ? ("-" + process.env.BUILD_FLAVOR) : "") + })); + }, + // needs to be specified here (instead of in Gruntfile.js) - grunt-protractor-runner seems + // to have an issue passing in args + multiCapabilities: [{ + browserName: "firefox", + "moz:firefoxOptions": { + args: [ ] + } + }, + { + browserName: "chrome" + } + ] +}; diff --git a/tests/protractor-config/safari.js b/tests/protractor-config/safari.js new file mode 100644 index 000000000..b816c082b --- /dev/null +++ b/tests/protractor-config/safari.js @@ -0,0 +1,14 @@ +/*eslint-env node*/ + +base = require("./base.js"); +var config = base.config; + +var capabilities = { + // technologyPreview: true, // uncomment to use Safari Technology Preview + browserName: "safari" +}; + +// capabilities in this file will take precedence +config.capabilities = Object.assign({}, config.capabilities || {}, capabilities); + +exports.config = config; diff --git a/tests/protractor.config.edge.js b/tests/protractor.config.edge.js new file mode 100644 index 000000000..8469d81c1 --- /dev/null +++ b/tests/protractor.config.edge.js @@ -0,0 +1,26 @@ +/* eslint-env node */ +/* global jasmine */ + +exports.config = { + onPrepare: function() { + var reporters = require("jasmine-reporters"); + + jasmine.getEnv().addReporter(new jasmine.ConsoleReporter({ + print: console.log, + showColors: true + })); + + jasmine.getEnv().addReporter(new reporters.JUnitXmlReporter({ + savePath: "tests/results", + consolidate: true, + consolidateAll: true, + useDotNotation: true, + filePrefix: "e2e" + (process.env.BUILD_FLAVOR ? ("-" + process.env.BUILD_FLAVOR) : "") + })); + }, + // needs to be specified here (instead of in Gruntfile.js) - grunt-protractor-runner seems + // to have an issue passing in args + capabilities: { + browserName: "MicrosoftEdge" + } +}; diff --git a/tests/protractor.config.firefoxheadless.js b/tests/protractor.config.firefoxheadless.js new file mode 100644 index 000000000..1940ad1bb --- /dev/null +++ b/tests/protractor.config.firefoxheadless.js @@ -0,0 +1,29 @@ +/* eslint-env node */ +/* global jasmine */ + +exports.config = { + onPrepare: function() { + var reporters = require("jasmine-reporters"); + + jasmine.getEnv().addReporter(new jasmine.ConsoleReporter({ + print: console.log, + showColors: true + })); + + jasmine.getEnv().addReporter(new reporters.JUnitXmlReporter({ + savePath: "tests/results", + consolidate: true, + consolidateAll: true, + useDotNotation: true, + filePrefix: "e2e" + (process.env.BUILD_FLAVOR ? ("-" + process.env.BUILD_FLAVOR) : "") + })); + }, + // needs to be specified here (instead of in Gruntfile.js) - grunt-protractor-runner seems + // to have an issue passing in args + capabilities: { + browserName: "firefox", + "moz:firefoxOptions": { + args: [ "--headless" ] + } + } +}; diff --git a/tests/protractor.config.ie.js b/tests/protractor.config.ie.js new file mode 100644 index 000000000..a911c6fc3 --- /dev/null +++ b/tests/protractor.config.ie.js @@ -0,0 +1,26 @@ +/* eslint-env node */ +/* global jasmine */ + +exports.config = { + onPrepare: function() { + var reporters = require("jasmine-reporters"); + + jasmine.getEnv().addReporter(new jasmine.ConsoleReporter({ + print: console.log, + showColors: true + })); + + jasmine.getEnv().addReporter(new reporters.JUnitXmlReporter({ + savePath: "tests/results", + consolidate: true, + consolidateAll: true, + useDotNotation: true, + filePrefix: "e2e" + (process.env.BUILD_FLAVOR ? ("-" + process.env.BUILD_FLAVOR) : "") + })); + }, + // needs to be specified here (instead of in Gruntfile.js) - grunt-protractor-runner seems + // to have an issue passing in args + capabilities: { + browserName: "internet explorer" + } +}; diff --git a/tests/protractor.config.safari.js b/tests/protractor.config.safari.js new file mode 100644 index 000000000..a406a38e7 --- /dev/null +++ b/tests/protractor.config.safari.js @@ -0,0 +1,27 @@ +/* eslint-env node */ +/* global jasmine */ + +exports.config = { + onPrepare: function() { + var reporters = require("jasmine-reporters"); + + jasmine.getEnv().addReporter(new jasmine.ConsoleReporter({ + print: console.log, + showColors: true + })); + + jasmine.getEnv().addReporter(new reporters.JUnitXmlReporter({ + savePath: "tests/results", + consolidate: true, + consolidateAll: true, + useDotNotation: true, + filePrefix: "e2e" + (process.env.BUILD_FLAVOR ? ("-" + process.env.BUILD_FLAVOR) : "") + })); + }, + // needs to be specified here (instead of in Gruntfile.js) - grunt-protractor-runner seems + // to have an issue passing in args + capabilities: { + // technologyPreview: true, // uncomment to use Safari Technology Preview + browserName: "safari" + } +}; diff --git a/tests/results/.gitignore b/tests/results/.gitignore new file mode 100644 index 000000000..f59ec20aa --- /dev/null +++ b/tests/results/.gitignore @@ -0,0 +1 @@ +* \ No newline at end of file diff --git a/tests/server/.eslintrc b/tests/server/.eslintrc new file mode 100644 index 000000000..10d223883 --- /dev/null +++ b/tests/server/.eslintrc @@ -0,0 +1,5 @@ +{ + "env": { + "node": true + } +} diff --git a/tests/server/app.js b/tests/server/app.js new file mode 100644 index 000000000..b41dcb353 --- /dev/null +++ b/tests/server/app.js @@ -0,0 +1,260 @@ +/* eslint-env node */ + +// +// Imports +// +var path = require("path"); +var fs = require("fs"); +var readline = require("readline"); +var express = require("express"); +var compress = require("compression"); +var http = require("http"); +var https = require("https"); + +// +// Load env.json +// +var envFile = path.resolve(path.join(__dirname, "env.json")); + +if (!fs.existsSync(envFile)) { + throw new Error("[APP] Please create " + envFile + ". There's a env.json.sample in the same dir."); +} + +// load JSON +var env = require(envFile); + +// +// Start HTTP server / Express +// +var wwwRoot = env.www; + +if (wwwRoot.indexOf("/") !== 0) { + wwwRoot = path.join(__dirname, "..", "..", wwwRoot); +} + +if (!fs.existsSync(wwwRoot)) { + wwwRoot = path.join(__dirname, ".."); +} + +var credentials = {}; + +try { + var privatekey = fs.readFileSync(path.join(__dirname, env.privatekey), "utf8"); + var certificate = fs.readFileSync(path.join(__dirname, env.certificate), "utf8"); + + credentials = { + key: privatekey, + cert: certificate + }; + console.log("[APP] Found credentials, key: " + env.privatekey + " cert: " + env.certificate); +} +catch (e) { + console.log("[APP] Credentials not found ", e); +} + +var app = express(); + +// ensure content is compressed +app.use(compress()); + +// +// Quick Handlers +// +function respond204(req, res) { + res.status(204).send(); +} + +function respond301(req, res) { + var q = require("url").parse(req.url, true).query; + var file = q.file; + + res.set("Access-Control-Allow-Origin", "*"); + res.redirect(301, file); +} + +function respond302(req, res) { + var q = require("url").parse(req.url, true).query; + var to = q.to || "/blackhole"; + + res.redirect(to); +} + +function respond500(req, res) { + res.status(500).send(); +} + +function dropRequest(req, res) { + // drop request, no http response + req.socket.destroy(); +} + +// +// Routes +// + +// Favicon empty response +app.get("/favicon.ico", respond204); + +// /beacon, /beacon/no-op and /blackhole: returns a 204 +app.get("/beacon", respond204); +app.post("/beacon", respond204); +app.get("/beacon/no-op", respond204); +app.post("/beacon/no-op", respond204); +app.get("/blackhole", respond204); +app.post("/blackhole", respond204); +app.get("/blackhole/*", respond204); +app.post("/blackhole/*", respond204); + +// /delay - delays a response +app.get("/delay", require("./route-delay")); +app.post("/delay", require("./route-delay")); + +// /redirect - 301 redirects +app.get("/redirect", respond301); +app.post("/redirect", respond301); + +// /redirect - 302 +app.get("/302", respond302); + +// /500 - Internal Server Error +app.get("/500", respond500); +app.post("/500", respond500); + +// /chunked +app.get("/chunked", require("./route-chunked")); +app.post("/chunked", require("./route-chunked")); + +// /json - JSON output +app.get("/json", require("./route-json")); +app.post("/json", require("./route-json")); + +// /drop +app.get("/drop", dropRequest); +app.post("/drop", dropRequest); + +app.get("/pages/34-bw/support/images/*", function(req, res, next) { + // Values copied from plugins/bw.js + images = [ + { name: "image-0.png", size: 11773, timeout: 1400 }, + { name: "image-1.png", size: 40836, timeout: 1200 }, + { name: "image-2.png", size: 165544, timeout: 1300 }, + { name: "image-3.png", size: 382946, timeout: 1500 }, + { name: "image-4.png", size: 1236278, timeout: 1200 }, + { name: "image-5.png", size: 4511798, timeout: 1200 }, + { name: "image-6.png", size: 9092136, timeout: 1200 }, + { name: "image-l.gif", size: 35, timeout: 1000 } + ]; + reqimg = req.params[0]; + matches = images.filter(function(i) { + return i.name === reqimg; + }); + + if (matches.length !== 1) { + return res.status(404).send(); + } + + sz = matches[0].size; + + if (reqimg === "image-l.gif") { + img = new Uint8Array([0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x21, 0xf9, + 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00]); + res.type("gif"); + } + else { + imgheader = new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10 ]); + imgbody0 = new Uint8Array([0, 0, 0, 13, 73, 72, 68, 82, 0, 0, 0, 5, 0, 0, 0, 5, 8, 6, 0, 0, 0, 141, + 111, 38, 229, 0, 0, 0, 28, 73, 68, 65, 84, 8, 215]); + imgbody1 = new Uint8Array(sz).fill(5); + imgbody = new Uint8Array(imgbody0.length + imgbody1.length); + imgbody.set(imgbody0, 0); + imgbody.set(imgbody1, imgbody0.length); + img = new Uint8Array(imgheader.length + imgbody.length); + img.set(imgheader, 0); + img.set(imgbody, imgheader.length); + res.type("png"); + } + + res.set("Cache-Control", "no-store"); + res.send(new Buffer(img, "binary")); +}); + +// load in any additional routes +if (fs.existsSync(path.resolve(path.join(__dirname, "routes.js")))) { + require("./routes")(app); +} + +// for every GET, look for a file with the same name appended with ".headers" +// if found, parse the headers and write them on the response +// whether found or not, let the req/res pass through with next() +app.get("/*", function(req, res, next) { + var fullPath = path.resolve(path.join(wwwRoot, req.url)); + var qIndex = fullPath.indexOf("?"); + + if (qIndex > -1) { + fullPath = fullPath.substring(0, qIndex); + } + + var headersFilePath = fullPath + ".headers"; + var input = fs.createReadStream(headersFilePath); + + input.on("error", function() { + next(); + }); + input.on("open", function() { + var headers = {}; + var lineReader = readline.createInterface({ input: input }); + + lineReader.on("line", function(line) { + var colon = ":"; + var colonIndex = line.indexOf(colon); + var name = line.substring(0, colonIndex).trim(); + + headers[name] = headers[name] || []; + headers[name].push(line.substring(colonIndex + colon.length).trim()); + }); + lineReader.on("close", function(line) { + Object.keys(headers).forEach(function(name) { + res.setHeader(name, headers[name]); + }); + next(); + }); + }); +}); + +// all static content follows afterwards +/* eslint dot-notation:0 */ + +// do not cache certain static resources +app.use("/assets", express.static(path.join(wwwRoot, "/assets"), { + etag: false, + lastModified: false, + index: false, + cacheControl: false, + setHeaders: function(res, _path) { + res.setHeader("Cache-Control", "no-cache, no-store"); + } +})); + +app.use(express.static(wwwRoot)); + +// this needs to be before `app.listen(...)` +require("express-middleware-server-timing")(app); + +// listen +var port = process.env.PORT || env.port; +var scheme = (process.argv.length >= 2 && process.argv[2]) || "http"; + +if (scheme === "https" && credentials.key && credentials.cert) { + var httpsServer = https.createServer(credentials, app); + + httpsServer.listen(port, function() { + console.log("[APP] HTTPS Server starting on port " + port + " for " + wwwRoot); + }); +} +else { + var httpServer = http.createServer(app); + + httpServer.listen(port, function() { + console.log("[APP] HTTP Server starting on port " + port + " for " + wwwRoot); + }); +} diff --git a/tests/server/certs/boomerang-test.local.crt b/tests/server/certs/boomerang-test.local.crt new file mode 100644 index 000000000..982f1f930 --- /dev/null +++ b/tests/server/certs/boomerang-test.local.crt @@ -0,0 +1,29 @@ +-----BEGIN CERTIFICATE----- +MIIE+zCCAuOgAwIBAgIJANdiAUc2nG1gMA0GCSqGSIb3DQEBCwUAMB8xHTAbBgNV +BAMMFGJvb21lcmFuZy10ZXN0LmxvY2FsMB4XDTIxMDMxNjE0MDEzMFoXDTMwMTIx +NDE0MDEzMFowHzEdMBsGA1UEAwwUYm9vbWVyYW5nLXRlc3QubG9jYWwwggIiMA0G +CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDF4W1p0GGssYj/NywCgCd2bCXknq2u +TSVYJqoZkMYMnlw1GBMe8kd/A7aB8UiUUgfuFaqiD75WdxixK/t8GaiQytPVgA8P +isRTwQv6Vpm+ZCsH0G1MTjyuyx0bLJwFXkBDhraFINy5RXz54Yma7rB5OJcNKGdH +9+s1yKfPHobRC0FIiX7qewm0ZTANxKmREkRRB60CbHXDuueJGuXX3bjpM8O2LXUc +W8mEO/8BCVOIfFIgOJq1wS4EkTWp/y6yiO95sZGatmewd7uDo42B+DFTaid6xqoe +SQ5c+zSn+/ld25/c4iZpWF3yyt2pok4LXlQuSoFQCffD6hOhNXahVGH54qVN38TL +sIU4DcIendlnEyNGsdfaExcjyLdoVIqLNKSWXRAJZW/56GU6nPgkNr3q5iC9927E +wVxiEnIC2yD5RBObL6sLW5+PsbThELWDbS56SvRlLa0njewpbYxBZ4VUWkqSPqUN +5i9waVBabdGMnxvmCVmPKZos2a9VWVsEY7jBxSMzsjqF7+/RJylOj/H65mAXJ6e/ +p2hTa+Q2qiPwQeShIUJUu7778/Uo8pLIO4QDBoC3ZaYGovrAoniCxnwHhCbNZZvu +UrykypVd1CmtotxLJWhU/23I4tU3B8tHfYO0kyTjHvNv5P2CHDlhTlvkE5zM0uqN +Ce0QJ2XrWd+MHQIDAQABozowODA2BgNVHREELzAtghRib29tZXJhbmctdGVzdC5s +b2NhbIIVYm9vbWVyYW5nLXRlc3QyLmxvY2FsMA0GCSqGSIb3DQEBCwUAA4ICAQCn +BAHnRha4coSA+uaqaE22a6jYF2P4jff7TyRQpvaI7vvVq5y1hvch0hNzoqs2zSXC +tl/K5eS7ZOgdBPxQLMs035/I4+D1DrKHBQ2OLqzHSBq5u6pZb2OOgHfZ2AqIJEKh +A+ejoM/yC5DNVOPTi0mQJTNll7yAeigWEemjS+jgur3OzZijnMm0vwk3+oB6HAbI +qO4rYvolYSwDrwhFoMxjFGR9Z7sNGsV+Vh04xjGqgO8tAz5a08tSHSf5xxc2Owlm +1PWL3W//Yw4LY3p7/podV+/v2B9csalTPIoQLkTMTutfy8XElExzBr09eKLhqHjX +9faxjdwI30qM64kelHJUAa3KtdOsYmeRXKLevnHlg7Rk1OLwwzHrBOiYCb/tjV5n +tHddbWMedtqe6tUx0uBDmb+7rB7+JI998Cp68nPjGIHvM1OumOXJFU84pFrLM0Jo +eRRwP8yoydcmxRv63lEhEfQoEM1opK5l4wemK6n22jOXOuL2vsriMjJ9Xffz9BZm +MN31/4xqipi76lT46TWcVf3FnQD8Hv2rRTRwy78lBiRwulJsVmjc1UyY/3o5Ey6L +lt9CJ7zPWqrUyDoAW6xnZ3cjIDsAGUpMQ/skgNCmBJpVAWJdcQT4MSXiANFLBMTg +hyd5XOFC6m3UT/EnsaRZeBb0lvuioWejzrP+KjDl3A== +-----END CERTIFICATE----- diff --git a/tests/server/certs/boomerang-test.local.pem b/tests/server/certs/boomerang-test.local.pem new file mode 100644 index 000000000..77d6ce5bb --- /dev/null +++ b/tests/server/certs/boomerang-test.local.pem @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQDF4W1p0GGssYj/ +NywCgCd2bCXknq2uTSVYJqoZkMYMnlw1GBMe8kd/A7aB8UiUUgfuFaqiD75Wdxix +K/t8GaiQytPVgA8PisRTwQv6Vpm+ZCsH0G1MTjyuyx0bLJwFXkBDhraFINy5RXz5 +4Yma7rB5OJcNKGdH9+s1yKfPHobRC0FIiX7qewm0ZTANxKmREkRRB60CbHXDuueJ +GuXX3bjpM8O2LXUcW8mEO/8BCVOIfFIgOJq1wS4EkTWp/y6yiO95sZGatmewd7uD +o42B+DFTaid6xqoeSQ5c+zSn+/ld25/c4iZpWF3yyt2pok4LXlQuSoFQCffD6hOh +NXahVGH54qVN38TLsIU4DcIendlnEyNGsdfaExcjyLdoVIqLNKSWXRAJZW/56GU6 +nPgkNr3q5iC9927EwVxiEnIC2yD5RBObL6sLW5+PsbThELWDbS56SvRlLa0njewp +bYxBZ4VUWkqSPqUN5i9waVBabdGMnxvmCVmPKZos2a9VWVsEY7jBxSMzsjqF7+/R +JylOj/H65mAXJ6e/p2hTa+Q2qiPwQeShIUJUu7778/Uo8pLIO4QDBoC3ZaYGovrA +oniCxnwHhCbNZZvuUrykypVd1CmtotxLJWhU/23I4tU3B8tHfYO0kyTjHvNv5P2C +HDlhTlvkE5zM0uqNCe0QJ2XrWd+MHQIDAQABAoICAQCqiw4Ei28TKnObGk7Y3OCm +BLFn54Zi2eRco/4oSIkYaQepTtVz73XFN0r9lRlrk4N4I75LLGxsEvr5/6EgJPEN +0XFe6Z6RU5S/ZnrPSAH7gFNBydXZVPih4TZ1rC3Pxpp2WE1IUblKCGSc7E/vctYv +s6Ej2vWgpkBWu1g9ft7Mnfmm7bGvn1kO28LdquhFusucVpQ6qYWZqqVQmhkLwfFT +M6Tyhx0O0uDMEfVGTGZtFFCnqoFUMmlZ0FjC6cayYqiaZnrKL3kQMzEuVDBqmqDQ +doYOH9iQf4BcjLimKtmRle6irVpTorBdx4/hWpwO2j6NIvNuKxkaBczjPLPAeXxy +IBT++ZptHB//RY/Kjk0+TVaHoeAi9KWvhMAHJjBWbejC7TMwog3W5cna3nkuuOU6 +u+rJMC4H4u8/AMscd2h85FbtN7pAPaWR1Wa1YgmYSi60QTDR8UdT4QI2IMUes00m +ubXQZgItpAkiAaX37ItdOllz21aWktNdgk69g4bj9PhDRLd/KjN810ihEmI5tHQ4 +DmuvUJCyNtDjz/k/C3W4+8Kb2GSvUyZu3PZrYXoN/TJaxpBiPLQUuux8U7XUPV8j +qAWlvFVHP7PKL2rF2GXvLZaflJOPchut3XtuZ/LRSFfhl8BDu1Dm8M3TkAnWXlk2 +Y9mkkWquK/Z8WnYTm5L3gQKCAQEA62XC5RcfB4Rp35UDyZNwbQzPd0TAOz/KDOXw +UEeX6SJEj/EB1CKgYFhg9SlfxYK+U9ZSqGq/o52ps6wt75rJn337pItkqK2o+Hgs +mJRo7w8ygy6DnvTj9FKVhPhERjf83dWWzWZhIF0mdudHSWjUOwBkOzhTxpmUl8Fx +dd4I0v7Z616mqFwXciHP2iSzXAc2CR4h7faQI1PEgqEA/QCd4kOBR5WBhmA+fX+p +F2cc06DknDGldXxKHWlMJJnhUmGD+I2tm5+tTIpn/bXgLtOppNr+kvs6Y8wa0E/H +lnasFKfB5Bgai4BWyXPeAb7XReW0+fuZcPL/236ZXIPte7/8PQKCAQEA1zMS/MZq +888TJXueMawNN5llYhas6x+XN6X1djF2OVez5RHOEbpypZWCw7supxpMV6vvvBE4 +7hY9arJkL1GuoXZm5l3UmAq1h5wwTYxQfxi/T+o7/sx5GSKLXFCRccLGRCPl7+4a +6X8aJl6I1MLQquPxts3sadU0hmJx0CIgEJwQTUaEKBLt8kGmjMAYVUC0Ty3aia7s +ywdvLSbzUdgbCmQ+ShoS4k/Cuj5LRJTq2SvgZSemEXAhdh755B0rCB5xeWGc+zcu +SaDQPaWVoCuN1hpwcKMQ2xjpi4GPiNIR9zuUoWGMs3/Z1TuskgkOgy5doiJswLNm +jcsd/sWE61dtYQKCAQAQdS7pIXKz7dgRH7QCFeLYXPrsVcwy2ETiHfltQ3QlgpXU +kn8UO8+GrOcqfNgLURbJecSRrdT0NYO14iXcCLBsDDzZxj0lZe7YYF6N1GJ5xFA9 +x7TAGlMNs/BsOYdceXGeeN5wQeYsgxa7XXQod8HO1jorwcZprjcrXQT3vqxTRCos +QJPaVYX2HtMPZ116464teQPaRVDSh3rHv0X4amCffyS8woEpyfTUhWZnEnh7e1ZA +bWAqFKSB5ejC7ldIkTTO3ejzZniRT2Px4xNncrj7o6wEzrfIXm7JfapwpJ9nWks+ +DUld03093HH7IUKfym2axLJ3qSWmTPWf+EFUU5npAoIBAQDUk200KURJJsqBAkXY +R16fB3YwmLmjYQddPyDj6iQgrNPVeesXdBgAHVqYwnRpOP8ks4ETQ+f6uBlfJmz0 +mTlp4a3v7cwtjJaZKN5GHE7/3ZBN/tHY9l7BR3BmdmfV+lMjyFpA0fHfqWBJeqSr +lu3NgXGp5Gyedq91+BT5RlxqS9+GlhvCAkDuR4+gBbmDrXDvrhnsW9EpFWTrM0i0 +FkdP0on9ZRg2Whe/Jf3v9zKt6Du3cJ0JKLzFFABBJ9b0JC5ves85EN82eBZ8bu8I +0z0+u8tKWKoz473/wrHos6pvkHlXmScFwUuK6XGkqDGfv9Nab6sCB7VMZ9JvcunE +GA7BAoIBABqaiXN/o87OHOhuDm4tvj3JTUOy/zGawLgC4J2e3rW/MU2fKYJK09TZ +pq/Ny4qo55CKGC+Y4WgxDdyxGyVLJsn7CK5HggYgVnlHu2dovPTuMXKLm4D58xOH +7JmypiWmxw9oPBn1FxMjh9XM6uLupBL+XYU0tDjXoF3+LFJApWJqgL9UMTFlFhEy +JWn1tRLnNUfKNjkX6T7u5dpkTBNXzU/ki5vQ0p2lUq35l0VDAfExBm4DPzGb0Xw8 +Aa+xmBjB0V3frFRTSOIMTAGKF19E7MmUXwyyu5nyP5nHTuviUSUZUDLkVjm/ABz/ +taRz4D+vFd0kxtWYXHISCHPabXgDoBE= +-----END PRIVATE KEY----- diff --git a/tests/server/config/05-angular-15-delayed-boomerang.js b/tests/server/config/05-angular-15-delayed-boomerang.js new file mode 100644 index 000000000..3e32da54e --- /dev/null +++ b/tests/server/config/05-angular-15-delayed-boomerang.js @@ -0,0 +1,18 @@ +BOOMR_configt=new Date().getTime(); +BOOMR.addVar({"h.t":{{H_T}},"h.cr":"{{H_CR}}"}); +BOOMR.init({ + Angular: { + enabled: true + }, + instrument_xhr: true, + autorun: false, + PageParams: { + pageGroups: [ + { + type: "Regexp", + parameter1: "/pages/", + parameter2: "MATCH" + } + ] + } +}); diff --git a/tests/server/config/05-angular-15-delayed-boomerang.json b/tests/server/config/05-angular-15-delayed-boomerang.json new file mode 100644 index 000000000..95f8178c9 --- /dev/null +++ b/tests/server/config/05-angular-15-delayed-boomerang.json @@ -0,0 +1,19 @@ +{ + "h.t": {{H_T}}, + "h.cr": "{{H_CR}}", + "h.d": "{{DOMAIN}}", + "Angular": { + "enabled": true + }, + "instrument_xhr": true, + "autorun": false, + "PageParams": { + "pageGroups": [ + { + "type": "Regexp", + "parameter1": "/pages/", + "parameter2": "MATCH" + } + ] + } +} diff --git a/tests/server/config/08-ember-15-delayed-boomerang.js b/tests/server/config/08-ember-15-delayed-boomerang.js new file mode 100644 index 000000000..0a0bcca8d --- /dev/null +++ b/tests/server/config/08-ember-15-delayed-boomerang.js @@ -0,0 +1,18 @@ +BOOMR_configt=new Date().getTime(); +BOOMR.addVar({"h.t":{{H_T}},"h.cr":"{{H_CR}}"}); +BOOMR.init({ + Ember: { + enabled: true + }, + instrument_xhr: true, + autorun: false, + PageParams: { + pageGroups: [ + { + type: "Regexp", + parameter1: "/pages/", + parameter2: "MATCH" + } + ] + } +}); diff --git a/tests/server/config/08-ember-15-delayed-boomerang.json b/tests/server/config/08-ember-15-delayed-boomerang.json new file mode 100644 index 000000000..bd56377ba --- /dev/null +++ b/tests/server/config/08-ember-15-delayed-boomerang.json @@ -0,0 +1,19 @@ +{ + "h.t": {{H_T}}, + "h.cr": "{{H_CR}}", + "h.d": "{{DOMAIN}}", + "Ember": { + "enabled": true + }, + "instrument_xhr": true, + "autorun": false, + "PageParams": { + "pageGroups": [ + { + "type": "Regexp", + "parameter1": "/pages/", + "parameter2": "MATCH" + } + ] + } +} diff --git a/tests/server/config/09-backbone-15-delayed-boomerang.js b/tests/server/config/09-backbone-15-delayed-boomerang.js new file mode 100644 index 000000000..11a10591d --- /dev/null +++ b/tests/server/config/09-backbone-15-delayed-boomerang.js @@ -0,0 +1,18 @@ +BOOMR_configt=new Date().getTime(); +BOOMR.addVar({"h.t":{{H_T}},"h.cr":"{{H_CR}}"}); +BOOMR.init({ + Backbone: { + enabled: true + }, + instrument_xhr: true, + autorun: false, + PageParams: { + pageGroups: [ + { + type: "Regexp", + parameter1: "/pages/", + parameter2: "MATCH" + } + ] + } +}); diff --git a/tests/server/config/09-backbone-15-delayed-boomerang.json b/tests/server/config/09-backbone-15-delayed-boomerang.json new file mode 100644 index 000000000..36b8138c7 --- /dev/null +++ b/tests/server/config/09-backbone-15-delayed-boomerang.json @@ -0,0 +1,19 @@ +{ + "h.t": {{H_T}}, + "h.cr": "{{H_CR}}", + "h.d": "{{DOMAIN}}", + "Backbone": { + "enabled": true + }, + "instrument_xhr": true, + "autorun": false, + "PageParams": { + "pageGroups": [ + { + "type": "Regexp", + "parameter1": "/pages/", + "parameter2": "MATCH" + } + ] + } +} diff --git a/tests/server/config/12-react-15-delayed-boomerang.js b/tests/server/config/12-react-15-delayed-boomerang.js new file mode 100644 index 000000000..386898c15 --- /dev/null +++ b/tests/server/config/12-react-15-delayed-boomerang.js @@ -0,0 +1,18 @@ +BOOMR_configt=new Date().getTime(); +BOOMR.addVar({"h.t":{{H_T}},"h.cr":"{{H_CR}}"}); +BOOMR.init({ + History: { + enabled: true + }, + instrument_xhr: true, + autorun: false, + PageParams: { + pageGroups: [ + { + type: "Regexp", + parameter1: "/pages/", + parameter2: "MATCH" + } + ] + } +}); diff --git a/tests/server/config/12-react-15-delayed-boomerang.json b/tests/server/config/12-react-15-delayed-boomerang.json new file mode 100644 index 000000000..dfeef6523 --- /dev/null +++ b/tests/server/config/12-react-15-delayed-boomerang.json @@ -0,0 +1,19 @@ +{ + "h.t": {{H_T}}, + "h.cr": "{{H_CR}}", + "h.d": "{{DOMAIN}}", + "History": { + "enabled": true + }, + "instrument_xhr": true, + "autorun": false, + "PageParams": { + "pageGroups": [ + { + "type": "Regexp", + "parameter1": "/pages/", + "parameter2": "MATCH" + } + ] + } +} diff --git a/tests/server/config/21-continuity-32-delayed-boomerang.js b/tests/server/config/21-continuity-32-delayed-boomerang.js new file mode 100644 index 000000000..a4d44c7c7 --- /dev/null +++ b/tests/server/config/21-continuity-32-delayed-boomerang.js @@ -0,0 +1,18 @@ +BOOMR_configt=new Date().getTime(); +BOOMR.addVar({"h.t":{{H_T}},"h.cr":"{{H_CR}}"}); +BOOMR.init({ + Continuity: { + enabled: true, + afterOnloadMaxLength: 60000, + afterOnloadMinWait: 1000 + }, + PageParams: { + pageGroups: [ + { + type: "Regexp", + parameter1: "/pages/", + parameter2: "MATCH" + } + ] + } +}); diff --git a/tests/server/config/21-continuity-32-delayed-boomerang.json b/tests/server/config/21-continuity-32-delayed-boomerang.json new file mode 100644 index 000000000..5f4315689 --- /dev/null +++ b/tests/server/config/21-continuity-32-delayed-boomerang.json @@ -0,0 +1,19 @@ +{ + "h.t": {{H_T}}, + "h.cr": "{{H_CR}}", + "h.d": "{{DOMAIN}}", + "Continuity": { + "enabled": true, + "afterOnloadMaxLength": 60000, + "afterOnloadMinWait": 1000 + }, + "PageParams": { + "pageGroups": [ + { + "type": "Regexp", + "parameter1": "/pages/", + "parameter2": "MATCH" + } + ] + } +} diff --git a/tests/server/config/22-early-14-spa-missed-xhr-off.js b/tests/server/config/22-early-14-spa-missed-xhr-off.js new file mode 100644 index 000000000..1deb0c14b --- /dev/null +++ b/tests/server/config/22-early-14-spa-missed-xhr-off.js @@ -0,0 +1,30 @@ +BOOMR_configt=new Date().getTime(); +BOOMR.addVar({"h.t":{{H_T}},"h.cr":"{{H_CR}}"}); +BOOMR.init({ + "autorun": false, + "instrument_xhr": false, + "History": { + "enabled": true + }, + "Early": { + "enabled": true + }, + "ResourceTiming": { + "enabled": true, + "splitAtPath": true + }, + "Errors": { + "enabled": true, + "monitorTimeout": true, + "monitorEvents": true, + "maxErrors": 25, + "sendAfterOnload": true, + "sendInterval": 1000 + }, + "Continuity": { + "enabled": true + }, + "PageParams": { + "xhr": "none" + } +}); diff --git a/tests/server/config/22-early-14-spa-missed-xhr-off.json b/tests/server/config/22-early-14-spa-missed-xhr-off.json new file mode 100644 index 000000000..b860ebe4c --- /dev/null +++ b/tests/server/config/22-early-14-spa-missed-xhr-off.json @@ -0,0 +1,32 @@ +{ + "h.t": {{H_T}}, + "h.cr": "{{H_CR}}", + "h.d": "{{DOMAIN}}", + + "autorun": false, + "instrument_xhr": false, + "History": { + "enabled": true + }, + "Early": { + "enabled": true + }, + "ResourceTiming": { + "enabled": true, + "splitAtPath": true + }, + "Errors": { + "enabled": true, + "monitorTimeout": true, + "monitorEvents": true, + "maxErrors": 25, + "sendAfterOnload": true, + "sendInterval": 1000 + }, + "Continuity": { + "enabled": true + }, + "PageParams": { + "xhr": "none" + } +} diff --git a/tests/server/config/22-early-14-spa-missed-xhr-on.js b/tests/server/config/22-early-14-spa-missed-xhr-on.js new file mode 100644 index 000000000..212f9ed43 --- /dev/null +++ b/tests/server/config/22-early-14-spa-missed-xhr-on.js @@ -0,0 +1,30 @@ +BOOMR_configt=new Date().getTime(); +BOOMR.addVar({"h.t":{{H_T}},"h.cr":"{{H_CR}}"}); +BOOMR.init({ + "autorun": false, + "instrument_xhr": true, + "History": { + "enabled": true + }, + "Early": { + "enabled": true + }, + "ResourceTiming": { + "enabled": true, + "splitAtPath": true + }, + "Errors": { + "enabled": true, + "monitorTimeout": true, + "monitorEvents": true, + "maxErrors": 25, + "sendAfterOnload": true, + "sendInterval": 1000 + }, + "Continuity": { + "enabled": true + }, + "PageParams": { + "xhr": "all" + } +}); diff --git a/tests/server/config/22-early-14-spa-missed-xhr-on.json b/tests/server/config/22-early-14-spa-missed-xhr-on.json new file mode 100644 index 000000000..adb40329f --- /dev/null +++ b/tests/server/config/22-early-14-spa-missed-xhr-on.json @@ -0,0 +1,32 @@ +{ + "h.t": {{H_T}}, + "h.cr": "{{H_CR}}", + "h.d": "{{DOMAIN}}", + + "autorun": false, + "instrument_xhr": true, + "History": { + "enabled": true + }, + "Early": { + "enabled": true + }, + "ResourceTiming": { + "enabled": true, + "splitAtPath": true + }, + "Errors": { + "enabled": true, + "monitorTimeout": true, + "monitorEvents": true, + "maxErrors": 25, + "sendAfterOnload": true, + "sendInterval": 1000 + }, + "Continuity": { + "enabled": true + }, + "PageParams": { + "xhr": "all" + } +} diff --git a/tests/server/config/22-early-localstorage.js b/tests/server/config/22-early-localstorage.js new file mode 100644 index 000000000..6fb55741e --- /dev/null +++ b/tests/server/config/22-early-localstorage.js @@ -0,0 +1,5 @@ +BOOMR_configt=new Date().getTime(); +BOOMR.addVar({"h.t":{{H_T}},"h.cr":"{{H_CR}}"}); +BOOMR.init({ + autorun: true +}); diff --git a/tests/server/config/22-early-localstorage.json b/tests/server/config/22-early-localstorage.json new file mode 100644 index 000000000..f054726bf --- /dev/null +++ b/tests/server/config/22-early-localstorage.json @@ -0,0 +1,21 @@ +{ + "h.t": "{{H_T}}", + "h.cr": "{{H_CR}}", + "h.d": "{{DOMAIN}}", + "autorun": true, + "PageParams": { + "pageGroups": [ + { + "type": "Regexp", + "parameter1": "/pages/", + "parameter2": "FROMSERVER" + } + ] + }, + "LOGN": { + "storeConfig": true + }, + "Early": { + "enabled": true + } +} diff --git a/tests/server/config/perf-00-basic-00-empty.json b/tests/server/config/perf-00-basic-00-empty.json new file mode 100644 index 000000000..dfc1240c2 --- /dev/null +++ b/tests/server/config/perf-00-basic-00-empty.json @@ -0,0 +1,5 @@ +{ + "h.t": {{H_T}}, + "h.cr": "{{H_CR}}", + "h.d": "{{DOMAIN}}" +} diff --git a/tests/server/config/perf-00-basic-01-all-plugins.json b/tests/server/config/perf-00-basic-01-all-plugins.json new file mode 100644 index 000000000..58cd96ff5 --- /dev/null +++ b/tests/server/config/perf-00-basic-01-all-plugins.json @@ -0,0 +1,38 @@ +{ + "h.t": {{H_T}}, + "h.cr": "{{H_CR}}", + "h.d": "{{DOMAIN}}", + "ResourceTiming": { + "enabled": true + }, + "Angular": { + "enabled": true + }, + "Ember": { + "enabled": true + }, + "Backbone": { + "enabled": true + }, + "History": { + "enabled": true + }, + "Errors": { + "enabled": true + }, + "TPAnalytics": { + "enabled": true + }, + "UserTiming": { + "enabled": true + }, + "Continuity": { + "enabled": true + }, + "IFrameDelay": { + "enabled": true + }, + "Akamai": { + "enabled": true + } +} diff --git a/tests/server/config/perf-01-restiming-00-expensive.json b/tests/server/config/perf-01-restiming-00-expensive.json new file mode 100644 index 000000000..5695f8c97 --- /dev/null +++ b/tests/server/config/perf-01-restiming-00-expensive.json @@ -0,0 +1,9 @@ +{ + "h.t": {{H_T}}, + "h.cr": "{{H_CR}}", + "h.d": "{{DOMAIN}}", + "ResourceTiming": { + "enabled": true, + "splitAtPath": true + } +} diff --git a/tests/server/doc-server.js b/tests/server/doc-server.js new file mode 100644 index 000000000..db39d6da9 --- /dev/null +++ b/tests/server/doc-server.js @@ -0,0 +1,41 @@ +/* eslint-env node */ + +// +// Imports +// +var path = require("path"); +var fs = require("fs"); +var express = require("express"); +var http = require("http"); + +var docPath = path.resolve(path.join(__dirname, "..", "..", "build", "doc")); +var packageJsonPath = path.resolve(path.join(__dirname, "..", "..", "package.json")); + +if (!fs.existsSync(docPath)) { + throw new Error("Documentation not available" + docPath); +} + +if (!fs.existsSync(packageJsonPath)) { + throw new Error("package.json not found: " + packageJsonPath); +} + +var pkg = require(packageJsonPath); + +var docResultPath = path.resolve(path.join(docPath, pkg.name, pkg.version)); + +if (!fs.existsSync(docResultPath)) { + throw new Error("Build path not found: " + packageJsonPath); +} + +var app = express(); + +app.use(express["static"](docResultPath)); + +var server = http.createServer(app); + +// listen +var port = process.env.PORT || 3000; + +server.listen(port, function() { + console.log("Server starting on port " + port + " for " + docResultPath); +}); diff --git a/tests/server/env.json.sample b/tests/server/env.json.sample new file mode 100644 index 000000000..0f6f55f34 --- /dev/null +++ b/tests/server/env.json.sample @@ -0,0 +1,7 @@ +{ + "www": "tests", + "port": 3000, + "publish": "/home/www", + "privatekey": "certs/boomerang-test.local.pem", + "certificate": "certs/boomerang-test.local.crt" +} diff --git a/tests/server/route-chunked.js b/tests/server/route-chunked.js new file mode 100644 index 000000000..c6f7d751c --- /dev/null +++ b/tests/server/route-chunked.js @@ -0,0 +1,51 @@ +// +// Constants +// +var CHARS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + +// +// Functions +// +function rnd(length) { + var str = "", + i; + + for (i = 0; i < length; i++) { + str += CHARS[Math.floor(Math.random() * CHARS.length)]; + } + + return str; +} + +// +// Exports +// +module.exports = function(req, res) { + var q = require("url").parse(req.url, true).query; + var chunkSize = q.chunkSize; + var chunkCount = q.chunkCount; + var chunkDelay = q.chunkDelay; + var contentLength = chunkSize * chunkCount; + + // set a few headers + res.setHeader("Last-Modified", (new Date()).toUTCString()); + res.setHeader("Transfer-Encoding", "chunked"); + + var cur = 0; + + function sendData() { + res.write(rnd(chunkSize)); + + cur++; + + // if we still have chunks to send, set another timeout + if (cur < chunkCount) { + setTimeout(sendData, chunkDelay); + } + else { + res.end(); + } + } + + setTimeout(sendData, chunkDelay); +}; diff --git a/tests/server/route-delay.js b/tests/server/route-delay.js new file mode 100644 index 000000000..3ed35c7b9 --- /dev/null +++ b/tests/server/route-delay.js @@ -0,0 +1,160 @@ +// +// Imports +// +var path = require("path"); +var fs = require("fs"); + +// +// Mime types +// +var mimeTypes = { + "html": "text/html", + "json": "application/json", + "jpeg": "image/jpeg", + "jpg": "image/jpeg", + "png": "image/png", + "js": "text/javascript", + "css": "text/css", + "xml": "text/xml" +}; + +// +// Load env.json +// +var envFile = path.resolve(path.join(__dirname, "env.json")); + +if (!fs.existsSync(envFile)) { + throw new Error("Please create " + envFile + ". There's a env.json.sample in the same dir."); +} + +// load JSON +var env = require(envFile); + +var wwwRoot = env.www; + +if (wwwRoot.indexOf("/") !== 0) { + wwwRoot = path.join(__dirname, "..", "..", wwwRoot); +} + +if (!fs.existsSync(wwwRoot)) { + wwwRoot = path.join(__dirname, ".."); +} + +// save previous 'delay' query param value +var previousDelay = 0; + +module.exports = function(req, res) { + // keep query strings on files if requested that way + var url = req.url; + var querySplit = req.url.split(/\?/); + + if (querySplit.length === 3) { + querySplit.pop(); + url = querySplit.join("?"); + } + + var q = require("url").parse(url, true).query; + var delay = q.delay || 0; + var file = q.file; + var response = q.response; + var sendACAO = !(q.noACAO === "1"); // send by default + var sendTAO = (q.TAO === "1"); // don't send by default + var responseHeaders = q.headers; + var redir = q.redir; + + // if we get a '+' or '-' delay prefix, add/sub its value with the delay used on the + // previous request. This is usefull in cases where we need to hit the + // same url and query params for multiple requests with differing delay times. + if (typeof delay === "string" && delay) { + if (delay[0] === "+") { + delay = previousDelay + parseInt(delay.slice(1), 10); + } + else if (delay[0] === "-") { + delay = previousDelay - parseInt(delay.slice(1), 10); + } + else { + delay = parseInt(delay, 10); + } + } + + delay = delay >= 0 ? delay : 0; + + previousDelay = delay; + + setTimeout(function() { + var headers = {}; + + if (redir) { + res.header("Cache-Control", "no-cache, no-store, must-revalidate"); + res.header("Pragma", "no-cache"); + res.header("Expires", 0); + + return res.redirect(302, file); + } + + if (delay > 0) { + res.header("Cache-Control", "no-cache, no-store, must-revalidate"); + res.header("Pragma", "no-cache"); + res.header("Expires", 0); + } + + if (sendACAO) { + headers["Access-Control-Allow-Origin"] = "*"; + } + + if (sendTAO) { + headers["Timing-Allow-Origin"] = "*"; + } + + if (responseHeaders) { + var responseHeadersObj = null; + + try { + responseHeadersObj = JSON.parse(responseHeaders); + } + catch (e) { + // nop + } + + for (var headerName in responseHeadersObj) { + if (responseHeadersObj.hasOwnProperty(headerName) && responseHeadersObj[headerName]) { + headers[headerName] = responseHeadersObj[headerName]; + } + } + } + + if (response) { + res.writeHead(200, headers); + + return res.end(response); + } + + var filePath = path.join(wwwRoot, file); + + // ensure file requested is rooted to wwwRoot + if (filePath.indexOf(wwwRoot) !== 0) { + return res.sendStatus(404); + } + + fs.exists(filePath, function(exists) { + if (!exists) { + return res.sendStatus(404); + } + + // determine MIME type + if (!headers["Content-Type"]) { + var mimeType = mimeTypes[path.extname(filePath).split(".")[1]]; + + if (mimeType) { + headers["Content-Type"] = mimeType; + } + } + + res.writeHead(200, headers); + + var fileStream = fs.createReadStream(filePath); + + fileStream.pipe(res); + }); + }, delay); +}; diff --git a/tests/server/route-json.js b/tests/server/route-json.js new file mode 100644 index 000000000..f410a4fbf --- /dev/null +++ b/tests/server/route-json.js @@ -0,0 +1,17 @@ +// +// Constants +// +var RESPONSE = { a: 1, b: 2 }; +var RESPONSE_STR = JSON.stringify(RESPONSE); + +// +// Exports +// +module.exports = function(req, res) { + // set a few headers + res.setHeader("Last-Modified", (new Date()).toUTCString()); + res.setHeader("Content-Length", RESPONSE_STR.length); + res.setHeader("Content-Type", "application/json"); + + res.send(RESPONSE_STR); +}; diff --git a/tests/test-templates/autoxhr/00-xhrs-duplicate.js b/tests/test-templates/autoxhr/00-xhrs-duplicate.js new file mode 100644 index 000000000..f9ba3bbae --- /dev/null +++ b/tests/test-templates/autoxhr/00-xhrs-duplicate.js @@ -0,0 +1,87 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ +BOOMR_test.templates.XHR = BOOMR_test.templates.XHR || {}; +BOOMR_test.templates.XHR["00-xhrs-duplicate"] = function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + + it("Should get 5 beacons: 1 onload, 4 xhr (XMLHttpRequest !== null)", function(done) { + this.timeout(30000); + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 5); + }, + this.skip.bind(this)); + }); + + function check(b, ms) { + assert.closeTo(b.t_done, ms, 400); + assert.closeTo(b.nt_res_end - b.nt_req_st, b.t_resp, 400); + + if (b.nt_fet_st) { + // not avail in PhantomJS + assert.ok(b.nt_fet_st <= b.nt_req_st, "nt_fet_st should be at most nt_req_st"); + } + + assert.ok(b.nt_req_st <= b.nt_res_st, "nt_req_st should be at most nt_res_st"); + assert.ok(b.nt_res_st <= b.nt_res_end, "nt_res_st should be at most nt_res_end"); + assert.ok(b.nt_res_end <= b.nt_load_st, "nt_res_end should be at most nt_load_st"); + } + + describe("Beacon 2 (xhr)", function() { + var i = 1; + + it("Should have a time of around around " + window.xhrTimes[i - 1] + " ms", function(done) { + t.ifAutoXHR( + done, + function() { + check(tf.beacons[i], window.xhrTimes[i - 1]); + done(); + }, + this.skip.bind(this)); + }); + }); + + describe("Beacon 3 (xhr)", function() { + var i = 2; + + it("Should have a time of around around " + window.xhrTimes[i - 1] + " ms", function(done) { + t.ifAutoXHR( + done, + function() { + check(tf.beacons[i], window.xhrTimes[i - 1]); + done(); + }, + this.skip.bind(this)); + }); + }); + + describe("Beacon 4 (xhr)", function() { + var i = 3; + + it("Should have a time of around around " + window.xhrTimes[i - 1] + " ms", function(done) { + t.ifAutoXHR( + done, + function() { + check(tf.beacons[i], window.xhrTimes[i - 1]); + done(); + }, + this.skip.bind(this)); + }); + }); + + describe("Beacon 5 (xhr)", function() { + var i = 4; + + it("Should have a time of around around " + window.xhrTimes[i - 1] + " ms", function(done) { + t.ifAutoXHR( + done, + function() { + check(tf.beacons[i], window.xhrTimes[i - 1]); + done(); + }, + this.skip.bind(this)); + }); + }); +}; diff --git a/tests/test-templates/common.js b/tests/test-templates/common.js new file mode 100644 index 000000000..56e2738cd --- /dev/null +++ b/tests/test-templates/common.js @@ -0,0 +1,658 @@ +/* eslint-env mocha */ +/* global BOOMR,BOOMR_test,assert */ + +describe("common", function() { + var t = BOOMR_test; + var tf = BOOMR.plugins.TestFramework; + var assert = window.chai.assert; + + // Get a performance.now() polyfill that doesn't rely on BOOMR.now + var dateNow = + (function() { + return Date.now || function() { + return new Date().getTime(); + }; + }()); + var perfNow = dateNow; + + if ("performance" in window && window.performance && typeof window.performance.now === "function") { + perfNow = function() { + if (window.performance && typeof window.performance.now === "function" && !window.performance.now.isCustom) { + return Math.round(window.performance.now() + window.performance.timing.navigationStart); + } + else { + // might've been deleted via BOOMR_test.removeNavigationTimingSupport() + return dateNow(); + } + }; + } + + /** + * Validates an page load beacon + */ + function testPageLoadBeacon(b, prefix) { + // TODO + assert.isUndefined(b.pgu, prefix + "does not have the pgu param"); + assert.isUndefined(b["xhr.pg"], prefix + "does not have the xhr.pg param"); + } + + /** + * Validates an spa_hard beacon + */ + function testSpaHardBeacon(b, prefix) { + assert.isUndefined(b.api, prefix + "does not have the api param"); + assert.isUndefined(b["xhr.pg"], prefix + "does not have the xhr.pg param"); + + if (!t.doNotTestSpaAbort) { + assert.isUndefined(b["rt.abld"], prefix + "does not have the rt.abld param"); + assert.isUndefined(b["rt.quit"], prefix + "does not have the rt.quit param"); + } + } + + /** + * Validates an spa soft beacon + */ + function testSpaSoftBeacon(b, prefix) { + var fieldsUndefined = [ + "api", + "xhr.pg", + "nt_nav_st", + "nt_fet_st", + "nt_dns_st", + "nt_dns_end", + "nt_con_st", + "nt_con_end", + "nt_req_st", + "nt_res_st", + "nt_res_end", + "nt_domloading", + "nt_domint", + "nt_domcontloaded_st", + "nt_domcontloaded_end", + "nt_domcomp", + "nt_load_st", + "nt_load_end", + "nt_unload_st", + "nt_unload_end", + "nt_enc_size", + "nt_dec_size", + "nt_trn_size", + "nt_protocol", + "nt_first_paint", + "nt_red_cnt", + "nt_nav_type" + ], + field; + + if (!t.doNotTestSpaAbort) { + assert.isUndefined(b["rt.abld"], prefix + "does not have the rt.abld param"); + assert.isUndefined(b["rt.quit"], prefix + "does not have the rt.quit param"); + } + + for (var i = 0; i < fieldsUndefined.length; i++) { + field = fieldsUndefined[i]; + assert.isUndefined(b[field], prefix + field + " must not be on spa soft beacon"); + } + } + + /** + * Validates an xhr beacon + */ + function testXhrBeacon(b, prefix) { + assert.isUndefined(b.api, prefix + "does not have the api param"); + assert.isDefined(b.pgu, prefix + "has the pgu param"); + assert.isUndefined(b["h.pg"], prefix + "does not have the h.pg param"); + } + + /** + * Validates an early beacon against the load beacon + */ + function testEarlyBeacon(early, normal) { + var i, field, timer, timers, + early_timers = {}, + normal_timers = {}; + + // Don't test h.pg, we'll do some magic in the tests to make sure page params runs twice + + // Not yet tested: + // "vis.st", + // "dom.res", + // "dom.doms", + // "mem.total", + // "mem.limit", + // "mem.used", + // "scr.xyv", + // "scr.bpp", + // "scr.orn", + // "scr.dpx", + // "cpu.cnc", + // "dom.ln", + // "dom.sz", + // "dom.img", + // "dom.script", + // "dom.script.ext", + // "dom.iframe", + // "dom.iframe.ext", + // "dom.link", + + // fields that should be the same on both beacons + var fieldsEqual = [ + "h.key", + "rt.start", + "rt.bmr", + "rt.tstart", + "rt.nstart", + "rt.bstart", + "rt.blstart", + "rt.si", + "rt.ss", + "rt.sstr_dur", + "rt.sstr_to", + "v", + "pid", + "ua.plt", + "ua.vnd", + "u", + "nt_red_cnt", + "nt_nav_type", + "nt_nav_st", + "nt_red_st", + "nt_red_end", + "nt_fet_st", + "nt_dns_st", + "nt_dns_end", + "nt_con_st", + "nt_con_end", + "nt_req_st", + "nt_res_st", + "nt_res_end", + "nt_domloading", + "nt_domint", + "nt_domcontloaded_st", + "nt_domcontloaded_end", + "nt_unload_st", + "nt_unload_end", + "nt_domcomp", + "nt_load_st", + // load could have ended in the case of SPA hard + "nt_load_end", + "nt_first_paint", + "nt_spdy", + "nt_cinf", + "if", + "vis.pre", + "t_configls", + "t_domloaded", + "t_load", + "t_prerender", + "t_postrender" + ]; + + // fields that should not be on early beacon + var fieldsUndefined = [ + // no page load timing available yet + "t_resp", + "t_page", + "t_done", + // no restiming on early beacons + "restiming" + ]; + + // fields that should be the same on both beacons if available + var fieldsEqualIfExists = [ + // may not be there if config loaded from localStorage + "rt.cnf", + "t_configfb", + "t_configjs" + ]; + + // fields that must be on the early beacon + var fieldsMustExist = [ + "rt.end", + "rt.tt", + "early" + ]; + + for (i = 0; i < fieldsEqual.length; i++) { + field = fieldsEqual[i]; + + if (field.indexOf("nt_") === 0 && (!(field in early) || early[field] === 0)) { + // nav timing fields may be 0 or missing on the early beacon + continue; + } + + if (typeof normal[field] === "undefined") { + assert.isUndefined(early[field], field + " must not be on early beacon if not on the load beacon"); + } + else { + assert.equal(normal[field], early[field], field + " " + normal[field] + " === " + early[field]); + } + } + + for (i = 0; i < fieldsUndefined.length; i++) { + field = fieldsUndefined[i]; + assert.isUndefined(early[field], field + " must not be on early beacon"); + } + + for (i = 0; i < fieldsEqualIfExists.length; i++) { + field = fieldsEqualIfExists[i]; + + if (typeof early[field] !== "undefined") { + assert.equal(normal[field], early[field], field + " " + normal[field] + " === " + early[field]); + } + } + + for (i = 0; i < fieldsMustExist.length; i++) { + field = fieldsMustExist[i]; + assert.isDefined(early[field], field + " must exist"); + } + + // rt.sl should be 1 less on the early beacon + assert.equal(parseInt(normal["rt.sl"], 10), parseInt(early["rt.sl"], 10) + 1, + "session length " + normal["rt.sl"] + " === " + early["rt.sl"] + " + 1"); + + // rt.obo should be equal or 1 more on the normal beacon (if navtiming not supported) + if (early["rt.obo"] !== normal["rt.obo"] && (parseInt(early["rt.obo"], 10) + 1) !== parseInt(normal["rt.obo"], 10)) { + assert.fail("rt.obo must be equal or 1 more on normal beacon"); + } + + // t_other, if a timer is on the early beacon then it must be on the normal beacon + if (early.t_other) { + if (normal.t_other) { + normal_timers = t.parseTimers(normal.t_other); + early_timers = t.parseTimers(early.t_other); + + for (timer in early_timers) { + if (early_timers.hasOwnProperty(timer)) { + if (timer.indexOf("custom") === 0) { + // custom timers may get longer (eg. ResourceGroups matching several resources) + assert.operator(normal_timers[timer], ">=", early_timers[timer], + "t_other " + timer + " " + normal_timers[timer] + " >= " + early_timers[timer]); + } + else { + assert.equal(normal_timers[timer], early_timers[timer], + "t_other " + timer + " " + normal_timers[timer] + " === " + early_timers[timer]); + } + } + } + } + else { + assert.fail("t_other on early beacon but missing on the normal beacon"); + } + } + + // http.initiator must be the same in both beacons (even if it is undefined) + assert.equal(early["http.initiator"], normal["http.initiator"], "both beacons should have the same initiator (http.initiator)"); + }; + + it("Should have sent beacons that pass basic validation", function() { + var i, b, tm, + now = perfNow(), + prefix, nextb; + + if (!tf.beacons.length) { + return this.skip(); + } + + for (i = 0; i < tf.beacons.length; i++) { + b = tf.beacons[i]; + prefix = "ensure beacon " + (i + 1) + " "; + + assert.equal(b.n, i + 1, prefix + "has the correct beacon number"); + + assert.equal(b.v, BOOMR.version, prefix + "has the boomerang version"); + + if (BOOMR.snippetVersion) { + assert.equal(b.sv, BOOMR.snippetVersion, prefix + "has the boomerang snippet version"); + } + + if (BOOMR.snippetMethod) { + assert.equal(b.sm, BOOMR.snippetMethod, prefix + "has the boomerang snippet method"); + } + + assert.equal(b["h.key"], window.BOOMR_API_key, prefix + "has the correct API key (h.key)"); + assert.isDefined(b["h.d"], prefix + "has the domain (h.d) param"); + + assert.isDefined(b["h.t"], prefix + "has the time (h.t) param"); + + if (!b.early) { + // not early beacon + if (typeof b["rt.sl"] !== "undefined") { + assert.operator(parseInt(b["rt.sl"], 10), ">", 0); + } + } + else { + // early beacon + if (typeof b["rt.sl"] !== "undefined") { + assert.operator(parseInt(b["rt.sl"], 10), ">=", 0); + } + + assert.operator(i + 1, "<", tf.beacons.length, prefix + "(early) is not the last beacon"); + nextb = tf.beacons[i + 1]; + assert.isUndefined(nextb.early, "(early) should not be followed by another early beacon"); + testEarlyBeacon(b, nextb); + } + + if (window.BOOMR_LOGN_always !== true) { + assert.equal(b["h.cr"], "abc", prefix + "has the correct crumb (h.cr)"); + } + else { + assert.isDefined(b["h.cr"], prefix + "has the crumb (h.cr)"); + } + + if (!t.doNotTestErrorsParam) { + assert.isUndefined(b.errors, prefix + "does not have the errors param"); + } + + if (b["rt.start"] === "navigation") { + // page load beacon + testPageLoadBeacon(b, prefix); + } + else if (b["rt.start"] === "manual") { + if (b["http.initiator"] === "spa_hard") { + // spa hard beacon + testSpaHardBeacon(b, prefix); + } + else if (b["http.initiator"] === "spa") { + // spa soft beacon + testSpaSoftBeacon(b, prefix); + } + else if (b["http.initiator"] === "xhr") { + // xhr beacon + testXhrBeacon(b, prefix); + } + else if (b["http.initiator"] === "click") { + // click (AutoXHR) beacon + assert.isUndefined(b.api, prefix + "does not have the api param"); + assert.isDefined(b.pgu, prefix + "has the pgu param"); + } + else if (b["http.initiator"] === "api_custom_metric") { + // send metric beacon + assert.equal(b.api, "1", prefix + "has the api param value equal to 1"); + assert.equal(b["api.v"], "2", prefix + "has api version equal to 2"); + assert.equal(b["api.l"], "boomr", prefix + "has the api source equal to boomr"); + } + else if (b["http.initiator"] === "api_custom_timer") { + // send timer beacon + assert.equal(b.api, "1", prefix + "has the api param value equal to 1"); + assert.equal(b["api.v"], "2", prefix + "has api version equal to 2"); + assert.equal(b["api.l"], "boomr", prefix + "has the api source equal to boomr"); + } + else if (b["http.initiator"] === "error") { + // error beacon + assert.equal(b.api, "1", prefix + "has the api param value equal to 1"); + } + else if (b["http.initiator"] === "bfcache") { + // BFCache navigation + assert.equal(b["rt.start"], "manual", prefix + "has rt.start set to manual"); + } + else if (b["http.initiator"] === "interaction") { + // Interaction beacon + // nothing special + } + else if (typeof b["http.initiator"] === "undefined") { + // requestStart and/or responseEnd initiated beacon + // TODO + } + else { + // invalid + assert.fail(prefix + "with a rt.start=manual has a valid http.initiator, was:" + b["http.initiator"]); + } + } + else if (b["rt.start"] === "none") { + if (b["http.initiator"] === "spa_hard") { + // spa hard beacon + testSpaHardBeacon(b, prefix); + } + else if (b["http.initiator"] === "spa") { + // spa soft beacon + testSpaSoftBeacon(b, prefix); + } + else { + // TODO + } + } + else if (b["rt.start"] === "cookie") { + // TODO + } + else if (b["rt.start"] === "csi") { + // TODO + } + else if (b["rt.start"] === "gtb") { + // TODO + } + else if (typeof b["rt.start"] === "undefined") { + if (b["http.initiator"] === "error") { + // error beacon + assert.equal(b.api, "1", prefix + "has the api param value equal to 1"); + } + else if (b["rt.quit"] !== "undefined") { + // unload beacon + } + else { + // invalid + assert.fail(prefix + "has a valid rt.start, was:" + b["rt.start"]); + } + } + else { + // invalid + assert.fail(prefix + "has a valid rt.start, was: " + b["rt.start"]); + } + } + }); + + it("Should have sent beacons with the same Page ID (pid)", function() { + var pid, i, prefix, b; + + if (!tf.beacons.length) { + return this.skip(); + } + + pid = tf.beacons[0].pid; + for (i = 0; i < tf.beacons.length; i++) { + b = tf.beacons[i]; + prefix = "ensure beacon " + (i + 1) + " "; + assert.lengthOf(b.pid, 8, prefix + "has a page ID (pid) with a length equal to 8"); + assert.equal(b.pid, pid, prefix + "has the same pid as first beacon"); + } + }); + + it("Should have sent beacons with valid timers", function() { + var i, j, b, prefix, timer; + var TIMERS = [ + "t_done", + "t_resp", + "t_page", + "rt.tt", + "mob.rtt", + "pt.fp", + "pt.fcp", + "pt.lcp", + "rt.sstr_dur" + ]; + + if (!tf.beacons.length) { + return this.skip(); + } + + for (i = 0; i < tf.beacons.length; i++) { + b = tf.beacons[i]; + prefix = "ensure beacon " + (i + 1) + " "; + + if (typeof b["rt.tstart"] !== "undefined" && typeof b["rt.end"] !== "undefined" && typeof b.t_done !== "undefined") { + if (!t.isPrerenderingSupported() || !b.nt_act_st) { + assert.equal(parseInt(b["rt.end"]) - parseInt(b["rt.tstart"]), parseInt(b.t_done), prefix + "has rt.end - rt.tstart == t_done"); + } + else { + // prerendering and activation_st exists. + if (b.nt_act_st > b["rt.end"]) { + // Activation happened after page load, so expect page load to be 1ms + assert.equal(1, parseInt(b.t_done), prefix + "Expected t_done to be 1ms"); + } + else { + // Activation started before page load. Expect pageload to be offset by activationTime + var actTime = parseInt(b.nt_act_st, 10) - parseInt(b.nt_nav_st, 10); + + // allow for rounding + assert.closeTo(parseInt(b.t_done), parseInt(b["rt.end"]) - parseInt(b["rt.tstart"]) - actTime, 1, "has (rt.end - rt.tstart) approximately close to t_done"); + } + } + } + + for (var j = 0; j < TIMERS.length; j++) { + timer = TIMERS[j]; + + if (typeof b[timer] !== "undefined") { + assert.operator(parseInt(b[timer], 10), ">=", 0, prefix + "has a positive '" + timer + "' timer"); + assert.operator(parseInt(b[timer], 10), "<", 10 * 60 * 1000, prefix + "has a sane value for '" + timer + "' timer"); + } + } + } + }); + + it("BUG: Should have sent beacons without negative custom timers", function() { + var i, b, prefix, timers, timer; + + if (!tf.beacons.length) { + return this.skip(); + } + + for (i = 0; i < tf.beacons.length; i++) { + b = tf.beacons[i]; + prefix = "ensure beacon " + (i + 1) + " "; + + if (b.t_other) { + timers = t.parseTimers(b.t_other); + for (timer in timers) { + if (timers.hasOwnProperty(timer)) { + // TODO: this test reveals a bug, see Issue #626 + // assert.isTrue(timers[timer] >= 0, prefix + "has a positive 't_other." + timer + "' timer"); + if (timers[timer] < 0) { + return this.skip(); + } + + if (b[timer + "_st"]) { + assert.operator(parseInt(b[timer + "_st"], 10), ">=", 0, prefix + "has a positive '" + timer + "_st' value"); + } + } + } + } + } + }); + + it("Should have sent beacons with valid epoch times", function() { + var i, j, b, prefix, param, tm, + now = perfNow(); + var TIMES = [ + "h.t", + "rt.tstart", + "rt.end", + "rt.ss", + "t.bstart", + "nt_nav_st", + "nt_fet_st", + "nt_dns_st", + "nt_dns_end", + "nt_con_st", + "nt_con_end", + "nt_req_st", + "nt_res_st", + "nt_res_end", + "nt_domloading", + "nt_domint", + "nt_domcontloaded_st", + "nt_domcontloaded_end", + "nt_domcomp", + "nt_load_st", + "nt_load_end", + "nt_unload_st", + "nt_unload_end", + "nt_ssl_st", + "nt_first_paint" + ]; + + if (!tf.beacons.length) { + return this.skip(); + } + + for (i = 0; i < tf.beacons.length; i++) { + b = tf.beacons[i]; + prefix = "ensure beacon " + (i + 1) + " "; + + for (var j = 0; j < TIMES.length; j++) { + param = TIMES[j]; + + if (b.nt_bad && param.match(/^nt_/)) { + // don't check navtiming timers, they were detected as bad + continue; + } + + if (typeof b[param] !== "undefined") { + tm = parseInt(b[param], 10); + + if (tm === 2997993600000) { + // this future timestamp is used for debugging + continue; + } + + // now +- an hour. Cloud based Simulators/Emulators might have clock skew + assert.closeTo(tm, now, (70 * 60 * 1000), prefix + "has " + param + " as a valid timestamp"); + } + } + + // rt.end should never be set to string "undefined" + assert.notEqual(b["rt.end"], "undefined", "rt.end should not be undefined"); + } + }); + + it("AutoXHR: Should not have pending events", function(done) { + if (t.isConsentConsentInlinePluginOptedOut()) { + this.skip(); + } + + t.ifAutoXHR( + done, + function() { + var events = BOOMR.plugins.AutoXHR.getMutationHandler().pending_events; + + events = BOOMR.utils.arrayFilter(events, function(val) { + return typeof val !== "undefined"; + }); + assert.lengthOf(events, 0); + done(); + }, + this.skip.bind(this) + ); + }); + + it("User-Agent Deprecation: If navigator.userAgentData is available, ensure navigator.userAgentData.platform is used instead of navigator.platform", function() { + if (window.navigator.userAgentData && + !window.navigator.userAgentData.testOverride && + tf.lastBeacon() && + tf.lastBeacon()["ua.plt"]) { + assert.isString(tf.lastBeacon()["ua.plt"], "ua.plt is not a string"); + assert.equal(tf.lastBeacon()["ua.plt"], navigator.userAgentData.platform); + } + }); + + it("Should not access navigator.platform if navigator.userAgentData.platform exists", function() { + if (window._BOOMR_navigatorCheck) { + throw window._BOOMR_navigatorCheck; + } + }); + + it("Should not access navigator.userAgent if navigator.userAgentData exists", function() { + if (window._BOOMR_userAgentCheck) { + throw window._BOOMR_userAgentCheck; + } + }); + + it("Should not leak global variables", function() { + var leakedVariables = BOOMR_test.findGlobalLeaks( + window, + BOOMR_test.initialGlobals.concat(BOOMR_test.addedGlobals), + BOOMR_test.globalProps(window)); + + assert.deepEqual(leakedVariables, []); + }); +}); diff --git a/tests/test-templates/spa/00-simple.js b/tests/test-templates/spa/00-simple.js new file mode 100644 index 000000000..2ca2e00a9 --- /dev/null +++ b/tests/test-templates/spa/00-simple.js @@ -0,0 +1,122 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ +BOOMR_test.templates.SPA = BOOMR_test.templates.SPA || {}; +BOOMR_test.templates.SPA["00-simple"] = function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have only sent one beacon", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 1); + }); + + it("Should take as long as the longest img load (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + t.validateBeaconWasSentAfter(0, "img.jpg", 150, 3000, 30000, true); + } + else { + return this.skip(); + } + }); + + it("Should have a t_resp of the root page (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + var pt = window.performance.timing; + var b = tf.lastBeacon(); + + assert.equal(b.t_resp, pt.responseStart - pt.navigationStart); + } + else { + return this.skip(); + } + }); + + it("Should have a t_page of total - t_resp (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + var pt = window.performance.timing; + var b = tf.lastBeacon(); + + assert.equal(b.t_page, b.t_done - b.t_resp); + } + else { + return this.skip(); + } + }); + + it("Should not have a load time (if MutationObserver is supported but NavigationTiming is not)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() === "undefined") { + var b = tf.lastBeacon(); + + assert.equal(b.t_done, undefined); + } + else { + return this.skip(); + } + }); + + it("Should take as long as the XHRs (if MutationObserver is not supported but NavigationTiming is)", function() { + if (!t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + // this fails in react, onload happens later than the XHR + t.validateBeaconWasSentAfter(0, "widgets.json", 100, 0, 30000, true); + } + else { + return this.skip(); + } + }); + + it("Shouldn't have a load time (if MutationObserver and NavigationTiming are not supported)", function() { + if (!t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() === "undefined") { + var b = tf.lastBeacon(); + + assert.equal(b.t_done, undefined); + assert.equal(b["rt.start"], "none"); + } + else { + return this.skip(); + } + }); + + it("Should have sent the http.initiator as 'spa_hard'", function() { + var b = tf.lastBeacon(); + + assert.equal(b["http.initiator"], "spa_hard"); + }); + + it("Should have NavigationTiming metrics (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + var b = tf.lastBeacon(); + + assert.equal(b.nt_red_cnt, 0, "nt_red_cnt is 0"); // no redirects + assert.isDefined(b.nt_nav_type, "nt_nav_type is defined"); + assert.isDefined(b.nt_nav_st, "nt_nav_st is defined"); + assert.isUndefined(b.nt_red_st, "nt_red_st is undefined"); // no redirects + assert.isUndefined(b.nt_red_end, "nt_red_end is undefined"); // no redirects + assert.isDefined(b.nt_fet_st, "nt_fet_st is defined"); + assert.isDefined(b.nt_dns_st, "nt_dns_st is defined"); + assert.isDefined(b.nt_dns_end, "nt_dns_end is defined"); + assert.isDefined(b.nt_con_st, "nt_con_st is defined"); + assert.isDefined(b.nt_con_end, "nt_con_end is defined"); + assert.isDefined(b.nt_req_st, "nt_req_st is defined"); + assert.isDefined(b.nt_res_st, "nt_res_st is defined"); + assert.isDefined(b.nt_res_end, "nt_res_end is defined"); + assert.isDefined(b.nt_domloading, "nt_domloading is defined"); + assert.isDefined(b.nt_domint, "nt_domint is defined"); + assert.isDefined(b.nt_domcontloaded_st, "nt_domcontloaded_st is defined"); + assert.isDefined(b.nt_domcontloaded_end, "nt_domcontloaded_end is defined"); + assert.isDefined(b.nt_domcomp, "nt_domcomp is defined"); + assert.isDefined(b.nt_load_st, "nt_load_st is defined"); + assert.isDefined(b.nt_load_end, "nt_load_end is defined"); + + // Unload times may be 0 and thus not on the beacon + // assert.isDefined(b.nt_unload_st, "nt_unload_st is defined"); + // assert.isDefined(b.nt_unload_end, "nt_unload_end is defined"); + } + else { + return this.skip(); + } + }); +}; diff --git a/tests/test-templates/spa/04-route-change.js b/tests/test-templates/spa/04-route-change.js new file mode 100644 index 000000000..8e2b082ab --- /dev/null +++ b/tests/test-templates/spa/04-route-change.js @@ -0,0 +1,257 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ +BOOMR_test.templates.SPA = BOOMR_test.templates.SPA || {}; +BOOMR_test.templates.SPA["04-route-change"] = function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + var pathName = window.location.pathname; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent three beacons", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 3); + }); + + it("Should have sent the first beacon as http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + + it("Should have sent all subsequent beacons as http.initiator = spa", function() { + for (var i = 1; i < 2; i++) { + assert.equal(tf.beacons[i]["http.initiator"], "spa"); + } + }); + + it("Should have sent all subsequent beacons have rt.nstart = navigationTiming (if NavigationTiming is supported)", function() { + if (typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + for (var i = 1; i < 2; i++) { + assert.equal(tf.beacons[i]["rt.nstart"], BOOMR.plugins.RT.navigationStart()); + } + } + else { + return this.skip(); + } + }); + + it("Should not have Boomerang timings on SPA Soft beacons", function() { + for (var i = 1; i < 2; i++) { + if (tf.beacons[i].t_other) { + assert.notInclude(tf.beacons[i].t_other, "boomr_fb", "should not have boomr_fb"); + assert.notInclude(tf.beacons[i].t_other, "boomr_ld", "should not have boomr_ld"); + assert.notInclude(tf.beacons[i].t_other, "boomr_lat", "should not have boomr_lat"); + assert.notInclude(tf.beacons[i].t_other, "boomerang", "should not have boomerang"); + } + + // Boomerang and config timing parameters + assert.isUndefined(tf.beacons[i]["rt.bmr"]); + assert.isUndefined(tf.beacons[i]["rt.cnf"]); + } + }); + + // + // Beacon 1 + // + describe("Beacon 1 (spa-hard)", function() { + it("Should have sent the first beacon for " + pathName, function() { + var b = tf.beacons[0]; + + assert.include(b.u, pathName); + }); + + it("Should take as long as the longest img load (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + t.validateBeaconWasSentAfter(0, "img.jpg&id=home", 500, 3000, 30000, 0); + } + else { + return this.skip(); + } + }); + + it("Should have a load time (if MutationObserver is supported but NavigationTiming is not)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() === "undefined") { + // start time from l_start or t_start will be used + var b = tf.beacons[0]; + + assert.isDefined(b.t_done); + assert.closeTo(b.t_done, 3000, 200); // MO will wait for the img download + assert.isUndefined(b.t_resp); + assert.isUndefined(b.t_page); + assert.equal(b["rt.start"], "manual"); + } + else { + return this.skip(); + } + }); + + it("Should take as long as the XHRs (if MutationObserver is not supported but NavigationTiming is)", function() { + if (!t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + t.validateBeaconWasSentAfter(0, "widgets.json", 500, 0, 30000, false); + } + else { + return this.skip(); + } + }); + + it("Should not have a load time (if MutationObserver and NavigationTiming are not supported)", function() { + if (!t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() === "undefined") { + var b = tf.beacons[0]; + + assert.isUndefined(b.t_done); + assert.isUndefined(b.t_resp); + assert.isUndefined(b.t_page); + assert.equal(b["rt.start"], "none"); + } + else { + return this.skip(); + } + }); + + it("Should have a t_resp of the root page (if MutationObserver and ResourceTiming are supported)", function() { + if (t.isMutationObserverSupported() && t.isResourceTimingSupported()) { + var pt = window.performance.timing; + var b = tf.beacons[0]; + + assert.equal(b.t_resp, pt.responseStart - pt.navigationStart); + } + else { + return this.skip(); + } + }); + + it("Should have a t_page of total - t_resp (if MutationObserver and ResourceTiming are supported)", function() { + if (t.isMutationObserverSupported() && t.isResourceTimingSupported()) { + var b = tf.beacons[0]; + + assert.equal(b.t_page, b.t_done - b.t_resp); + } + else { + return this.skip(); + } + }); + }); + + // + // Beacon 2 + // + describe("Beacon 2 (spa)", function() { + it("Should have sent the second beacon for /widgets/1", function() { + var b = tf.beacons[1]; + + assert.include(b.u, "/widgets/1"); + }); + + it("Should have sent the second beacon with a timestamp of at least 1 second (if MutationObserver is supported)", function() { + if (t.isMutationObserverSupported()) { + // because of the widget IMG delaying 1 second + var b = tf.beacons[1]; + + assert.operator(b.t_done, ">=", 1000); + } + else { + return this.skip(); + } + }); + + it("Should have sent the second beacon with a timestamp of at least 1 millisecond (if MutationObserver is not supported)", function() { + if (!t.isMutationObserverSupported()) { + // because of the widget IMG delaying 1 second but we couldn't track it because no MO support + var b = tf.beacons[1]; + + assert.operator(b.t_done, ">=", 0); + } + else { + return this.skip(); + } + }); + + it("Should have sent the second beacon with a t_resp value (if MutationObserver and ResourceTiming are supported)", function() { + if (t.isMutationObserverSupported() && t.isResourceTimingSupported()) { + var b = tf.beacons[1]; + + assert.operator(b.t_resp, ">=", 0); + } + else { + return this.skip(); + } + }); + + it("Should have sent the second beacon with a t_page of total - t_resp (if MutationObserver and ResourceTiming are supported)", function() { + if (t.isMutationObserverSupported() && t.isResourceTimingSupported()) { + var b = tf.beacons[1]; + + assert.equal(b.t_page, b.t_done - b.t_resp); + } + else { + return this.skip(); + } + }); + }); + + // + // Beacon 3 + // + describe("Beacon 3 (spa)", function() { + it("Should have sent the third beacon for " + pathName, function() { + var b = tf.beacons[2]; + + assert.include(b.u, pathName); + }); + + it("Should have sent the third with a timestamp of at least 3 seconds (if MutationObserver is supported)", function() { + if (t.isMutationObserverSupported()) { + var b = tf.beacons[2]; + + assert.operator(b.t_done, ">=", 3000); + } + else { + return this.skip(); + } + }); + + it("Should have sent the third with a timestamp of under 1 second (if MutationObserver is not supported)", function() { + if (!t.isMutationObserverSupported()) { + var b = tf.beacons[2]; + + assert.operator(b.t_done, "<=", 1000); + } + else { + return this.skip(); + } + }); + + it("Should have sent the third beacon with a t_resp value (if MutationObserver and ResourceTiming are supported)", function() { + if (t.isMutationObserverSupported() && t.isResourceTimingSupported()) { + var b = tf.beacons[2]; + + // older versions of Chrome have a blank initiatorType instead of `fetch` for Fetch requests. + // If that happens, we won't attribute widgets.json to back-end time + var entry = t.findLastResource("widgets.json"); + + if (entry && entry.initiatorType === "") { + assert.equal(b.t_resp, 0); + } + else { + assert.operator(b.t_resp, ">=", 250); // widgets.json has a 250ms delay + } + } + else { + return this.skip(); + } + }); + + it("Should have sent the third beacon with a t_page of total - t_resp (if MutationObserver and ResourceTiming are supported)", function() { + if (t.isMutationObserverSupported() && t.isResourceTimingSupported()) { + var b = tf.beacons[2]; + + assert.equal(b.t_page, b.t_done - b.t_resp); + } + else { + return this.skip(); + } + }); + }); +}; diff --git a/tests/test-templates/spa/05-route-change-hashtags.js b/tests/test-templates/spa/05-route-change-hashtags.js new file mode 100644 index 000000000..7a68d26ff --- /dev/null +++ b/tests/test-templates/spa/05-route-change-hashtags.js @@ -0,0 +1,153 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ +BOOMR_test.templates.SPA = BOOMR_test.templates.SPA || {}; +BOOMR_test.templates.SPA["05-route-change-hashtags"] = function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent three beacons", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 3); + }); + + it("Should have sent the first beacon as http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + + it("Should have sent all subsequent beacons as http.initiator = spa", function() { + for (var i = 1; i < 2; i++) { + assert.equal(tf.beacons[i]["http.initiator"], "spa"); + } + }); + + it("Should have sent all subsequent beacons have rt.nstart = navigationTiming (if NavigationTiming is supported)", function() { + if (typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + for (var i = 1; i < 2; i++) { + assert.equal(tf.beacons[i]["rt.nstart"], BOOMR.plugins.RT.navigationStart()); + } + } + else { + return this.skip(); + } + }); + + // + // Beacon 1 + // + it("Should have sent the first beacon for 05-route-change-hashtags.html", function() { + var b = tf.beacons[0]; + + assert.isTrue(b.u.indexOf("05-route-change-hashtags.html") !== -1); + }); + + it("Should take as long as the longest img load (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + t.validateBeaconWasSentAfter(0, "img.jpg&id=home", 500, 3000, 30000, 0); + } + else { + return this.skip(); + } + }); + + it("Should take as long as the longest img load (if MutationObserver is supported but NavigationTiming is not)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() === "undefined") { + var b = tf.beacons[0]; + + assert.equal(b.t_done, undefined); + } + else { + return this.skip(); + } + }); + + it("Should take as long as the XHRs (if MutationObserver is not supported but NavigationTiming is)", function() { + if (!t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + t.validateBeaconWasSentAfter(0, "widgets.json", 500, 0, 30000, false); + } + else { + return this.skip(); + } + }); + + it("Shouldn't have a load time (if MutationObserver and NavigationTiming are not supported)", function() { + if (!t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() === "undefined") { + var b = tf.beacons[0]; + + assert.equal(b.t_done, undefined); + assert.equal(b["rt.start"], "none"); + } + else { + return this.skip(); + } + }); + + // + // Beacon 2 + // + it("Should have sent the second beacon for /widgets/1", function() { + var b = tf.beacons[1]; + + assert.isTrue(b.u.indexOf("#/widgets/1") !== -1 || + b.u.indexOf("#widgets/1") !== -1); + }); + + it("Should have sent the second beacon with a timestamp of at least 1 second (if MutationObserver is supported)", function() { + if (t.isMutationObserverSupported()) { + // because of the widget IMG delaying 1 second + var b = tf.beacons[1]; + + assert.operator(b.t_done, ">=", 1000); + } + else { + return this.skip(); + } + }); + + it("Should have sent the second beacon with a timestamp of at least 1 millisecond (if MutationObserver is not supported)", function() { + if (!t.isMutationObserverSupported()) { + // because of the widget IMG delaying 1 second but we couldn't track it because no MO support + var b = tf.beacons[1]; + + assert.operator(b.t_done, ">=", 0); + } + else { + return this.skip(); + } + }); + + // + // Beacon 3 + // + it("Should have sent the third beacon for /05-route-change.html", function() { + var b = tf.beacons[2]; + + assert.isTrue(b.u.indexOf("05-route-change-hashtags.html#") !== -1 || + b.u.indexOf("05-route-change-hashtags.html#/") !== -1); + }); + + it("Should have sent the third with a timestamp of at least 3 seconds (if MutationObserver is supported)", function() { + if (t.isMutationObserverSupported()) { + var b = tf.beacons[2]; + + assert.operator(b.t_done, ">=", 3000); + } + else { + return this.skip(); + } + }); + + it("Should have sent the third with a timestamp of under 1 second (if MutationObserver is not supported)", function() { + if (!t.isMutationObserverSupported()) { + var b = tf.beacons[2]; + + assert.operator(b.t_done, "<=", 1000); + } + else { + return this.skip(); + } + }); +}; diff --git a/tests/test-templates/spa/06-hard-nav-resources.js b/tests/test-templates/spa/06-hard-nav-resources.js new file mode 100644 index 000000000..5d214351a --- /dev/null +++ b/tests/test-templates/spa/06-hard-nav-resources.js @@ -0,0 +1,63 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ +BOOMR_test.templates.SPA = BOOMR_test.templates.SPA || {}; +BOOMR_test.templates.SPA["06-hard-nav-resources"] = function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have only sent one beacon", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 1); + }); + + it("Should take as long as the longest img load (if MutationObserver and NavigationTiming is supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + t.validateBeaconWasSentAfter(0, "delay=5000", 100, 3000, 30000, true); + } + else { + return this.skip(); + } + }); + + it("Should take as long as the longest img load (if MutationObserver is supported but NavigationTiming is not)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() === "undefined") { + var b = tf.beacons[0]; + + assert.equal(b.t_done, undefined); + } + else { + return this.skip(); + } + }); + + it("Should take as long as the XHRs (if MutationObserver is not supported but NavigationTiming is)", function() { + if (!t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + t.validateBeaconWasSentAfter(0, "widgets.json", 100, 0, 30000, true); + } + else { + return this.skip(); + } + }); + + it("Shouldn't have a load time (if MutationObserver and NavigationTiming are not supported)", function() { + if (!t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() === "undefined") { + var b = tf.beacons[0]; + + assert.equal(b.t_done, undefined); + assert.equal(b["rt.start"], "none"); + } + else { + return this.skip(); + } + }); + + it("Should have sent the http.initiator as 'spa_hard'", function() { + var b = tf.lastBeacon(); + + assert.equal(b["http.initiator"], "spa_hard"); + }); +}; diff --git a/tests/test-templates/spa/07-soft-nav-resources.js b/tests/test-templates/spa/07-soft-nav-resources.js new file mode 100644 index 000000000..a0d780058 --- /dev/null +++ b/tests/test-templates/spa/07-soft-nav-resources.js @@ -0,0 +1,252 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ +BOOMR_test.templates.SPA = BOOMR_test.templates.SPA || {}; +BOOMR_test.templates.SPA["07-soft-nav-resources"] = function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent five beacons", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 5); + }); + + it("Should have sent the first beacon as http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + + it("Should have sent all subsequent beacons as http.initiator = spa", function() { + for (var i = 1; i < 4; i++) { + assert.equal(tf.beacons[i]["http.initiator"], "spa"); + } + }); + + it("Should have sent all subsequent beacons have rt.nstart = navigationTiming (if NavigationTiming is supported)", function() { + if (typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + for (var i = 1; i < 4; i++) { + assert.equal(tf.beacons[i]["rt.nstart"], BOOMR.plugins.RT.navigationStart()); + } + } + else { + return this.skip(); + } + }); + + // + // Beacon 1 + // + it("Should have sent the first beacon for /07-soft-nav-resources.html", function() { + var b = tf.beacons[0]; + + assert.isTrue(b.u.indexOf("/07-soft-nav-resources.html") !== -1); + }); + + it("Should have the first beacon take as long as the first img load (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + t.validateBeaconWasSentAfter(0, "img.jpg&id=home", 500, 3000, 30000); + } + else { + return this.skip(); + } + }); + + it("Should have the first beacon take as long as the first img load (if MutationObserver is supported but NavigationTiming is not)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() === "undefined") { + var b = tf.beacons[0]; + + assert.equal(b.t_done, undefined); + } + else { + return this.skip(); + } + }); + + it("Should have the first beacon take as long as the XHRs (if MutationObserver is not supported but NavigationTiming is)", function() { + if (!t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + t.validateBeaconWasSentAfter(0, "widgets.json", 500, 0, 30000, false); + } + else { + return this.skip(); + } + }); + + it("Shouldn't have the first beacon have a load time (if MutationObserver and NavigationTiming are not supported)", function() { + if (!t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() === "undefined") { + var b = tf.beacons[0]; + + assert.equal(b.t_done, undefined); + assert.equal(b["rt.start"], "none"); + } + else { + return this.skip(); + } + }); + + // + // Beacon 2 + // + it("Should have sent the second beacon for /widgets/1", function() { + var b = tf.beacons[1]; + + assert.isTrue(b.u.indexOf("/widgets/1") !== -1); + }); + + it("Should have sent the second beacon with a timestamp of at least 1 seconds (if MutationObserver is supported)", function() { + if (t.isMutationObserverSupported()) { + // because of the widget IMG delaying 1 second + var b = tf.beacons[1]; + + assert.operator(b.t_done, ">=", 1000); + } + else { + return this.skip(); + } + }); + + it("Should have sent the second beacon with a timestamp of at least 1 millisecond (if MutationObserver is not supported)", function() { + if (!t.isMutationObserverSupported()) { + // because of the widget IMG delaying 1 second but we couldn't track it because no MO support + var b = tf.beacons[1]; + + assert.operator(b.t_done, ">=", 0); + } + else { + return this.skip(); + } + }); + + it("Should have sent the second beacon with a timestamp of less than 30 seconds", function() { + // because of the widget IMG delaying 1 second + var b = tf.beacons[1]; + + assert.operator(b.t_done, "<=", 30000); + }); + + // + // Beacon 3 + // + it("Should have sent the third beacon for /widgets/2", function() { + var b = tf.beacons[2]; + + assert.isTrue(b.u.indexOf("/widgets/2") !== -1); + }); + + it("Should have sent the third beacon with a timestamp of at least 2 seconds (if MutationObserver is supported)", function() { + if (t.isMutationObserverSupported()) { + // because of the widget IMG delaying 2 second + var b = tf.beacons[2]; + + assert.operator(b.t_done, ">=", 2000); + } + else { + return this.skip(); + } + }); + + it("Should have sent the third beacon with a timestamp of at least 1 millisecond (if MutationObserver is not supported)", function() { + if (!t.isMutationObserverSupported()) { + // because of the widget IMG delaying 2 seconds but we couldn't track it because no MO support + var b = tf.beacons[2]; + + assert.operator(b.t_done, ">=", 0); + } + }); + + it("Should have sent the third beacon with a timestamp of less than 30 seconds", function() { + // because of the widget IMG delaying 2 second + var b = tf.beacons[2]; + + assert.operator(b.t_done, "<=", 30000); + }); + + // + // Beacon 4 + // + it("Should have sent the fourth beacon for /widgets/3", function() { + var b = tf.beacons[3]; + + assert.isTrue(b.u.indexOf("/widgets/3") !== -1); + }); + + it("Should have sent the fourth beacon with a timestamp of at least 3 seconds (if MutationObserver is supported)", function() { + if (t.isMutationObserverSupported()) { + // because of the widget IMG delaying 3 second + var b = tf.beacons[3]; + + assert.operator(b.t_done, ">=", 3000); + } + else { + return this.skip(); + } + }); + + it("Should have sent the fourth beacon with a timestamp of at least 1 millisecond (if MutationObserver is not supported)", function() { + if (!t.isMutationObserverSupported()) { + // because of the widget IMG delaying 3 seconds but we couldn't track it because no MO support + var b = tf.beacons[3]; + + assert.operator(b.t_done, ">=", 0); + } + else { + return this.skip(); + } + }); + + it("Should have sent the fourth beacon with a timestamp of less than 30 seconds", function() { + // because of the widget IMG delaying 3 second + var b = tf.beacons[3]; + + assert.operator(b.t_done, "<=", 30000); + }); + + // + // Beacon 5 + // + it("Should have sent the fifth beacon for /07-soft-nav-resources.html", function() { + var b = tf.beacons[4]; + + assert.isTrue(b.u.indexOf("/07-soft-nav-resources.html") !== -1); + }); + + it("Should have sent the fifth with a timestamp of less than 10 seconds", function() { + // now that the initial page is cached, it should be a quick navigation + var b = tf.beacons[4]; + + assert.operator(b.t_done, "<=", 10000); + }); + + it("Should have sent the fifth beacon without any NavigationTiming metrics (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + var b = tf.lastBeacon(); + + assert.isUndefined(b.nt_red_cnt); + assert.isUndefined(b.nt_nav_type); + assert.isUndefined(b.nt_nav_st); + assert.isUndefined(b.nt_red_st); + assert.isUndefined(b.nt_red_end); + assert.isUndefined(b.nt_fet_st); + assert.isUndefined(b.nt_dns_st); + assert.isUndefined(b.nt_dns_end); + assert.isUndefined(b.nt_con_st); + assert.isUndefined(b.nt_con_end); + assert.isUndefined(b.nt_req_st); + assert.isUndefined(b.nt_res_st); + assert.isUndefined(b.nt_res_end); + assert.isUndefined(b.nt_domloading); + assert.isUndefined(b.nt_domint); + assert.isUndefined(b.nt_domcontloaded_st); + assert.isUndefined(b.nt_domcontloaded_end); + assert.isUndefined(b.nt_domcomp); + assert.isUndefined(b.nt_load_st); + assert.isUndefined(b.nt_load_end); + assert.isUndefined(b.nt_unload_st); + assert.isUndefined(b.nt_unload_end); + } + else { + return this.skip(); + } + }); +}; diff --git a/tests/test-templates/spa/08-no-images.js b/tests/test-templates/spa/08-no-images.js new file mode 100644 index 000000000..1fb2f3b6a --- /dev/null +++ b/tests/test-templates/spa/08-no-images.js @@ -0,0 +1,37 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ +BOOMR_test.templates.SPA = BOOMR_test.templates.SPA || {}; +BOOMR_test.templates.SPA["08-no-images"] = function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have only sent one beacon", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 1); + }); + + it("Should take as long as the widgets.json take to load (if NavigationTiming is supported)", function() { + if (typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + t.validateBeaconWasSentAfter(0, "widgets.json", 500, 0, 30000, true); + } + else { + return this.skip(); + } + }); + + it("Shouldn't have a load time (if NavigationTiming is not supported)", function() { + if (typeof BOOMR.plugins.RT.navigationStart() === "undefined") { + var b = tf.lastBeacon(); + + assert.equal(b.t_done, undefined); + assert.equal(b["rt.start"], "none"); + } + else { + return this.skip(); + } + }); +}; diff --git a/tests/test-templates/spa/09-autoxhr-after-load.js b/tests/test-templates/spa/09-autoxhr-after-load.js new file mode 100644 index 000000000..a17768e13 --- /dev/null +++ b/tests/test-templates/spa/09-autoxhr-after-load.js @@ -0,0 +1,159 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ +BOOMR_test.templates.SPA = BOOMR_test.templates.SPA || {}; +BOOMR_test.templates.SPA["09-autoxhr-after-load"] = function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have only sent one beacon (if AutoXHR is not enabled)", function(done) { + t.ifAutoXHR( + done, + this.skip.bind(this), + function() { + t.ensureBeaconCount(done, 1); + }); + }); + + it("Should have sent three beacons (if AutoXHR is enabled)", function(done) { + var _this = this; + + t.ifAutoXHR( + done, + function() { + _this.timeout(10000); + t.ensureBeaconCount(done, 3); + }, + this.skip.bind(this)); + }); + + // + // Beacon 1 (page load) + // + describe("Beacon 1 (spa_hard)", function() { + var i = 0; + + it("Should have sent the first beacon as http.initiator = spa_hard", function() { + assert.equal(tf.beacons[i]["http.initiator"], "spa_hard"); + }); + + it("Should send the first beacon (page load) with the time it took to load img.jpg?id=home (if NavigationTiming and MutationObserver is supported)", function(done) { + if (typeof BOOMR.plugins.RT.navigationStart() !== "undefined" && t.isMutationObserverSupported()) { + t.ifAutoXHR( + done, + function() { + t.validateBeaconWasSentAfter(i, "support/img.jpg?id=home", 500, 0, 30000, 0); + done(); + }, + this.skip.bind(this)); + } + else { + this.skip(); + } + }); + + it("Should send the first beacon (page load) with the time it took to load widgets.json (if NavigationTiming is supported and MutationObserver is not)", function(done) { + if (typeof BOOMR.plugins.RT.navigationStart() !== "undefined" && !t.isMutationObserverSupported()) { + t.ifAutoXHR( + done, + function() { + t.validateBeaconWasSentAfter(i, "support/widgets.json", 500, 0, 30000, 0); + done(); + }, + this.skip.bind(this)); + } + else { + this.skip(); + } + }); + + it("Should send the first beacon (page load) without any load time (if NavigationTiming is not supported)", function(done) { + if (typeof BOOMR.plugins.RT.navigationStart() === "undefined") { + t.ifAutoXHR( + done, + function() { + var b = tf.beacons[i]; + + assert.equal(b.t_done, undefined); + assert.equal(b["rt.start"], "none"); + done(); + }, + this.skip.bind(this)); + } + else { + this.skip(); + } + }); + + it("Should send the first beacon (page load) with the page URL as the 'u' parameter", function(done) { + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[i].u, "09-autoxhr-after-load.html"); + done(); + }, + this.skip.bind(this)); + }); + }); + + // + // Beacon 2 (XHR) + // + describe("Beacon 2 (xhr)", function() { + var i = 1; + + it("Should have sent the first beacon as http.initiator = xhr", function() { + assert.equal(tf.beacons[i]["http.initiator"], "xhr"); + }); + + it("Should send the second beacon (XHR) with the 3s it took to load the widgets.json XHR", function(done) { + t.ifAutoXHR( + done, + function() { + assert.closeTo(tf.beacons[i].t_done, 3000, 500); + done(); + }, + this.skip.bind(this)); + }); + + it("Should send the second beacon (XHR) with widgets.json as the 'u' parameter", function(done) { + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[i].u, "widgets.json"); + done(); + }, + this.skip.bind(this)); + }); + }); + + // + // Beacon 3 (XHR) + // + describe("Beacon 3 (xhr)", function() { + var i = 2; + + it("Should have sent the first beacon as http.initiator = xhr", function() { + assert.equal(tf.beacons[i]["http.initiator"], "xhr"); + }); + + it("Should send the third beacon (XHR) with the 1s it took to load the widgets.json XHR", function(done) { + t.ifAutoXHR( + done, + function() { + assert.closeTo(tf.beacons[i].t_done, 1000, 250); + done(); + }, + this.skip.bind(this)); + }); + + it("Should send the third beacon (XHR) with widgets.json as the 'u' parameter", function(done) { + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[i].u, "widgets.json"); + done(); + }, + this.skip.bind(this)); + }); + }); +}; diff --git a/tests/test-templates/spa/10-autoxhr-overlapping.js b/tests/test-templates/spa/10-autoxhr-overlapping.js new file mode 100644 index 000000000..488caa72d --- /dev/null +++ b/tests/test-templates/spa/10-autoxhr-overlapping.js @@ -0,0 +1,103 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ +BOOMR_test.templates.SPA = BOOMR_test.templates.SPA || {}; +BOOMR_test.templates.SPA["10-autoxhr-overlapping"] = function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have only sent one beacon (if AutoXHR is not enabled)", function(done) { + t.ifAutoXHR( + done, + this.skip.bind(this), + function() { + t.ensureBeaconCount(done, 1); + }); + }); + + it("Should have sent two beacons (if AutoXHR is enabled)", function(done) { + var _this = this; + + t.ifAutoXHR( + done, + function() { + _this.timeout(10000); + t.ensureBeaconCount(done, 2); + }, + this.skip.bind(this)); + }); + + // + // Beacon 1 (page load) + // + describe("Beacon 1 (page load)", function() { + it("Should send the first beacon (page load) with the time it took to load widgets.json (if NavigationTiming is supported)", function(done) { + if (typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + t.ifAutoXHR( + done, + function() { + t.validateBeaconWasSentAfter(0, "support/widgets.json", 500, 0, 30000); + done(); + }, + this.skip.bind(this)); + } + else { + this.skip(); + } + }); + + it("Should send the first beacon (page load) without any load time (if NavigationTiming is not supported)", function(done) { + if (typeof BOOMR.plugins.RT.navigationStart() === "undefined") { + t.ifAutoXHR( + done, + function() { + var b = tf.beacons[0]; + + assert.equal(b.t_done, undefined); + assert.equal(b["rt.start"], "none"); + done(); + }, + this.skip.bind(this)); + } + else { + this.skip(); + } + }); + + it("Should send the first beacon (page load) with the page URL as the 'u' parameter", function(done) { + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[0].u, "10-autoxhr-overlapping.html"); + done(); + }, + this.skip.bind(this)); + }); + }); + + // + // Beacon 2 (XHR) + // + describe("Beacon 2 (xhr)", function() { + var i = 1; + + it("Should send the second beacon (XHR) with a t_done that includes both xhr requests", function(done) { + t.ifAutoXHR( + done, + function() { + assert.closeTo(tf.beacons[i].t_done, 5000, 200); + done(); + }, + this.skip.bind(this)); + }); + + it("Should send the second beacon (XHR) with widgets.json&id=1 as the 'u' parameter", function(done) { + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[i].u, "widgets.json&id=1"); + done(); + }, + this.skip.bind(this)); + }); + }); +}; diff --git a/tests/test-templates/spa/11-autoxhr-trigger-additional.js b/tests/test-templates/spa/11-autoxhr-trigger-additional.js new file mode 100644 index 000000000..dbd05ab79 --- /dev/null +++ b/tests/test-templates/spa/11-autoxhr-trigger-additional.js @@ -0,0 +1,126 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ +BOOMR_test.templates.SPA = BOOMR_test.templates.SPA || {}; +BOOMR_test.templates.SPA["11-autoxhr-trigger-additional"] = function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have only sent one beacon (if AutoXHR is not enabled)", function(done) { + t.ifAutoXHR( + done, + this.skip.bind(this), + function() { + t.ensureBeaconCount(done, 1); + }); + }); + + it("Should have sent two beacons (if AutoXHR is enabled)", function(done) { + var _this = this; + + t.ifAutoXHR( + done, + function() { + _this.timeout(10000); + t.ensureBeaconCount(done, 2); + }, + this.skip.bind(this)); + }); + + // + // Beacon 1 (page load) + // + it("Should send the first beacon (page load) with the time it took to load widgets.json (if NavigationTiming is supported)", function(done) { + if (typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + t.ifAutoXHR( + done, + function() { + t.validateBeaconWasSentAfter(0, "support/widgets.json", 500, 0, 30000); + done(); + }, + this.skip.bind(this)); + } + else { + this.skip(); + } + }); + + it("Should send the first beacon (page load) without any load time (if NavigationTiming is not supported)", function(done) { + if (typeof BOOMR.plugins.RT.navigationStart() === "undefined") { + t.ifAutoXHR( + done, + function() { + var b = tf.beacons[0]; + + assert.equal(b.t_done, undefined); + assert.equal(b["rt.start"], "none"); + done(); + }, + this.skip.bind(this)); + } + else { + this.skip(); + } + }); + + it("Should send the first beacon (page load) with the page URL as the 'u' parameter", function(done) { + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[0].u, "11-autoxhr-trigger-additional.html"); + done(); + }, + this.skip.bind(this)); + }); + + // + // Beacon 2 (XHRs) + // + it("Should send the second beacon (XHR) with the 4 seconds it took to load both widgets.json and the image (if MutationObserver is supported)", function(done) { + if (t.isMutationObserverSupported()) { + t.ifAutoXHR( + done, + function() { + var duration, + delta = 500; + + if (t.isResourceTimingSupported()) { + duration = t.findFirstResource("support/widgets.json&id=1").duration + t.findFirstResource("support/img.jpg").duration; + delta = 200; + } + + duration = duration && duration >= 4000 ? duration : 4000; + assert.closeTo(tf.beacons[1].t_done, duration, delta); + done(); + }, + this.skip.bind(this)); + } + else { + this.skip(); + } + }); + + it("Should send the second beacon (XHR) with the 2 seconds it took to load widgets.json (if MutationObserver is not supported)", function(done) { + if (!t.isMutationObserverSupported()) { + t.ifAutoXHR( + done, + function() { + assert.closeTo(tf.beacons[1].t_done, 2000, 500); + done(); + }, + this.skip.bind(this)); + } + else { + this.skip(); + } + }); + + it("Should send the second beacon (XHR) with widgets.json as the 'u' parameter", function(done) { + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[1].u, "widgets.json&id=1"); + done(); + }, + this.skip.bind(this)); + }); +}; diff --git a/tests/test-templates/spa/12-autoxhr-trigger-additional-after-delay.js b/tests/test-templates/spa/12-autoxhr-trigger-additional-after-delay.js new file mode 100644 index 000000000..1d55e8c46 --- /dev/null +++ b/tests/test-templates/spa/12-autoxhr-trigger-additional-after-delay.js @@ -0,0 +1,126 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ +BOOMR_test.templates.SPA = BOOMR_test.templates.SPA || {}; +BOOMR_test.templates.SPA["12-autoxhr-trigger-additional-after-delay"] = function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have only sent one beacon (if AutoXHR is not enabled)", function(done) { + t.ifAutoXHR( + done, + this.skip.bind(this), + function() { + t.ensureBeaconCount(done, 1); + }); + }); + + it("Should have sent two beacons (if AutoXHR is enabled)", function(done) { + var _this = this; + + t.ifAutoXHR( + done, + function() { + _this.timeout(10000); + t.ensureBeaconCount(done, 2); + }, + this.skip.bind(this)); + }); + + // + // Beacon 1 (page load) + // + it("Should send the first beacon (page load) with the time it took to load widgets.json (if NavigationTiming is supported)", function(done) { + if (typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + t.ifAutoXHR( + done, + function() { + t.validateBeaconWasSentAfter(0, "support/widgets.json", 500, 0, 30000); + done(); + }, + this.skip.bind(this)); + } + else { + this.skip(); + } + }); + + it("Should send the first beacon (page load) without any load time (if NavigationTiming is not supported)", function(done) { + if (typeof BOOMR.plugins.RT.navigationStart() === "undefined") { + t.ifAutoXHR( + done, + function() { + var b = tf.beacons[0]; + + assert.equal(b.t_done, undefined); + assert.equal(b["rt.start"], "none"); + done(); + }, + this.skip.bind(this)); + } + else { + this.skip(); + } + }); + + it("Should send the first beacon (page load) with the page URL as the 'u' parameter", function(done) { + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[0].u, "12-autoxhr-trigger-additional-after-delay.html"); + done(); + }, + this.skip.bind(this)); + }); + + // + // Beacon 2 (XHRs) + // + it("Should send the second beacon (XHR) with the 4 seconds it took to load both widgets.json and the image (if MutationObserver is supported)", function(done) { + if (t.isMutationObserverSupported()) { + t.ifAutoXHR( + done, + function() { + var duration, + delta = 500; + + if (t.isResourceTimingSupported()) { + duration = t.findFirstResource("support/widgets.json&id=1").duration + t.findFirstResource("support/img.jpg").duration; + delta = 200; + } + + duration = duration && duration >= 4000 ? duration : 4000; + assert.closeTo(tf.beacons[1].t_done, duration, delta); + done(); + }, + this.skip.bind(this)); + } + else { + this.skip(); + } + }); + + it("Should send the second beacon (XHR) with the 2 seconds it took to load widgets.json (if MutationObserver is not supported)", function(done) { + if (!t.isMutationObserverSupported()) { + t.ifAutoXHR( + done, + function() { + assert.closeTo(tf.beacons[1].t_done, 2000, 500); + done(); + }, + this.skip.bind(this)); + } + else { + this.skip(); + } + }); + + it("Should send the second beacon (XHR) with widgets.json as the 'u' parameter", function(done) { + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[1].u, "widgets.json&id=1"); + done(); + }, + this.skip.bind(this)); + }); +}; diff --git a/tests/test-templates/spa/13-autoxhr-disabled.js b/tests/test-templates/spa/13-autoxhr-disabled.js new file mode 100644 index 000000000..382def3a4 --- /dev/null +++ b/tests/test-templates/spa/13-autoxhr-disabled.js @@ -0,0 +1,83 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ +BOOMR_test.templates.SPA = BOOMR_test.templates.SPA || {}; +BOOMR_test.templates.SPA["13-autoxhr-disabled"] = function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have only sent one beacon (if AutoXHR is not enabled)", function(done) { + var that = this; + + t.ifAutoXHR( + done, + this.skip.bind(this), + function() { + // wait 3 seconds to make sure another beacon wasn't sent + that.timeout(10000); + setTimeout(function() { + t.ensureBeaconCount(done, 1); + }, 3000); + }); + }); + + it("Should have only sent one beacon (if AutoXHR is enabled)", function(done) { + var that = this; + + t.ifAutoXHR( + done, + function() { + // wait 3 seconds to make sure another beacon wasn't sent + that.timeout(10000); + setTimeout(function() { + t.ensureBeaconCount(done, 1); + }, 3000); + }, + this.skip.bind(this)); + }); + + // + // Beacon 1 (page load) + // + it("Should send the first beacon (page load) with the time it took to load widgets.json (if NavigationTiming is supported)", function(done) { + if (typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + t.ifAutoXHR( + done, + function() { + t.validateBeaconWasSentAfter(0, "support/widgets.json", 500, 0, 30000); + done(); + }, + this.skip.bind(this)); + } + else { + this.skip(); + } + }); + + it("Should send the first beacon (page load) without any load time (if NavigationTiming is not supported)", function(done) { + if (typeof BOOMR.plugins.RT.navigationStart() === "undefined") { + t.ifAutoXHR( + done, + function() { + var b = tf.beacons[0]; + + assert.equal(b.t_done, undefined); + assert.equal(b["rt.start"], "none"); + done(); + }, + this.skip.bind(this)); + } + else { + this.skip(); + } + }); + + it("Should send the first beacon (page load) with the page URL as the 'u' parameter", function(done) { + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[0].u, "13-autoxhr-disabled.html"); + done(); + }, + this.skip.bind(this)); + }); +}; diff --git a/tests/test-templates/spa/14-autoxhr-before-page-load.js b/tests/test-templates/spa/14-autoxhr-before-page-load.js new file mode 100644 index 000000000..94f2e77c7 --- /dev/null +++ b/tests/test-templates/spa/14-autoxhr-before-page-load.js @@ -0,0 +1,63 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ +BOOMR_test.templates.SPA = BOOMR_test.templates.SPA || {}; +BOOMR_test.templates.SPA["14-autoxhr-before-page-load"] = function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have only sent one beacon", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 1); + }); + + it("Should take as long as the longest img load (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + t.validateBeaconWasSentAfter(0, "img.jpg", 250, 3000, 30000, true); + } + else { + return this.skip(); + } + }); + + it("Should not have a load time (if MutationObserver is supported but NavigationTiming is not)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() === "undefined") { + var b = tf.lastBeacon(); + + assert.equal(b.t_done, undefined); + } + else { + return this.skip(); + } + }); + + it("Should take as long as the XHRs (if MutationObserver is not supported but NavigationTiming is)", function() { + if (!t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + t.validateBeaconWasSentAfter(0, "widgets.json", 250, 0, 30000, true); + } + else { + return this.skip(); + } + }); + + it("Shouldn't have a load time (if MutationObserver and NavigationTiming are not supported)", function() { + if (!t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() === "undefined") { + var b = tf.lastBeacon(); + + assert.equal(b.t_done, undefined); + assert.equal(b["rt.start"], "none"); + } + else { + return this.skip(); + } + }); + + it("Should have sent the http.initiator as 'spa_hard'", function() { + var b = tf.lastBeacon(); + + assert.equal(b["http.initiator"], "spa_hard"); + }); +}; diff --git a/tests/test-templates/spa/15-delayed-boomerang.js b/tests/test-templates/spa/15-delayed-boomerang.js new file mode 100644 index 000000000..7977cf03d --- /dev/null +++ b/tests/test-templates/spa/15-delayed-boomerang.js @@ -0,0 +1,21 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ +BOOMR_test.templates.SPA = BOOMR_test.templates.SPA || {}; +BOOMR_test.templates.SPA["15-delayed-boomerang"] = function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have only sent one beacon", function() { + // only one beacon should've been sent + assert.equal(tf.beacons.length, 1); + }); + + it("Should have sent a spa hard beacon", function() { + // only one beacon should've been sent + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); +}; diff --git a/tests/test-templates/spa/16-autoxhr-after-load-with-mutation.js b/tests/test-templates/spa/16-autoxhr-after-load-with-mutation.js new file mode 100644 index 000000000..91698f9ef --- /dev/null +++ b/tests/test-templates/spa/16-autoxhr-after-load-with-mutation.js @@ -0,0 +1,119 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ +BOOMR_test.templates.SPA = BOOMR_test.templates.SPA || {}; +BOOMR_test.templates.SPA["16-autoxhr-after-load-with-mutation"] = function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have only sent one beacon (if AutoXHR is not enabled)", function(done) { + t.ifAutoXHR( + done, + this.skip.bind(this), + function() { + t.ensureBeaconCount(done, 1); + }); + }); + + it("Should have sent three beacons (if AutoXHR is enabled)", function(done) { + var _this = this; + + t.ifAutoXHR( + done, + function() { + _this.timeout(10000); + t.ensureBeaconCount(done, 3); + }, + this.skip.bind(this)); + }); + + // + // Beacon 1 (page load) + // + it("Should send the first beacon (page load) with the time it took to load widgets.json (if NavigationTiming is supported)", function(done) { + if (typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + t.ifAutoXHR( + done, + function() { + t.validateBeaconWasSentAfter(0, "support/widgets.json", 500, 0, 30000); + done(); + }, + this.skip.bind(this)); + } + else { + this.skip(); + } + }); + + it("Should send the first beacon (page load) without any load time (if NavigationTiming is not supported)", function(done) { + if (typeof BOOMR.plugins.RT.navigationStart() === "undefined") { + t.ifAutoXHR( + done, + function() { + var b = tf.beacons[0]; + + assert.equal(b.t_done, undefined); + assert.equal(b["rt.start"], "none"); + done(); + }, + this.skip.bind(this)); + } + else { + this.skip(); + } + }); + + it("Should send the first beacon (page load) with the page URL as the 'u' parameter", function(done) { + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[0].u, "16-autoxhr-after-load-with-mutation.html"); + done(); + }); + }); + + // + // Beacon 2 (XHR) + // + it("Should send the second beacon (XHR) with the 3s it took to load the widgets.json XHR", function(done) { + t.ifAutoXHR( + done, + function() { + assert.closeTo(tf.beacons[1].t_done, 3000, 500); + done(); + }, + this.skip.bind(this)); + }); + + it("Should send the second beacon (XHR) with widgets.json as the 'u' parameter", function(done) { + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[1].u, "widgets.json"); + done(); + }, + this.skip.bind(this)); + }); + + // + // Beacon 3 (XHR) + // + it("Should send the third beacon (XHR) with the 1s it took to load the widgets.json XHR", function(done) { + t.ifAutoXHR( + done, + function() { + assert.closeTo(tf.beacons[2].t_done, 1000, 250); + done(); + }, + this.skip.bind(this)); + }); + + it("Should send the third beacon (XHR) with widgets.json as the 'u' parameter", function(done) { + t.ifAutoXHR( + done, + function() { + assert.include(tf.beacons[2].u, "widgets.json"); + done(); + }, + this.skip.bind(this)); + }); +}; diff --git a/tests/test-templates/spa/17-wait.js b/tests/test-templates/spa/17-wait.js new file mode 100644 index 000000000..524a4b975 --- /dev/null +++ b/tests/test-templates/spa/17-wait.js @@ -0,0 +1,167 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ +BOOMR_test.templates.SPA = BOOMR_test.templates.SPA || {}; +BOOMR_test.templates.SPA["17-wait"] = function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent four beacons", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 4); + }); + + it("Should have sent the first beacon as http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + + it("Should have sent all subsequent beacons as http.initiator = spa", function() { + for (var i = 1; i < 3; i++) { + assert.equal(tf.beacons[i]["http.initiator"], "spa"); + } + }); + + // + // Beacon 1 + // + describe("Beacon 1 (spa_hard)", function() { + var i = 0; + + it("Should have sent the first beacon for *-wait.html", function() { + var b = tf.beacons[i]; + + assert.isTrue(b.u.indexOf("-wait.html") !== -1); + }); + + it("Should have sent the first beacon without an additional wait (if ResourceTiming is supported)", function() { + if (t.isResourceTimingSupported()) { + var b = tf.beacons[i]; + // t_done should be close to the end time of the first request for widgets.json + var r = t.findFirstResource("widgets.json"); + + assert.isDefined(b.t_done); + assert.closeTo(r.responseEnd /* HR timestamp */, b.t_done, 300); + } + else { + this.skip(); + } + }); + }); + + // + // Beacon 2 + // + describe("Beacon 2 (spa)", function() { + var i = 1; + + it("Should have sent the second beacon for /widgets/1", function() { + var b = tf.beacons[i]; + + assert.isTrue(b.u.indexOf("/widgets/1") !== -1); + }); + + it("Should have sent the second beacon with the time it took to run the 5 second wait", function(done) { + t.ifAutoXHR( + done, + function() { + var navStart = BOOMR.plugins.RT.navigationStart(); + + assert.operator(tf.beacons[i].t_done, ">=", 5000); + assert.operator(tf.beacons[i].t_done, "<", window.spaWaitCompleteTimes[i - 1] - navStart + 200); + done(); + }, + this.skip.bind(this)); + }); + + it("Should have sent the second beacon with the end time (rt.end) of when the 5 second wait fired", function(done) { + t.ifAutoXHR( + done, + function() { + assert.closeTo(tf.beacons[i]["rt.end"], window.spaWaitCompleteTimes[i - 1], 200); + done(); + }, + this.skip.bind(this)); + }); + }); + + // + // Beacon 3 + // + describe("Beacon 3 (spa)", function() { + var i = 2; + + it("Should have sent the third beacon for /widgets/2", function() { + var b = tf.beacons[i]; + + assert.isTrue(b.u.indexOf("/widgets/2") !== -1); + }); + + it("Should have sent the third with a timestamp of at least 2s (if MutationObserver is supported)", function(done) { + if (t.isMutationObserverSupported()) { + t.ifAutoXHR( + done, + function() { + var b = tf.beacons[i], + r, + navStart = BOOMR.plugins.RT.navigationStart(); + + assert.operator(b.t_done, ">=", 2000); + + if (t.isResourceTimingSupported() && typeof navStart !== "undefined") { + r = t.findFirstResource("support/img.jpg&id=2"); + assert.operator(b["rt.end"], ">=", Math.floor(navStart + r.responseEnd)); + assert.closeTo(b["rt.end"], Math.floor(navStart + r.responseEnd), 200); + } + else { + assert.operator(b.t_done, "<", 3000); // depending on app, could be up to 250ms xhr and 2s img + } + + done(); + }, + this.skip.bind(this)); + } + else { + return this.skip(); + } + }); + + it("Should have sent the third with a timestamp of at least 1ms (if MutationObserver is not supported)", function(done) { + if (!t.isMutationObserverSupported()) { + t.ifAutoXHR( + done, + function() { + assert.operator(tf.beacons[i].t_done, ">=", 1); + assert.operator(tf.beacons[i].t_done, "<", 300); + done(); + }, + this.skip.bind(this)); + } + else { + return this.skip(); + } + }); + }); + + // + // Beacon 4 + // + describe("Beacon 4 (spa)", function() { + var i = 3; + + it("Should have sent the fourth beacon for *-wait.html", function() { + var b = tf.beacons[i]; + + assert.isTrue(b.u.indexOf("-wait.html") !== -1); + }); + + it("Should have sent the fourth beacon with a timestamp of less than 1 second", function() { + // now that the initial page is cached, it should be a quick navigation + var b = tf.beacons[i]; + + assert.operator(b.t_done, "<=", 1000); + }); + }); +}; diff --git a/tests/test-templates/spa/18-autoxhr-before-page-load-alwayssendxhr.js b/tests/test-templates/spa/18-autoxhr-before-page-load-alwayssendxhr.js new file mode 100644 index 000000000..c09affc62 --- /dev/null +++ b/tests/test-templates/spa/18-autoxhr-before-page-load-alwayssendxhr.js @@ -0,0 +1,225 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ +BOOMR_test.templates.SPA = BOOMR_test.templates.SPA || {}; +BOOMR_test.templates.SPA["18-autoxhr-before-page-load-alwayssendxhr"] = function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + var BEACON_VAR_RT_MAP = { + "nt_con_end": "connectEnd", + "nt_con_st": "connectStart", + "nt_dns_end": "domainLookupEnd", + "nt_dns_st": "domainLookupStart", + // "nt_domint": maps to the DOM's interactive state + "nt_fet_st": "fetchStart", + // "nt_load_end": maps to loadEventEnd of the XHR + // "nt_load_st": maps to loadEventEnd of the XHR + "nt_req_st": "requestStart", + "nt_res_end": "responseEnd", + "nt_res_st": "responseStart" + }; + + var XHR_BEACONS = [1, 2]; + var SPA_BEACONS = [0]; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent three beacons (AutoXHR is enabled)", function(done) { + if (BOOMR.plugins.AutoXHR) { + this.timeout(10000); + t.ensureBeaconCount(done, 3); + } + else { + return this.skip(); + } + }); + + it("Should have sent one beacon (if AutoXHR is not enabled)", function() { + if (!BOOMR.plugins.AutoXHR) { + assert.equal(tf.beacons.length, 1); + } + else { + return this.skip(); + } + }); + + // + // XHR beacons + // + it("Should have set http.initiator = 'xhr' on the XHR beacons (if AutoXHR is enabled)", function() { + if (BOOMR.plugins.AutoXHR) { + for (var k in XHR_BEACONS) { + if (XHR_BEACONS.hasOwnProperty(k)) { + var i = XHR_BEACONS[k]; + + assert.equal(tf.beacons[i]["http.initiator"], "xhr"); + } + } + } + else { + return this.skip(); + } + }); + + it("Should have set rt.start = 'manual' on the XHR beacons (if AutoXHR is enabled)", function() { + if (BOOMR.plugins.AutoXHR) { + for (var k in XHR_BEACONS) { + if (XHR_BEACONS.hasOwnProperty(k)) { + var i = XHR_BEACONS[k]; + + assert.equal(tf.beacons[i]["rt.start"], "manual"); + } + } + } + else { + return this.skip(); + } + }); + + it("Should have set beacon's nt_* timestamps accurately (if AutoXHR is enabled and NavigationTiming is supported)", function() { + if (BOOMR.plugins.AutoXHR && t.isNavigationTimingSupported()) { + for (var k in XHR_BEACONS) { + if (XHR_BEACONS.hasOwnProperty(k)) { + var i = XHR_BEACONS[k]; + var b = tf.beacons[i]; + + var res = t.findFirstResource(b.u); + var st = BOOMR.window.performance.timing.navigationStart; + + for (var beaconProp in BEACON_VAR_RT_MAP) { + var resTime = Math.floor(res[BEACON_VAR_RT_MAP[beaconProp]] + st); + + assert.equal( + b[beaconProp], + resTime, + "Beacon #" + (i + 1) + ": " + beaconProp + "=" + b[beaconProp] + " vs " + BEACON_VAR_RT_MAP[beaconProp] + "=" + resTime); + } + } + } + } + else { + return this.skip(); + } + }); + + it("Should have set pgu = the page's location on the XHR beacons (if AutoXHR is enabled)", function() { + if (BOOMR.plugins.AutoXHR) { + for (var k in XHR_BEACONS) { + if (XHR_BEACONS.hasOwnProperty(k)) { + var i = XHR_BEACONS[k]; + + assert.include(BOOMR.window.location.href, tf.beacons[i].pgu); + } + } + } + else { + return this.skip(); + } + }); + + it("Should have set nt_load_end==nt_load_st==rt.end on the XHR beacons (if AutoXHR is enabled)", function() { + if (BOOMR.plugins.AutoXHR) { + for (var k in XHR_BEACONS) { + if (XHR_BEACONS.hasOwnProperty(k)) { + var i = XHR_BEACONS[k]; + var b = tf.beacons[i]; + + assert.equal(b.nt_load_end, b.nt_load_st); + assert.equal(b.nt_load_end, b["rt.end"]); + } + } + } + else { + return this.skip(); + } + }); + + it("Should have set t_done = rt.end - nt_fet_st for the XHR beacons (if AutoXHR is enabled and NavigationTiming is supported)", function() { + if (BOOMR.plugins.AutoXHR && t.isNavigationTimingSupported()) { + for (var k in XHR_BEACONS) { + if (XHR_BEACONS.hasOwnProperty(k)) { + var i = XHR_BEACONS[k]; + var b = tf.beacons[i]; + + assert.equal(b.t_done, b["rt.end"] - b.nt_fet_st); + } + } + } + else { + return this.skip(); + } + }); + + it("Should have set t_resp = nt_res_end - nt_fet_st for the XHR beacons (if AutoXHR is enabled and NavigationTiming is supported)", function() { + if (BOOMR.plugins.AutoXHR && t.isNavigationTimingSupported()) { + for (var k in XHR_BEACONS) { + if (XHR_BEACONS.hasOwnProperty(k)) { + var i = XHR_BEACONS[k]; + var b = tf.beacons[i]; + + assert.equal(b.t_resp, b.nt_res_end - b.nt_fet_st); + } + } + } + else { + return this.skip(); + } + }); + + it("Should have set t_page = t_done - t_resp for the XHR beacons (if AutoXHR is enabled)", function() { + if (BOOMR.plugins.AutoXHR) { + for (var k in XHR_BEACONS) { + if (XHR_BEACONS.hasOwnProperty(k)) { + var i = XHR_BEACONS[k]; + var b = tf.beacons[i]; + + assert.closeTo(b.t_page, b.t_done - b.t_resp, 100); + } + } + } + else { + return this.skip(); + } + }); + + // + // First beacon: XHR home.html + // + it("Should have contain home.html for the first beacon's URL (if AutoXHR is enabled)", function() { + if (BOOMR.plugins.AutoXHR) { + var b1 = tf.beacons[XHR_BEACONS[0]]; + var b2 = tf.beacons[XHR_BEACONS[1]]; + + assert.equal(t.checkStringInArray("home.html", [b1.u, b2.u]).length, 1, "Neither of the 2 XHR beacons had the string we were looking for."); + } + else { + return this.skip(); + } + }); + + // + // Second beacon: XHR widgets.json + // + it("Should have contain widgets.json for the first beacon's URL (if AutoXHR is enabled)", function() { + if (BOOMR.plugins.AutoXHR) { + var b1 = tf.beacons[XHR_BEACONS[0]]; + var b2 = tf.beacons[XHR_BEACONS[1]]; + + assert.equal(t.checkStringInArray("widgets.json", [b1.u, b2.u]).length, 1, "Neither of the 2 XHR beacons had the string we were looking for."); + } + else { + return this.skip(); + } + }); + + // + // Third beacon: SPA + // + it("Should have set http.initiator = 'spa_hard' on the last beacon", function() { + var b = tf.beacons[SPA_BEACONS[0]]; + + assert.equal(b["http.initiator"], "spa_hard"); + }); +}; diff --git a/tests/test-templates/spa/19-autoxhr-during-nav-alwayssendxhr.js b/tests/test-templates/spa/19-autoxhr-during-nav-alwayssendxhr.js new file mode 100644 index 000000000..f54fbc01d --- /dev/null +++ b/tests/test-templates/spa/19-autoxhr-during-nav-alwayssendxhr.js @@ -0,0 +1,300 @@ + +/* eslint-env mocha */ +/* global BOOMR_test,assert */ +BOOMR_test.templates.SPA = BOOMR_test.templates.SPA || {}; +BOOMR_test.templates.SPA["19-autoxhr-during-nav-alwayssendxhr"] = function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + // + // Beacon order: + // + // home.html + // widgets.json + // spa_hard + // widget.html + // widget.json + // spa + // widgets.json + // spa + // + // Note this scenario changes a bit if MutationObserver isn't supported. + // + var BEACONS_SENT = 8; + var BEACONS_SENT_SPA = 2; + var BEACONS_SENT_SPA_HARD = 1; + var BEACONS_SENT_XHR = 5; + + // + // Beacons probably come in in the above order, but for non-MutationObserver clients, + // they may be slightly out of order. Iterate over all beacons first to bucket them. + // + function getBeaconByType() { + var beacons = { "spa_hard": [], "spa": [], "xhr": []}; + + for (var type in beacons) { + if (beacons.hasOwnProperty(type)) { + for (var j = 0; j < tf.beacons.length; j++) { + var beacon = tf.beacons[j]; + + if (beacon["http.initiator"] === type) { + beacons[type].push(beacon); + } + } + } + } + + return beacons; + } + + var BEACON_VAR_RT_MAP = { + "nt_con_end": "connectEnd", + "nt_con_st": "connectStart", + "nt_dns_end": "domainLookupEnd", + "nt_dns_st": "domainLookupStart", + // "nt_domint": maps to the DOM's interactive state + "nt_fet_st": "fetchStart", + // "nt_load_end": maps to loadEventEnd of the XHR + // "nt_load_st": maps to loadEventEnd of the XHR + "nt_req_st": "requestStart", + "nt_res_end": "responseEnd", + "nt_res_st": "responseStart" + }; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent " + BEACONS_SENT + " beacons (AutoXHR is enabled and MutationObserver is supported)", function(done) { + if (BOOMR.plugins.AutoXHR && t.isMutationObserverSupported()) { + this.timeout(10000); + t.ensureBeaconCount(done, BEACONS_SENT); + } + else { + return this.skip(); + } + }); + + it("Should have sent " + BEACONS_SENT_SPA + " spa beacons (AutoXHR is enabled and MutationObserver is supported)", function() { + if (BOOMR.plugins.AutoXHR && t.isMutationObserverSupported()) { + assert.equal(getBeaconByType().spa.length, BEACONS_SENT_SPA); + } + else { + return this.skip(); + } + }); + + it("Should have sent " + BEACONS_SENT_SPA_HARD + " spa_hard beacons (AutoXHR is enabled and MutationObserver is supported)", function() { + if (BOOMR.plugins.AutoXHR && t.isMutationObserverSupported()) { + assert.equal(getBeaconByType().spa_hard.length, BEACONS_SENT_SPA_HARD); + } + else { + return this.skip(); + } + }); + + it("Should have sent " + BEACONS_SENT_XHR + " xhr beacons (AutoXHR is enabled and MutationObserver is supported)", function() { + if (BOOMR.plugins.AutoXHR && t.isMutationObserverSupported()) { + assert.equal(getBeaconByType().xhr.length, BEACONS_SENT_XHR); + } + else { + return this.skip(); + } + }); + + it("Should have sent one beacon (if AutoXHR is not enabled)", function(done) { + if (!BOOMR.plugins.AutoXHR) { + this.timeout(10000); + t.ensureBeaconCount(done, 1); + } + else { + return this.skip(); + } + }); + + // + // XHR beacons + // + it("Should have set http.initiator = 'xhr' on the XHR beacons (if AutoXHR is enabled and MutationObserver is supported)", function() { + if (BOOMR.plugins.AutoXHR && t.isMutationObserverSupported()) { + var beacons = getBeaconByType(); + + for (var k in beacons.xhr) { + if (beacons.xhr.hasOwnProperty(k)) { + assert.equal(beacons.xhr[k]["http.initiator"], "xhr", "XHR for beacon #" + k); + } + } + } + else { + return this.skip(); + } + }); + + it("Should have set rt.start = 'manual' on the XHR beacons (if AutoXHR is enabled and MutationObserver is supported)", function() { + if (BOOMR.plugins.AutoXHR && t.isMutationObserverSupported()) { + var beacons = getBeaconByType(); + + for (var k in beacons.xhr) { + if (beacons.xhr.hasOwnProperty(k)) { + assert.equal(beacons.xhr[k]["rt.start"], "manual"); + } + } + } + else { + return this.skip(); + } + }); + + it("Should have set beacon's nt_* timestamps accurately (if AutoXHR is enabled and NavigationTiming is supported and MutationObserver is supported)", function() { + if (BOOMR.plugins.AutoXHR && t.isNavigationTimingSupported() && t.isMutationObserverSupported()) { + var beacons = getBeaconByType(); + + for (var k in beacons.xhr) { + if (beacons.xhr.hasOwnProperty(k)) { + var b = beacons.xhr[k]; + + var res = t.findFirstResource(b.u); + var st = BOOMR.window.performance.timing.navigationStart; + + for (var beaconProp in BEACON_VAR_RT_MAP) { + var resTime = Math.floor(res[BEACON_VAR_RT_MAP[beaconProp]] + st); + + assert.equal( + b[beaconProp], + resTime, + "Beacon #" + k + ": " + beaconProp + "=" + b[beaconProp] + + " vs " + BEACON_VAR_RT_MAP[beaconProp] + "=" + resTime + + " (" + b.u + ")"); + } + } + } + } + else { + return this.skip(); + } + }); + + it("Should have set pgu = the page's location on the XHR beacons (if AutoXHR is enabled and MutationObserver is supported)", function() { + if (BOOMR.plugins.AutoXHR && t.isMutationObserverSupported()) { + var beacons = getBeaconByType(); + + for (var k in beacons.xhr) { + if (beacons.xhr.hasOwnProperty(k)) { + assert.include(BOOMR.window.location.href, beacons.xhr[k].pgu); + } + } + } + else { + return this.skip(); + } + }); + + it("Should have set nt_load_end==nt_load_st==rt.end on the XHR beacons (if AutoXHR is enabled and MutationObserver is supported)", function() { + if (BOOMR.plugins.AutoXHR && t.isMutationObserverSupported()) { + var beacons = getBeaconByType(); + + for (var k in beacons.xhr) { + if (beacons.xhr.hasOwnProperty(k)) { + var b = beacons.xhr[k]; + + assert.equal(b.nt_load_end, b.nt_load_st); + assert.equal(b.nt_load_end, b["rt.end"]); + } + } + } + else { + return this.skip(); + } + }); + + it("Should have set t_done = rt.end - nt_fet_st for the XHR beacons (if AutoXHR is enabled and NavigationTiming is supported and MutationObserver is supported)", function() { + if (BOOMR.plugins.AutoXHR && t.isNavigationTimingSupported() && t.isMutationObserverSupported()) { + var beacons = getBeaconByType(); + + for (var k in beacons.xhr) { + if (beacons.xhr.hasOwnProperty(k)) { + var b = beacons.xhr[k]; + + assert.equal(b.t_done, b["rt.end"] - b.nt_fet_st); + } + } + } + else { + return this.skip(); + } + }); + + it("Should have set t_resp = nt_res_end - nt_fet_st for the XHR beacons (if AutoXHR is enabled and NavigationTiming is supported and MutationObserver is supported)", function() { + if (BOOMR.plugins.AutoXHR && t.isNavigationTimingSupported() && t.isMutationObserverSupported()) { + var beacons = getBeaconByType(); + + for (var k in beacons.xhr) { + if (beacons.xhr.hasOwnProperty(k)) { + var b = beacons.xhr[k]; + + assert.equal(b.t_resp, b.nt_res_end - b.nt_fet_st); + } + } + } + else { + return this.skip(); + } + }); + + it("Should have set t_page = t_done - t_resp for the XHR beacons (if AutoXHR is enabled and MutationObserver is supported)", function() { + if (BOOMR.plugins.AutoXHR && t.isMutationObserverSupported()) { + var beacons = getBeaconByType(); + + for (var k in beacons.xhr) { + if (beacons.xhr.hasOwnProperty(k)) { + var b = beacons.xhr[k]; + + assert.equal(b.t_page, b.t_done - b.t_resp); + } + } + } + else { + return this.skip(); + } + }); + + // + // Test all Hard Beacons + // + it("Should have set http.initiator = 'spa_hard' on the first SPA beacon (if MutationObserver is supported)", function() { + if (BOOMR.plugins.AutoXHR && t.isMutationObserverSupported()) { + var beacons = getBeaconByType(); + + for (var k in beacons.spa_hard) { + if (beacons.spa_hard.hasOwnProperty(k)) { + var b = beacons.spa_hard[k]; + + assert.equal(b["http.initiator"], "spa_hard"); + } + } + } + else { + return this.skip(); + } + }); + + // + // Test all Soft Beacons + // + it("Should have set http.initiator = 'spa' on the next SPA beacons (if MutationObserver is supported)", function() { + if (BOOMR.plugins.AutoXHR && t.isMutationObserverSupported()) { + var beacons = getBeaconByType(); + + for (var k in beacons.spa) { + if (beacons.spa.hasOwnProperty(k)) { + var b = beacons.spa[k]; + + assert.equal(b["http.initiator"], "spa"); + } + } + } + else { + return this.skip(); + } + }); +}; diff --git a/tests/test-templates/spa/20-no-resources.js b/tests/test-templates/spa/20-no-resources.js new file mode 100644 index 000000000..62de3ba73 --- /dev/null +++ b/tests/test-templates/spa/20-no-resources.js @@ -0,0 +1,64 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ +BOOMR_test.templates.SPA = BOOMR_test.templates.SPA || {}; +BOOMR_test.templates.SPA["20-no-resources"] = function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent three beacons", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 3); + }); + + it("Should have sent the first beacons as http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + + it("Should have sent the rest of the beacons as http.initiator = spa", function() { + for (var i = 1; i < 2; i++) { + assert.equal(tf.beacons[i]["http.initiator"], "spa"); + } + }); + + // + // Beacon 1 + // + it("Should have sent the first beacon for /20-no-resources.html", function() { + var b = tf.beacons[0]; + + assert.isTrue(b.u.indexOf("/20-no-resources.html") !== -1); + }); + + // + // Beacon 2 + // + it("Should have sent the second beacon for /empty", function() { + var b = tf.beacons[1]; + + assert.isTrue(b.u.indexOf("/empty") !== -1); + }); + + it("Should have sent the second beacon with a timestamp of 1ms (if MutationObserver is supported)", function() { + if (t.isMutationObserverSupported()) { + var b = tf.beacons[1]; + + assert.equal(b.t_done, 1); + } + else { + return this.skip(); + } + }); + + // + // Beacon 3 + // + it("Should have sent the third beacon for /20-no-resources.html", function() { + var b = tf.beacons[2]; + + assert.isTrue(b.u.indexOf("/20-no-resources.html") !== -1); + }); +}; diff --git a/tests/test-templates/spa/21-constant-mutations.js b/tests/test-templates/spa/21-constant-mutations.js new file mode 100644 index 000000000..fb99356b0 --- /dev/null +++ b/tests/test-templates/spa/21-constant-mutations.js @@ -0,0 +1,26 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ +BOOMR_test.templates.SPA = BOOMR_test.templates.SPA || {}; +BOOMR_test.templates.SPA["21-constant-mutations"] = function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent 1 beacon", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 1); + }); + + it("Should have sent the beacon as http.initiator = spa_hard", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + + it("Should have sent the first beacon for /21-constant-mutations.html", function() { + var b = tf.beacons[0]; + + assert.isTrue(b.u.indexOf("/21-constant-mutations.html") !== -1); + }); +}; diff --git a/tests/test-templates/spa/22-config-after-onload.js b/tests/test-templates/spa/22-config-after-onload.js new file mode 100644 index 000000000..a37e529a0 --- /dev/null +++ b/tests/test-templates/spa/22-config-after-onload.js @@ -0,0 +1,34 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ +BOOMR_test.templates.SPA = BOOMR_test.templates.SPA || {}; +BOOMR_test.templates.SPA["22-config-after-onload"] = function() { + var t = BOOMR_test; + + it("Should have sent two beacons (if MutationObserver is supported)", function(done) { + this.timeout(10000); + + // 2 beacons because of the forced BOOMR.page_ready() call + + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 2); + }, + this.skip.bind(this)); + }); + + it("Should have sent one beacon (if MutationObserver is not supported)", function(done) { + this.timeout(10000); + + t.ifAutoXHR( + done, + this.skip.bind(this), + function() { + t.ensureBeaconCount(done, 1); + }); + }); + + it("Should not have fired page_ready after onload", function() { + assert.isFalse(BOOMR.plugins.hold.fired); + }); +}; diff --git a/tests/test-templates/spa/23-hard-wait-for-onload.js b/tests/test-templates/spa/23-hard-wait-for-onload.js new file mode 100644 index 000000000..fd1292a5f --- /dev/null +++ b/tests/test-templates/spa/23-hard-wait-for-onload.js @@ -0,0 +1,31 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ +BOOMR_test.templates.SPA = BOOMR_test.templates.SPA || {}; +BOOMR_test.templates.SPA["23-hard-wait-for-onload"] = function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have only sent one beacon", function(done) { + this.timeout(10000); + // only one beacon should've been sent + t.ensureBeaconCount(done, 1); + }); + + it("Should have been a spa_hard beacon (if MutationObserver and NavigationTiming are supported)", function() { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + }); + + it("Should have fired after onload (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + var b = tf.beacons[0]; + + assert.notEqual(b.nt_load_st, 0, "performance.timing.loadEventStart should not be 0"); + + assert.operator(b.t_done, ">=", b.nt_load_end - b.nt_nav_st - 1); // in FF, the timings are rounded (may need to change this to -2) + } + else { + // should be greater than the 5s for the /blackhole request + assert.operator(b.t_done, ">=", 5000); + } + }); +}; diff --git a/tests/test-templates/spa/24-route-filter.js b/tests/test-templates/spa/24-route-filter.js new file mode 100644 index 000000000..664553e27 --- /dev/null +++ b/tests/test-templates/spa/24-route-filter.js @@ -0,0 +1,134 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ +BOOMR_test.templates.SPA = BOOMR_test.templates.SPA || {}; +BOOMR_test.templates.SPA["24-route-filter"] = function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + var pathName = window.location.pathname; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent three beacons", function(done) { + this.timeout(10000); + t.ensureBeaconCount(done, 3); + }); + + // + // Beacon 1 + // + describe("Beacon 1 (spa_hard)", function() { + var i = 0; + + it("Should have sent the first beacon as http.initiator = spa_hard", function() { + assert.equal(tf.beacons[i]["http.initiator"], "spa_hard"); + }); + + it("Should have sent the first beacon for " + pathName, function() { + var b = tf.beacons[i]; + + assert.isTrue(b.u.indexOf(pathName) !== -1); + }); + + it("Should take as long as the longest img load (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + t.validateBeaconWasSentAfter(i, "img.jpg&id=home", 500, 3000, 30000, 0); + } + else { + return this.skip(); + } + }); + + it("Should not have a load time (if MutationObserver is supported but NavigationTiming is not)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() === "undefined") { + var b = tf.beacons[i]; + + assert.equal(b.t_done, undefined); + } + else { + return this.skip(); + } + }); + + it("Should take as long as the XHRs (if MutationObserver is not supported but NavigationTiming is)", function() { + if (!t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + t.validateBeaconWasSentAfter(i, "widgets.json", 500, 0, 30000, false); + } + else { + return this.skip(); + } + }); + + it("Shouldn't have a load time (if MutationObserver and NavigationTiming are not supported)", function() { + if (!t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() === "undefined") { + var b = tf.beacons[i]; + + assert.equal(b.t_done, undefined); + assert.equal(b["rt.start"], "none"); + } + else { + return this.skip(); + } + }); + }); + + // + // Beacon 2 + // + describe("Beacon 2 (spa)", function() { + var i = 1; + + it("Should have sent second beacon as http.initiator = spa", function() { + assert.equal(tf.beacons[i]["http.initiator"], "spa"); + }); + + it("Should have sent the second beacon for /widgets/1", function() { + var b = tf.beacons[i]; + + assert.isTrue(b.u.indexOf("/widgets/1") !== -1); + }); + + it("Should have sent the second beacon with a timestamp of at least 1 second (if MutationObserver is supported)", function() { + if (t.isMutationObserverSupported()) { + // because of the widget IMG delaying 1 second + var b = tf.beacons[i]; + + assert.operator(b.t_done, ">=", 1000); + } + else { + return this.skip(); + } + }); + + it("Should have sent the second beacon with a timestamp of at least 1 millisecond (if MutationObserver is not supported)", function() { + if (!t.isMutationObserverSupported()) { + // because of the widget IMG delaying 1 second but we couldn't track it because no MO support + var b = tf.beacons[i]; + + assert.operator(b.t_done, ">=", 0); + } + else { + return this.skip(); + } + }); + }); + + // + // Beacon 3 + // + describe("Beacon 3 (xhr)", function() { + var i = 2; + + it("Should have sent third beacon as http.initiator = xhr", function() { + assert.equal(tf.beacons[i]["http.initiator"], "xhr"); + }); + + it("Should have sent the second beacon for widgets.json", function() { + var b = tf.beacons[i]; + + assert.isTrue(b.u.indexOf("support/widgets.json") !== -1); + }); + }); +}; diff --git a/tests/test-templates/spa/25-delayed-boomerang-pre-config-snippet.js b/tests/test-templates/spa/25-delayed-boomerang-pre-config-snippet.js new file mode 100644 index 000000000..889b0a4a3 --- /dev/null +++ b/tests/test-templates/spa/25-delayed-boomerang-pre-config-snippet.js @@ -0,0 +1,39 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ +BOOMR_test.templates.SPA = BOOMR_test.templates.SPA || {}; +BOOMR_test.templates.SPA["25-delayed-boomerang-pre-config-snippet"] = function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + it("Should have sent two beacons", function(done) { + this.timeout(10000); + + t.ifAutoXHR( + done, + function() { + t.ensureBeaconCount(done, 2); + }, + this.skip.bind(this)); + }); + + it("Should have had the first beacon be a spa_hard beacon", function() { + var b = tf.beacons[0]; + + assert.equal(b["http.initiator"], "spa_hard"); + assert.include(b.u, "25-delayed-boomerang-pre-config-snippet.html"); + assert.isUndefined(b.pgu); + }); + + it("Should have had the second beacon was an xhr beacon", function() { + var b = tf.beacons[1]; + + assert.equal(b["http.initiator"], "xhr"); + assert.equal(b["rt.tstart"], window.timestamp + 5); + assert.equal(b["rt.start"], "manual"); + assert.equal(b.t_resp, 5); + assert.equal(b.t_page, 1); + assert.equal(b.t_done, 6); + assert.equal(b.u, "http://foo.com/xhr/"); + assert.include(b.pgu, "25-delayed-boomerang-pre-config-snippet.html"); + }); +}; diff --git a/tests/test-templates/spa/29-route-change-early-beacon.js b/tests/test-templates/spa/29-route-change-early-beacon.js new file mode 100644 index 000000000..888fd064a --- /dev/null +++ b/tests/test-templates/spa/29-route-change-early-beacon.js @@ -0,0 +1,813 @@ +/* eslint-env mocha */ +/* global BOOMR_test,assert */ +BOOMR_test.templates.SPA = BOOMR_test.templates.SPA || {}; +BOOMR_test.templates.SPA["29-route-change-early-beacon"] = function() { + var tf = BOOMR.plugins.TestFramework; + var t = BOOMR_test; + + var pathName = window.location.pathname; + + it("Should pass basic beacon validation", function(done) { + t.validateBeaconWasSent(done); + }); + + it("Should have sent six beacons", function() { + assert.equal(tf.beacons.length, 6); + }); + + it("Should have sent the first two beacons as http.initiator = spa_hard", function() { + for (var i = 0; i < 2; i++) { + assert.equal(tf.beacons[0]["http.initiator"], "spa_hard"); + } + }); + + it("Should have sent all subsequent beacons as http.initiator = spa", function() { + for (var i = 2; i < 5; i++) { + assert.equal(tf.beacons[i]["http.initiator"], "spa"); + } + }); + + it("Should have sent rt.nstart = navigationTiming (if NavigationTiming is supported) on all SPA soft beacons", function() { + if (typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + for (var i = 2; i < 5; i++) { + assert.equal(tf.beacons[i]["rt.nstart"], BOOMR.plugins.RT.navigationStart()); + } + } + else { + this.skip(); + } + }); + + it("Should have set the same Page ID (pid) on all beacons", function() { + var pid = BOOMR.pageId; + + for (var i = 0; i <= 5; i++) { + var b = tf.beacons[i]; + + assert.equal(b.pid, pid); + } + }); + + it("Should not have Boomerang timings on SPA Soft beacons", function() { + for (var i = 2; i < 5; i++) { + if (tf.beacons[i].t_other) { + assert.equal(tf.beacons[i].t_other.indexOf("boomr_fb"), -1, "should not have boomr_fb on beacon " + (i + 1)); + assert.equal(tf.beacons[i].t_other.indexOf("boomr_ld"), -1, "should not have boomr_ld on beacon " + (i + 1)); + assert.equal(tf.beacons[i].t_other.indexOf("boomr_lat"), -1, "should not have boomr_lat on beacon " + (i + 1)); + assert.equal(tf.beacons[i].t_other.indexOf("boomerang"), -1, "should not have boomerang on beacon " + (i + 1)); + } + + // Boomerang and config timing parameters + assert.isUndefined(tf.beacons[i]["rt.bmr"], "should not have rt.bmr on beacon " + (i + 1)); + assert.isUndefined(tf.beacons[i]["rt.cnf"], "should not have rt.cnf on beacon " + (i + 1)); + } + }); + + // + // Beacon 1 + // + describe("Beacon 1 (spa_hard early)", function() { + var i = 0; + + it("Should be an early beacon", function() { + var b = tf.beacons[i]; + + assert.isDefined(b.early); + }); + + // the following tests are only executed if mPulse's PageParams plugin exists + if (BOOMR.plugins.PageParams) { + it("Should have custom metric 1 - JavaScript var", function() { + var b = tf.beacons[i]; + + assert.equal(b.cmet1, 11); + }); + + it("Should have custom metric 2 - JavaScript function", function() { + var b = tf.beacons[i]; + + assert.equal(b.cmet2, 22); + }); + + it("Should be missing custom metric 3 - undefined JavaScript var", function() { + var b = tf.beacons[i]; + + assert.equal(b.cmet3, undefined); + }); + + it("Should have the custom metric 4 - XPath", function() { + var b = tf.beacons[i]; + + assert.equal(b.cmet4, 444.44); + }); + + it("Should have the custom metric 5 - URL", function() { + var b = tf.beacons[i]; + + assert.equal(b.cmet5, 1); + }); + + it("Should be missing the custom timer 0 - NavigationTiming - because it's handled on the server", function() { + var b = tf.beacons[i]; + + assert.isUndefined(t.parseTimers(b.t_other).custom0); + }); + + it("Should have the custom timer 1 - JavaScript variable - having been updated by the SPA App", function() { + var b = tf.beacons[i]; + + assert.equal(t.parseTimers(b.t_other).custom1, 11); + }); + + it("Should have the custom timer 2 - JavaScript function - having been updated by the SPA App", function() { + var b = tf.beacons[i]; + + assert.equal(t.parseTimers(b.t_other).custom2, 22); + }); + + it("Should have the custom timer 3 - UserTiming (if UserTiming is supported)", function() { + if (t.isUserTimingSupported()) { + var b = tf.beacons[i]; + + assert.isTrue(t.parseTimers(b.t_other).custom3 > 0); + } + else { + this.skip(); + } + }); + + it("Should be missing the custom timer 3 - UserTiming (if UserTiming is not supported)", function() { + if (!t.isUserTimingSupported()) { + var b = tf.beacons[i]; + + assert.isUndefined(t.parseTimers(b.t_other).custom3); + } + else { + this.skip(); + } + }); + + it("Should be missing custom timer 4 - JavaScript var", function() { + var b = tf.beacons[i]; + + assert.isUndefined(t.parseTimers(b.t_other).custom4); + }); + + it("Should be missing custom timer 5 - UserTiming", function() { + var b = tf.beacons[i]; + + assert.isUndefined(t.parseTimers(b.t_other).custom5); + }); + } + }); + + // + // Beacon 2 + // + describe("Beacon 2 (spa_hard)", function() { + var i = 1; + + it("Should have sent http.initiator = spa_hard", function() { + assert.equal(tf.beacons[i]["http.initiator"], "spa_hard"); + }); + + it("Should have sent the beacon for " + pathName, function() { + var b = tf.beacons[i]; + + assert.isTrue(b.u.indexOf(pathName) !== -1); + }); + + it("Should take as long as the longest img load (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + t.validateBeaconWasSentAfter(i, "img.jpg&id=home", 500, 3000, 30000, 0); + } + else { + this.skip(); + } + }); + + it("Should not have a load time (if MutationObserver is supported but NavigationTiming is not)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() === "undefined") { + var b = tf.beacons[i]; + + assert.equal(b.t_done, undefined); + } + else { + this.skip(); + } + }); + + it("Should take as long as the XHRs (if MutationObserver is not supported but NavigationTiming is)", function() { + if (!t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + t.validateBeaconWasSentAfter(i, "widgets.json", 500, 0, 30000, false); + } + else { + this.skip(); + } + }); + + it("Shouldn't have a load time (if MutationObserver and NavigationTiming are not supported)", function() { + if (!t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() === "undefined") { + var b = tf.beacons[i]; + + assert.equal(b.t_done, undefined); + assert.equal(b["rt.start"], "none"); + } + else { + this.skip(); + } + }); + + it("Should have a t_resp of the root page (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + var pt = window.performance.timing; + var b = tf.beacons[i]; + + assert.equal(b.t_resp, pt.responseStart - pt.navigationStart); + } + else { + this.skip(); + } + }); + + it("Should have a t_page of total - t_resp (if MutationObserver and NavigationTiming are supported)", function() { + if (t.isMutationObserverSupported() && typeof BOOMR.plugins.RT.navigationStart() !== "undefined") { + var pt = window.performance.timing; + var b = tf.beacons[i]; + + assert.equal(b.t_page, b.t_done - b.t_resp); + } + else { + this.skip(); + } + }); + + // the following tests are only executed if mPulse's PageParams plugin exists + if (BOOMR.plugins.PageParams) { + it("Should have custom metric 1 - JavaScript var - having been updated by the SPA App", function() { + var b = tf.beacons[i]; + + assert.equal(b.cmet1, 11); + }); + + it("Should have custom metric 2 - JavaScript function - having been updated by the SPA App", function() { + var b = tf.beacons[i]; + + assert.equal(b.cmet2, 22); + }); + + it("Should be missing custom metric 3 - undefined JavaScript var", function() { + var b = tf.beacons[i]; + + assert.equal(b.cmet3, undefined); + }); + + it("Should have the custom metric 4 - XPath", function() { + var b = tf.beacons[i]; + + assert.equal(b.cmet4, 444.44); + }); + + it("Should have the custom metric 5 - URL", function() { + var b = tf.beacons[i]; + + assert.equal(b.cmet5, 1); + }); + + it("Should be missing the custom timer 0 - NavigationTiming - because it's handled on the server", function() { + var b = tf.beacons[i]; + + assert.isUndefined(t.parseTimers(b.t_other).custom0); + }); + + it("Should have the custom timer 1 - JavaScript variable - having been updated by the SPA App", function() { + var b = tf.beacons[i]; + + assert.equal(t.parseTimers(b.t_other).custom1, 11); + }); + + it("Should have the custom timer 2 - JavaScript function - having been updated by the SPA App", function() { + var b = tf.beacons[i]; + + assert.equal(t.parseTimers(b.t_other).custom2, 22); + }); + + it("Should have the custom timer 3 - UserTiming (if UserTiming is supported)", function() { + if (t.isUserTimingSupported()) { + var b = tf.beacons[i]; + + assert.isTrue(t.parseTimers(b.t_other).custom3 > 0); + } + else { + this.skip(); + } + }); + + it("Should be missing the custom timer 3 - UserTiming (if UserTiming is not supported)", function() { + if (!t.isUserTimingSupported()) { + var b = tf.beacons[i]; + + assert.isUndefined(t.parseTimers(b.t_other).custom3); + } + else { + this.skip(); + } + }); + + it("Should be missing custom timer 4 - JavaScript var", function() { + var b = tf.beacons[i]; + + assert.isUndefined(t.parseTimers(b.t_other).custom4); + }); + + it("Should be missing custom timer 5 - UserTiming", function() { + var b = tf.beacons[i]; + + assert.isUndefined(t.parseTimers(b.t_other).custom5); + }); + } + }); + + // + // Beacon 3 + // + describe("Beacon 3 (spa early)", function() { + var i = 2; + + it("Should be an early beacon", function() { + var b = tf.beacons[i]; + + assert.isDefined(b.early); + }); + + // the following tests are only executed if mPulse's PageParams plugin exists + if (BOOMR.plugins.PageParams) { + it("Should have custom metric 1 - JavaScript var - having been updated by the SPA App", function() { + var b = tf.beacons[i]; + + assert.equal(b.cmet1, 1); + }); + + it("Should have custom metric 2 - JavaScript function - having been updated by the SPA App", function() { + var b = tf.beacons[i]; + + assert.equal(b.cmet2, 10); + }); + + it("Should be missing custom metric 3 - undefined JavaScript var", function() { + var b = tf.beacons[i]; + + assert.equal(b.cmet3, undefined); + }); + + it("Should have the custom metric 4 - XPath", function() { + var b = tf.beacons[i]; + + assert.equal(b.cmet4, 11.11); + }); + + it("Should have the custom metric 5 - URL", function() { + var b = tf.beacons[i]; + + assert.equal(b.cmet5, 1); + }); + + it("Should be missing the custom timer 0 - NavigationTiming - because it's handled on the server", function() { + var b = tf.beacons[i]; + + assert.isUndefined(t.parseTimers(b.t_other).custom0); + }); + + it("Should have the custom timer 1 - JavaScript variable - having been updated by the SPA App", function() { + var b = tf.beacons[i]; + + assert.equal(t.parseTimers(b.t_other).custom1, 1); + }); + + it("Should have the custom timer 2 - JavaScript function - having been updated by the SPA App", function() { + var b = tf.beacons[i]; + + assert.equal(t.parseTimers(b.t_other).custom2, 10); + }); + + it("Should have the custom timer 3 - UserTiming (if UserTiming is supported)", function() { + if (t.isUserTimingSupported()) { + var b = tf.beacons[i]; + + assert.isTrue(t.parseTimers(b.t_other).custom3 > 0); + } + else { + this.skip(); + } + }); + + it("Should be missing the custom timer 3 - UserTiming (if UserTiming is not supported)", function() { + if (!t.isUserTimingSupported()) { + var b = tf.beacons[i]; + + assert.isUndefined(t.parseTimers(b.t_other).custom3); + } + else { + this.skip(); + } + }); + + it("Should be missing custom timer 4 - JavaScript var", function() { + var b = tf.beacons[i]; + + assert.isUndefined(t.parseTimers(b.t_other).custom4); + }); + + it("Should be missing custom timer 5 - UserTiming", function() { + var b = tf.beacons[i]; + + assert.isUndefined(t.parseTimers(b.t_other).custom5); + }); + } + }); + + // + // Beacon 4 + // + describe("Beacon 4 (spa)", function() { + var i = 3; + + it("Should have sent the beacon for /widgets/1", function() { + var b = tf.beacons[i]; + + assert.isTrue(b.u.indexOf("/widgets/1") !== -1); + }); + + it("Should have sent the beacon with a timestamp of at least 1 second (if MutationObserver is supported)", function() { + if (t.isMutationObserverSupported()) { + // because of the widget IMG delaying 1 second + var b = tf.beacons[i]; + + assert.operator(b.t_done, ">=", 1000); + } + else { + this.skip(); + } + }); + + it("Should have sent the beacon with a timestamp of at least 1 millisecond (if MutationObserver is not supported)", function() { + if (!t.isMutationObserverSupported()) { + // because of the widget IMG delaying 1 second but we couldn't track it because no MO support + var b = tf.beacons[i]; + + assert.operator(b.t_done, ">=", 0); + } + else { + this.skip(); + } + }); + + it("Should have sent the beacon with a t_resp value (if MutationObserver and ResourceTiming are supported)", function() { + if (t.isMutationObserverSupported() && t.isResourceTimingSupported()) { + var b = tf.beacons[i]; + + assert.operator(b.t_resp, ">=", 0); + } + else { + this.skip(); + } + }); + + it("Should have sent the beacon with a t_page of total - t_resp (if MutationObserver and ResourceTiming are supported)", function() { + if (t.isMutationObserverSupported() && t.isResourceTimingSupported()) { + var b = tf.beacons[i]; + + assert.equal(b.t_page, b.t_done - b.t_resp); + } + else { + this.skip(); + } + }); + + // the following tests are only executed if mPulse's PageParams plugin exists + if (BOOMR.plugins.PageParams) { + it("Should have custom metric 1 - JavaScript var - having been updated by the SPA App", function() { + var b = tf.beacons[i]; + + assert.equal(b.cmet1, 1); + }); + + it("Should have custom metric 2 - JavaScript function - having been updated by the SPA App", function() { + var b = tf.beacons[i]; + + assert.equal(b.cmet2, 10); + }); + + it("Should be missing custom metric 3 - undefined JavaScript var", function() { + var b = tf.beacons[i]; + + assert.equal(b.cmet3, undefined); + }); + + it("Should have the custom metric 4 - XPath", function() { + var b = tf.beacons[i]; + + assert.equal(b.cmet4, 11.11); + }); + + it("Should have the custom metric 5 - URL", function() { + var b = tf.beacons[i]; + + assert.equal(b.cmet5, 1); + }); + + it("Should be missing the custom timer 0 - NavigationTiming - because it's handled on the server", function() { + var b = tf.beacons[i]; + + assert.isUndefined(t.parseTimers(b.t_other).custom0); + }); + + it("Should have the custom timer 1 - JavaScript variable - having been updated by the SPA App", function() { + var b = tf.beacons[i]; + + assert.equal(t.parseTimers(b.t_other).custom1, 1); + }); + + it("Should have the custom timer 2 - JavaScript function - having been updated by the SPA App", function() { + var b = tf.beacons[i]; + + assert.equal(t.parseTimers(b.t_other).custom2, 10); + }); + + it("Should have the custom timer 3 - UserTiming (if UserTiming is supported)", function() { + if (t.isUserTimingSupported()) { + var b = tf.beacons[i]; + + assert.isTrue(t.parseTimers(b.t_other).custom3 > 0); + } + else { + this.skip(); + } + }); + + it("Should be missing the custom timer 3 - UserTiming (if UserTiming is not supported)", function() { + if (!t.isUserTimingSupported()) { + var b = tf.beacons[i]; + + assert.isUndefined(t.parseTimers(b.t_other).custom3); + } + else { + this.skip(); + } + }); + + it("Should be missing custom timer 4 - JavaScript var", function() { + var b = tf.beacons[i]; + + assert.isUndefined(t.parseTimers(b.t_other).custom4); + }); + + it("Should be missing custom timer 5 - UserTiming", function() { + var b = tf.beacons[i]; + + assert.isUndefined(t.parseTimers(b.t_other).custom5); + }); + } + }); + + // + // Beacon 5 + // + describe("Beacon 5 (spa early)", function() { + var i = 4; + + it("Should be an early beacon", function() { + var b = tf.beacons[i]; + + assert.isDefined(b.early); + }); + + // the following tests are only executed if mPulse's PageParams plugin exists + if (BOOMR.plugins.PageParams) { + it("Should have custom metric 1 - JavaScript var - having been updated by the SPA App", function() { + var b = tf.beacons[i]; + + assert.equal(b.cmet1, 11); + }); + + it("Should have custom metric 2 - JavaScript function - having been updated by the SPA App", function() { + var b = tf.beacons[i]; + + assert.equal(b.cmet2, 22); + }); + + it("Should be missing custom metric 3 - undefined JavaScript var", function() { + var b = tf.beacons[i]; + + assert.equal(b.cmet3, undefined); + }); + + it("Should have the custom metric 4 - XPath", function() { + var b = tf.beacons[i]; + + assert.equal(b.cmet4, 444.44); + }); + + it("Should have the custom metric 5 - URL", function() { + var b = tf.beacons[i]; + + assert.equal(b.cmet5, 1); + }); + + it("Should be missing the custom timer 0 - NavigationTiming - because it's handled on the server", function() { + var b = tf.beacons[i]; + + assert.isUndefined(t.parseTimers(b.t_other).custom0); + }); + + it("Should have the custom timer 1 - JavaScript variable - having been updated by the SPA App", function() { + var b = tf.beacons[i]; + + assert.equal(t.parseTimers(b.t_other).custom1, 11); + }); + + it("Should have the custom timer 2 - JavaScript function - having been updated by the SPA App", function() { + var b = tf.beacons[i]; + + assert.equal(t.parseTimers(b.t_other).custom2, 22); + }); + + it("Should have the custom timer 3 - UserTiming (if UserTiming is supported)", function() { + if (t.isUserTimingSupported()) { + var b = tf.beacons[i]; + + assert.isTrue(t.parseTimers(b.t_other).custom3 > 0); + } + else { + this.skip(); + } + }); + + it("Should be missing the custom timer 3 - UserTiming (if UserTiming is not supported)", function() { + if (!t.isUserTimingSupported()) { + var b = tf.beacons[i]; + + assert.isUndefined(t.parseTimers(b.t_other).custom3); + } + else { + this.skip(); + } + }); + + it("Should be missing custom timer 4 - JavaScript var", function() { + var b = tf.beacons[i]; + + assert.isUndefined(t.parseTimers(b.t_other).custom4); + }); + + it("Should be missing custom timer 5 - UserTiming", function() { + var b = tf.beacons[i]; + + assert.isUndefined(t.parseTimers(b.t_other).custom5); + }); + } + }); + + // + // Beacon 6 + // + describe("Beacon 6 (spa)", function() { + var i = 5; + + it("Should have sent the beacon for " + pathName, function() { + var b = tf.beacons[i]; + + assert.isTrue(b.u.indexOf(pathName) !== -1); + }); + + it("Should have sent the with a timestamp of at least 3 seconds (if MutationObserver is supported)", function() { + if (t.isMutationObserverSupported()) { + var b = tf.beacons[i]; + + assert.operator(b.t_done, ">=", 3000); + } + else { + this.skip(); + } + }); + + it("Should have sent the with a timestamp of under 1 second (if MutationObserver is not supported)", function() { + if (!t.isMutationObserverSupported()) { + var b = tf.beacons[i]; + + assert.operator(b.t_done, "<=", 1000); + } + else { + this.skip(); + } + }); + + it("Should have sent the beacon with a t_resp value (if MutationObserver and ResourceTiming are supported)", function() { + if (t.isMutationObserverSupported() && t.isResourceTimingSupported()) { + var b = tf.beacons[i]; + + assert.operator(b.t_resp, ">=", 0); + } + else { + this.skip(); + } + }); + + it("Should have sent the beacon with a t_page of total - t_resp (if MutationObserver and ResourceTiming are supported)", function() { + if (t.isMutationObserverSupported() && t.isResourceTimingSupported()) { + var pt = window.performance.timing; + var b = tf.beacons[i]; + + assert.equal(b.t_page, b.t_done - b.t_resp); + } + else { + this.skip(); + } + }); + + // the following tests are only executed if mPulse's PageParams plugin exists + if (BOOMR.plugins.PageParams) { + it("Should have custom metric 1 - JavaScript var - having been updated by the SPA App", function() { + var b = tf.beacons[i]; + + assert.equal(b.cmet1, 11); + }); + + it("Should have custom metric 2 - JavaScript function - having been updated by the SPA App", function() { + var b = tf.beacons[i]; + + assert.equal(b.cmet2, 22); + }); + + it("Should be missing custom metric 3 - undefined JavaScript var", function() { + var b = tf.beacons[i]; + + assert.equal(b.cmet3, undefined); + }); + + it("Should have the custom metric 4 - XPath", function() { + var b = tf.beacons[i]; + + assert.equal(b.cmet4, 444.44); + }); + + it("Should have the custom metric 5 - URL", function() { + var b = tf.beacons[i]; + + assert.equal(b.cmet5, 1); + }); + + it("Should be missing the custom timer 0 - NavigationTiming - because it's handled on the server", function() { + var b = tf.beacons[i]; + + assert.isUndefined(t.parseTimers(b.t_other).custom0); + }); + + it("Should have the custom timer 1 - JavaScript variable - having been updated by the SPA App", function() { + var b = tf.beacons[i]; + + assert.equal(t.parseTimers(b.t_other).custom1, 11); + }); + + it("Should have the custom timer 2 - JavaScript function - having been updated by the SPA App", function() { + var b = tf.beacons[i]; + + assert.equal(t.parseTimers(b.t_other).custom2, 22); + }); + + it("Should have the custom timer 3 - UserTiming (if UserTiming is supported)", function() { + if (t.isUserTimingSupported()) { + var b = tf.beacons[i]; + + assert.isTrue(t.parseTimers(b.t_other).custom3 > 0); + } + else { + this.skip(); + } + }); + + it("Should be missing the custom timer 3 - UserTiming (if UserTiming is not supported)", function() { + if (!t.isUserTimingSupported()) { + var b = tf.beacons[i]; + + assert.isUndefined(t.parseTimers(b.t_other).custom3); + } + else { + this.skip(); + } + }); + + it("Should be missing custom timer 4 - JavaScript var", function() { + var b = tf.beacons[i]; + + assert.isUndefined(t.parseTimers(b.t_other).custom4); + }); + + it("Should be missing custom timer 5 - UserTiming", function() { + var b = tf.beacons[i]; + + assert.isUndefined(t.parseTimers(b.t_other).custom5); + }); + } + }); +}; diff --git a/tests/unit/00-logging.js b/tests/unit/00-logging.js new file mode 100644 index 000000000..48985dd26 --- /dev/null +++ b/tests/unit/00-logging.js @@ -0,0 +1,26 @@ +/* eslint-env mocha */ +/* global chai */ + +describe("BOOMR logging", function() { + var assert = chai.assert; + + it("Should have an existing BOOMR.info() function (Will be set to a stub for less noise during tests)", function() { + assert.isFunction(BOOMR.info); + BOOMR.info = function() {}; + }); + + it("Should have an existing BOOMR.debug() function (Will be set to a stub for less noise during tests)", function() { + assert.isFunction(BOOMR.debug); + BOOMR.debug = function() {}; + }); + + it("Should have an existing BOOMR.warn() function (Will be set to a stub for less noise during tests)", function() { + assert.isFunction(BOOMR.warn); + BOOMR.warn = function() {}; + }); + + it("Should have an existing BOOMR.error() function (Will be set to a stub for less noise during tests)", function() { + assert.isFunction(BOOMR.error); + BOOMR.error = function() {}; + }); +}); diff --git a/tests/unit/01-basic.js b/tests/unit/01-basic.js new file mode 100644 index 000000000..e2ed7af38 --- /dev/null +++ b/tests/unit/01-basic.js @@ -0,0 +1,34 @@ +/* eslint-env mocha */ +/* global chai */ + +describe("BOOMR exports", function() { + var assert = chai.assert; + + it("Should have an existing BOOMR object", function() { + assert.isObject(BOOMR); + }); + + it("Should have an existing BOOMR.utils object", function() { + assert.isObject(BOOMR.utils); + }); + + it("Should have an existing BOOMR.version String", function() { + assert.isString(BOOMR.version); + }); + + it("Should have an existing BOOMR.session Object", function() { + assert.isObject(BOOMR.session); + }); + + it("Should have an existing BOOMR.init() Function", function() { + assert.isFunction(BOOMR.init); + }); + + it("Should have an existing BOOMR.plugins Object", function(){ + assert.isObject(BOOMR.plugins); + }); + + it("Should have an existing BOOMR.url value", function(){ + assert.isString(BOOMR.url); + }); +}); diff --git a/tests/unit/02-utils-arrayfilter.js b/tests/unit/02-utils-arrayfilter.js new file mode 100644 index 000000000..b4327b4f9 --- /dev/null +++ b/tests/unit/02-utils-arrayfilter.js @@ -0,0 +1,82 @@ +/* eslint-env mocha */ +/* global chai */ + +describe("BOOMR.utils.arrayFilter()", function() { + var assert = chai.assert; + + it("Should return an empty array if input is not an array", function() { + var expect = []; + + var filterFunction = function() { + return false; + }; + + assert.deepEqual(BOOMR.utils.arrayFilter(null, filterFunction), expect); + }); + + it("Should return an empty array if predicate is not a function", function() { + var input = [1, 2, 3, 4], + expect = []; + + assert.deepEqual(BOOMR.utils.arrayFilter(input, null), expect); + }); + + it("Should return an empty array if the predicate only returns false", function() { + var input = [1, 2, 3, 4], + expect = []; + + var filterFunction = function() { + return false; + }; + + assert.deepEqual(BOOMR.utils.arrayFilter(input, filterFunction), expect); + }); + + it("Should return an array of length one if only one returns true", function() { + var input = [true, false], + expect = [true]; + + var filterFunction = function(value) { + return value; + }; + + assert.deepEqual(BOOMR.utils.arrayFilter(input, filterFunction), expect); + }); + + it("Should have the array passed in as the third value and return the complete array as passed in", function() { + var input = [1, 2, 3], + expect = input; + + var filterFunction = function(value, index, array) { + assert.deepEqual(array, expect); + + return true; + }; + + assert.deepEqual(BOOMR.utils.arrayFilter(input, filterFunction), expect); + }); + + it("Should return half the array of numbers as it matches the rule we defined in the filter function", function() { + var input = [1, 2, 3, 4], + expect = [1, 2]; + + var filterFunction = function(value) { + return value <= 2; + }; + + assert.deepEqual(BOOMR.utils.arrayFilter(input, filterFunction), expect); + }); + + it("Should also work if filter has been set to null or undefined (ie. lacking [].filter support)", function() { + var input = [1, 2, 3, 4], + expect = [1, 2]; + + input.filter = undefined; + + var filterFunction = function(value) { + return value <= 2; + }; + + assert.deepEqual(BOOMR.utils.arrayFilter(input, filterFunction), expect); + }); +}); diff --git a/tests/unit/02-utils-arrayfind.js b/tests/unit/02-utils-arrayfind.js new file mode 100644 index 000000000..3974597f4 --- /dev/null +++ b/tests/unit/02-utils-arrayfind.js @@ -0,0 +1,75 @@ +/* eslint-env mocha */ +/* global chai */ + +describe("BOOMR.utils.arrayFind()", function() { + var assert = chai.assert; + + it("Should return undefined if input is not an array", function() { + var findFunction = function() { + return false; + }; + + assert.isUndefined(BOOMR.utils.arrayFind(null, findFunction)); + }); + + it("Should return undefined if predicate is not a function", function() { + var input = [1, 2, 3, 4]; + + assert.isUndefined(BOOMR.utils.arrayFind(input, null)); + }); + + it("Should return undefined if the predicate only returns false", function() { + var input = [1, 2, 3, 4]; + + var findFunction = function() { + return false; + }; + + assert.isUndefined(BOOMR.utils.arrayFind(input, findFunction)); + }); + + it("Should return value of match if only one matches", function() { + var input = [1, 2, 3, 4], + expect = 3; + + var findFunction = function(value) { + return value === 3; + }; + + assert.equal(BOOMR.utils.arrayFind(input, findFunction), expect); + }); + + it("Should return value of first match if multiple matches", function() { + var input = [1, 2, 3, 4], + expect = 2; + + var findFunction = function(value, index, array) { + return value >= 2; + }; + + assert.equal(BOOMR.utils.arrayFind(input, findFunction), expect); + }); + + it("Should return undefined if no matches", function() { + var input = [1, 2, 3, 4]; + + var findFunction = function(value) { + return value === 0; + }; + + assert.isUndefined(BOOMR.utils.arrayFind(input, findFunction)); + }); + + it("Should also work if find has been set to null or undefined (ie. lacking [].find support)", function() { + var input = [1, 2, 3, 4], + expect = 2; + + input.filter = undefined; + + var findFunction = function(value) { + return value >= 2; + }; + + assert.equal(BOOMR.utils.arrayFind(input, findFunction), expect); + }); +}); diff --git a/tests/unit/02-utils-cleanupurl.js b/tests/unit/02-utils-cleanupurl.js new file mode 100644 index 000000000..db5e3c650 --- /dev/null +++ b/tests/unit/02-utils-cleanupurl.js @@ -0,0 +1,91 @@ +/* eslint-env mocha */ +/* global chai */ + +describe("BOOMR.utils.cleanupURL()", function() { + var assert = chai.assert; + + it("Should return an empty string when no argument is given", function() { + assert.equal(BOOMR.utils.cleanupURL(), ""); + }); + + it("Should return an empty string when null is passed as argument", function() { + assert.equal(BOOMR.utils.cleanupURL(null), ""); + }); + + it("Should return an empty string when undefined is passed as argument", function() { + assert.equal(BOOMR.utils.cleanupURL(undefined), ""); + }); + + it("Should return an empty string when an empty string is passed as argument", function() { + assert.equal(BOOMR.utils.cleanupURL(undefined), ""); + }); + + it("Should return an empty string when false is passed as argument", function() { + assert.equal(BOOMR.utils.cleanupURL(false), ""); + }); + + it("Should return an empty string when an array is passed as argument", function() { + assert.equal(BOOMR.utils.cleanupURL(["a", "b"]), ""); + }); + + it("Should return the given URL without a parameters string when a URL with prameters is passed as argument", function() { + var BOOMR_reset = BOOMR; + + BOOMR.init({ + strip_query_string: true + }); + + var url = "http://www.example.com/?key=value", + expected = "http://www.example.com/?qs-redacted"; + + assert.equal(BOOMR.utils.cleanupURL(url), expected); + + BOOMR = BOOMR_reset; + }); + + it("Should return the given path without parameters when a URL-path with prameters is passed as argument", function() { + var BOOMR_reset = BOOMR; + + BOOMR.init({ + strip_query_string: true + }); + + var url = "/app/page?key=value", + expected = "/app/page?qs-redacted"; + + assert.equal(BOOMR.utils.cleanupURL(url), expected); + + BOOMR = BOOMR_reset; + }); + + it("Should return original URL when strip_query_string is false", function() { + var BOOMR_reset = BOOMR; + + BOOMR.init({ + strip_query_string: false + }); + + var url = "/app/page?key=value", + expected = "/app/page?key=value"; + + assert.equal(BOOMR.utils.cleanupURL(url), expected); + + BOOMR = BOOMR_reset; + }); + + it("Should not trim a URL underneath the limit", function() { + assert.equal(BOOMR.utils.cleanupURL("http://foo.com", 1000), "http://foo.com"); + }); + + it("Should trim a URL with a query string over the limit at the query string", function() { + assert.equal(BOOMR.utils.cleanupURL("http://foo.com?aaaaaa", 20), "http://foo.com?..."); + }); + + it("Should trim a URL with a query string too long over the limit at the limit", function() { + assert.equal(BOOMR.utils.cleanupURL("http://foo.com?aaaaaa", 14), "http://foo...."); + }); + + it("Should trim a URL without a query string over the limit at the limit", function() { + assert.equal(BOOMR.utils.cleanupURL("http://foo.com/a/b/c/d", 17), "http://foo.com..."); + }); +}); diff --git a/tests/unit/02-utils-cookies.js b/tests/unit/02-utils-cookies.js new file mode 100644 index 000000000..b1c805a93 --- /dev/null +++ b/tests/unit/02-utils-cookies.js @@ -0,0 +1,281 @@ +/* eslint-env mocha */ +/* global chai */ + +describe("BOOMR.utils cookies", function() { + var assert = chai.assert; + + var cookieName = "myCookie"; + + /* + NOTE: + + These tests can only run in a client-server setup with a properly + configured FQDN for the server. + + Please read: + RFC 2109 (https://www.ietf.org/rfc/rfc2109.txt) + and this thread on the chromium bugtracker: + https://code.google.com/p/chromium/issues/detail?id=535 + + In your development environment please configure your localhost with a fully + qualified domain name locally: + + In a UNIX/Mac/Linux environment you can add a name for 127.0.0.1 to + your /etc/hosts such as: + 127.0.0.1 www.example.org www + + You can do the same under windows, however the path to the file is a + little different: + + Valid for Windown Vista/7/2008/2012: C:\Windows\System32\drivers\etc\hosts + + We (as in the boomerang team) are not responsible for any accidental or + direct damages and or damage claims. See LICENSE for further information. + */ + + if (window.location.protocol === "file:") { + return; + } + + // need to skip some tests this isn't on a TLD (eg localhost), because cookies can't get set + var canSetCookies = window.location.host.indexOf(".") !== -1; + + // cookie domain can't have a port + var cookieDomain = window.location.host.indexOf(":") === -1 ? + window.location.host : + window.location.host.substring(0, window.location.host.indexOf(":")); + + describe("BOOMR.utils.getCookie()", function() { + it("Should have an exisiting BOOMR.utils.getCookie function", function() { + assert.isFunction(BOOMR.utils.getCookie); + }); + + it("Should return null when calling getCookie() with empty arguments", function() { + assert.isNull(BOOMR.utils.getCookie()); + }); + + it("Should return null when calling getCookie with empty String", function() { + assert.isNull(BOOMR.utils.getCookie("")); + }); + + it("Should return null when calling with null as first argument", function() { + assert.isNull(BOOMR.utils.getCookie(null)); + }); + + it("Should return false when calling with non existing cookie", function() { + assert.isUndefined(BOOMR.utils.getCookie("some-non-existing-cooke")); + }); + }); + + describe("BOOMR.utils.setCookie()", function() { + it("Should have an exisiting BOOMR.utils.setCookie function", function() { + BOOMR.session.domain = cookieDomain; + + assert.isFunction(BOOMR.utils.setCookie); + }); + + it("Should return false if no domain is set", function() { + BOOMR.session.domain = null; + + assert.isFalse(BOOMR.utils.setCookie()); + assert.isFalse(BOOMR.utils.setCookie(cookieName)); + }); + + it("Should return false if no name was passed as first argument to setCookie()", function() { + BOOMR.session.domain = cookieDomain; + + assert.isFalse(BOOMR.utils.setCookie()); + }); + + if (canSetCookies) { + it("Should return false when setting only Cookie name", function() { + BOOMR.session.domain = cookieDomain; + + assert.isFalse(BOOMR.utils.setCookie(cookieName)); + }); + + it("Should return true when setting Cookie with value", function() { + BOOMR.session.domain = cookieDomain; + + assert.isTrue(BOOMR.utils.setCookie(cookieName, "value")); + }); + + it("Should return the cookie value string that we've set previously", function() { + BOOMR.session.domain = cookieDomain; + + var value = "value"; + + BOOMR.utils.setCookie(cookieName, value); + + assert.equal(BOOMR.utils.getCookie(cookieName), value); + BOOMR.utils.removeCookie(cookieName); + }); + + it("Should return the EXACT value string that we've set previously", function() { + BOOMR.session.domain = cookieDomain; + + var value = "1"; + var value_strict_false = 1; + + BOOMR.utils.setCookie(cookieName, value); + assert.strictEqual(BOOMR.utils.getCookie(cookieName), value); + assert.notStrictEqual(BOOMR.utils.getCookie(cookieName), value_strict_false); + BOOMR.utils.removeCookie(cookieName); + }); + + it("Should return the cookie value string that we've set previously with a large expiry", function() { + BOOMR.session.domain = cookieDomain; + + var value = "1"; + + BOOMR.utils.setCookie(cookieName, value, 5 * 60); + assert.strictEqual(BOOMR.utils.getCookie(cookieName), value); + BOOMR.utils.removeCookie(cookieName); + }); + + it("Should return undefined for a cookie that we've set previously with a zero expiry", function() { + BOOMR.session.domain = cookieDomain; + + var value = "1"; + + BOOMR.utils.setCookie(cookieName, value, 0); + assert.isUndefined(BOOMR.utils.getCookie(cookieName)); + }); + + it("Should return undefined for a cookie that we've set previously with a negative expiry", function() { + BOOMR.session.domain = cookieDomain; + + var value = "1"; + + BOOMR.utils.setCookie(cookieName, value, -1); + assert.isUndefined(BOOMR.utils.getCookie(cookieName)); + }); + } + + it("Should return false when trying to set a cookie bigger than 500 characters", function() { + BOOMR.session.domain = cookieDomain; + + var value = ""; + + for (var index = 0; index <= 500; index++) { + value += "1"; + } + + assert.isFalse(BOOMR.utils.setCookie("failCookie", value)); + }); + }); + + describe("BOOMR.utils.getSubCookies()", function() { + it("Should have an exisiting BOOMR.utils.getSubCookies function", function() { + assert.isFunction(BOOMR.utils.getSubCookies); + }); + + it("Should return null when calling getSubCookies() with empty arguments", function() { + assert.isNull(BOOMR.utils.getSubCookies()); + }); + + it("Should return null when calling getSubCookies with empty String", function() { + assert.isNull(BOOMR.utils.getSubCookies("")); + }); + + it("Should return null when calling with null as first argument", function() { + assert.isNull(BOOMR.utils.getSubCookies(null)); + }); + + it("Should return null when calling with undefined as first argument", function() { + assert.isNull(BOOMR.utils.getSubCookies(undefined)); + }); + + it("Should return null when calling with a non-string object", function() { + assert.isNull(BOOMR.utils.getSubCookies({key: "value"})); + }); + + if (canSetCookies) { + it("Should return the value that we've set previously", function() { + var value = { subValue: "value" }; + + BOOMR.utils.setCookie(cookieName, value); + assert.deepEqual(BOOMR.utils.getSubCookies(BOOMR.utils.getCookie(cookieName)), { subValue: "value" }); + }); + } + + it("Should return null when requesting the subCookie '&'", function() { + assert.isNull(BOOMR.utils.getSubCookies("&")); + }); + + it("Should return null when requesting a subCookie named '='", function() { + assert.isNull(BOOMR.utils.getSubCookies("=")); + }); + + it("Should return null when requesting the subCookie '=&='", function() { + assert.isNull(BOOMR.utils.getSubCookies("=&=")); + }); + + it("Should return null when requesting a value instead of a key for a subCookie i.e. '=someValue'", function() { + assert.isNull(BOOMR.utils.getSubCookies("=someValue")); + }); + + it("Should validate the old YUI Boomerang test", function() { + var cookie = "one=1&two=2&three=3rd&four=null&five=undefined&six=0&seven=1.2&eight=" + encodeURIComponent("a=b") + "&nine=" + encodeURIComponent("1,2") + "&%3d=&10=11&11"; + + var o = BOOMR.utils.getSubCookies(cookie); + + assert.isNotNull(o); + assert.isObject(o); + + assert.strictEqual(o.one, "1"); + assert.strictEqual(o.two, "2"); + assert.strictEqual(o.three, "3rd"); + assert.strictEqual(o.four, "null"); + assert.strictEqual(o.five, "undefined"); + assert.strictEqual(o.six, "0"); + assert.strictEqual(o.seven, "1.2"); + assert.strictEqual(o.eight, "a=b"); + assert.strictEqual(o.nine, "1,2"); + assert.strictEqual(o["="], ""); + assert.strictEqual(o["10"], "11"); + assert.strictEqual(o["11"], ""); + }); + + it("Should validate that document.cookie contains quotes for a complex cookie", function() { + var c = { a: 10, b: 20, c: "foo bar" }; + + BOOMR.utils.setCookie("complex-cookie", c); + + assert.match(document.cookie, /(^|; *)complex-cookie="a=10&b=20&c=foo%20bar"($| *;)/, "complex-cookie should be quoted"); + + var d = BOOMR.utils.getCookie("complex-cookie"); + + assert.equal(d, "a=10&b=20&c=foo%20bar", "getCookie should remove quotes from complex cookie"); + + var e = BOOMR.utils.getSubCookies(d); + + assert.equal(e.a, c.a, "subcookies should match"); + assert.equal(e.b, c.b, "subcookies should match"); + assert.equal(e.c, c.c, "subcookies should match"); + }); + }); + + describe("BOOMR.utils.removeCookie()", function() { + it("Should return false when given no argurments", function() { + BOOMR.session.domain = cookieDomain; + + assert.isFalse(BOOMR.utils.removeCookie()); + }); + + it("Should return false when the session domain isn't set", function() { + BOOMR.session.domain = undefined; + + assert.isFalse(BOOMR.utils.removeCookie(cookieName)); + }); + + if (canSetCookies) { + it("Should return true when removing a Cookie", function() { + BOOMR.session.domain = cookieDomain; + + assert.isTrue(BOOMR.utils.setCookie(cookieName, "value")); + assert.isTrue(BOOMR.utils.removeCookie(cookieName)); + }); + } + }); +}); diff --git a/tests/unit/02-utils-foreach.js b/tests/unit/02-utils-foreach.js new file mode 100644 index 000000000..54846321b --- /dev/null +++ b/tests/unit/02-utils-foreach.js @@ -0,0 +1,56 @@ +/* eslint-env mocha */ +/* global chai */ + +describe("BOOMR.utils.forEach()", function() { + var assert = chai.assert; + + it("Should handle degenerate cases", function() { + assert.doesNotThrow(function() { + BOOMR.utils.forEach(); + BOOMR.utils.forEach(null); + BOOMR.utils.forEach(true); + BOOMR.utils.forEach(false); + BOOMR.utils.forEach(0); + BOOMR.utils.forEach(1); + BOOMR.utils.forEach(123); + BOOMR.utils.forEach(""); + BOOMR.utils.forEach({}); + }); + }); + + it("Should not call the callback when the array is empty", function(done) { + BOOMR.utils.forEach([], function() { + done(new Error("how dare you")); + }); + done(); + }); + + it("Should have no return value", function() { + var returnValue = BOOMR.utils.forEach([1, 2, 3], function() {}); + + assert.isUndefined(returnValue); + }); + + it("Should iterate over an array", function() { + var expected = [1, 2, 3]; + var actual = []; + + BOOMR.utils.forEach(expected, function(i) { + actual.push(i); + }); + assert.sameMembers(expected, actual); + }); + + it("Should use the correct context on the callback", function(done) { + function Obj(value) { + this.value = value; + } + + Obj.prototype.check = function() { + assert.strictEqual(this.value, 123); + done(); + }; + + BOOMR.utils.forEach([0], Obj.prototype.check, new Obj(123)); + }); +}); diff --git a/tests/unit/02-utils-getqueryparamvalue.js b/tests/unit/02-utils-getqueryparamvalue.js new file mode 100644 index 000000000..21476baaa --- /dev/null +++ b/tests/unit/02-utils-getqueryparamvalue.js @@ -0,0 +1,76 @@ +/* eslint-env mocha */ +/* global chai */ + +describe("BOOMR.utils.getQueryParamValue()", function() { + var assert = chai.assert; + + function getA(url) { + var a = BOOMR.window.document.createElement("a"); + + a.href = url; + + return a; + }; + + it("Should return null when undefined is passed as param argument", function() { + assert.isNull(BOOMR.utils.getQueryParamValue(undefined)); + }); + + it("Should return null when null is passed as param argument", function() { + assert.isNull(BOOMR.utils.getQueryParamValue(null)); + }); + + it("Should return null when when an empty string is passed as param argument", function() { + assert.isNull(BOOMR.utils.getQueryParamValue("")); + }); + + it("Should return the correct parameter value (string url)", function() { + var url = "http://www.example.com/"; + + assert.strictEqual(BOOMR.utils.getQueryParamValue("foo", url + "?foo=&bar="), ""); + assert.strictEqual(BOOMR.utils.getQueryParamValue("foo", url + "?foo&bar="), ""); + assert.strictEqual(BOOMR.utils.getQueryParamValue("foo", url + "?foo=1&bar="), "1"); + assert.strictEqual(BOOMR.utils.getQueryParamValue("foo", url + "?foo=%20a&bar="), " a"); + assert.strictEqual(BOOMR.utils.getQueryParamValue("foo", url + "?foo=bar=1&bar=2"), "bar=1"); + assert.strictEqual(BOOMR.utils.getQueryParamValue("bar", url + "?foo=1&bar"), ""); + assert.strictEqual(BOOMR.utils.getQueryParamValue("bar", url + "?foo=1&bar="), ""); + assert.strictEqual(BOOMR.utils.getQueryParamValue("bar", url + "?foo=1&bar=2"), "2"); + assert.strictEqual(BOOMR.utils.getQueryParamValue("bar", url + "?foo=1&bar=#3"), ""); + assert.strictEqual(BOOMR.utils.getQueryParamValue("bar", url + "?foo=1&bar=2+3"), "2 3"); + }); + + it("Should return the correct parameter value (object url)", function() { + var url = "http://www.example.com/"; + + assert.strictEqual(BOOMR.utils.getQueryParamValue("foo", getA(url + "?foo=&bar=")), ""); + assert.strictEqual(BOOMR.utils.getQueryParamValue("foo", getA(url + "?foo&bar=")), ""); + assert.strictEqual(BOOMR.utils.getQueryParamValue("foo", getA(url + "?foo=1&bar=")), "1"); + assert.strictEqual(BOOMR.utils.getQueryParamValue("foo", getA(url + "?foo=%20a&bar=")), " a"); + assert.strictEqual(BOOMR.utils.getQueryParamValue("foo", getA(url + "?foo=bar=1&bar=2")), "bar=1"); + assert.strictEqual(BOOMR.utils.getQueryParamValue("bar", getA(url + "?foo=1&bar")), ""); + assert.strictEqual(BOOMR.utils.getQueryParamValue("bar", getA(url + "?foo=1&bar=")), ""); + assert.strictEqual(BOOMR.utils.getQueryParamValue("bar", getA(url + "?foo=1&bar=2")), "2"); + assert.strictEqual(BOOMR.utils.getQueryParamValue("bar", getA(url + "?foo=1&bar=#3")), ""); + assert.strictEqual(BOOMR.utils.getQueryParamValue("bar", getA(url + "?foo=1&bar=2+3")), "2 3"); + }); + + it("Should return null when there are no query parameters", function() { + var url = "http://www.example.com/"; + + assert.isNull(BOOMR.utils.getQueryParamValue("bar", url + "#?foo=1&bar=2")); + assert.isNull(BOOMR.utils.getQueryParamValue("bar", getA(url + "#?foo=1&bar=2"))); + }); + + it("Should return null when the param is not in the query parameters", function() { + var url = "http://www.example.com/"; + + assert.isNull(BOOMR.utils.getQueryParamValue("abc", url + "?foo=1&bar=2")); + assert.isNull(BOOMR.utils.getQueryParamValue("abc", getA(url + "?foo=1&bar=2"))); + }); + + it("Should return null when the param is malformed query parameters", function() { + var url = "http://www.example.com/"; + + assert.isNull(BOOMR.utils.getQueryParamValue("bar", url + "?foo=1&bar=_v%B0%FC")); + }); +}); diff --git a/tests/unit/02-utils-hashquerystring.js b/tests/unit/02-utils-hashquerystring.js new file mode 100644 index 000000000..b35cd31c6 --- /dev/null +++ b/tests/unit/02-utils-hashquerystring.js @@ -0,0 +1,83 @@ +/* eslint-env mocha */ +/* global chai */ + +describe("BOOMR.utils.hashQueryString()", function() { + var assert = chai.assert; + + it("Should return undefined when undefined is passed as argument", function() { + assert.isUndefined(BOOMR.utils.hashQueryString()); + }); + + it("Should return null when null is passed as argument", function() { + assert.isNull(BOOMR.utils.hashQueryString(null)); + }); + + it("Should return an empty string when an empty string is passed as argument", function() { + assert.strictEqual(BOOMR.utils.hashQueryString(""), ""); + }); + + it("Should return an empty string when a non-URL is passed as an argument", function() { + assert.strictEqual(BOOMR.utils.hashQueryString("foo"), ""); + }); + + it("Should return an empty string when an a non-string is passed as argument", function() { + assert.strictEqual(BOOMR.utils.hashQueryString(true), ""); + assert.strictEqual(BOOMR.utils.hashQueryString({}), ""); + assert.strictEqual(BOOMR.utils.hashQueryString([]), ""); + assert.strictEqual(BOOMR.utils.hashQueryString(1), ""); + }); + + it("Should return the URL untouched when passed with second argument false", function() { + var url = "http://www.example.org/page#/app"; + + assert.equal(BOOMR.utils.hashQueryString(url, false), url); + }); + + it("Should return the URL untouched when passed with second argument false when there is a query string in it", function() { + var url = "http://www.example.org/page?foo=1#app"; + + assert.equal(BOOMR.utils.hashQueryString(url, false), url); + }); + + it("Should return cleaned URL when passed with second argument true", function() { + var url = "http://www.example.org/page#/app"; + var expected = "http://www.example.org/page"; + + assert.equal(BOOMR.utils.hashQueryString(url, true), expected); + }); + + it("Should return empty URL when URL starts with \"/\"", function() { + var url = "/page", + expected = ""; + + assert.equal(BOOMR.utils.hashQueryString(url), expected); + }); + + it("Should return empty URL when URL starts with \"/\" and second argument is true", function() { + var url = "/page", + expected = ""; + + assert.equal(BOOMR.utils.hashQueryString(url, true), expected); + }); + + it("Should append a protocol string to the URL when URL starts with \"//\" and second argument is true", function() { + var url = "//page", + expected = window.location.protocol + url; + + assert.equal(BOOMR.utils.hashQueryString(url, true), expected); + }); + + it("Should hash the parameters in the URL but retain the hash when the second argument is false and 'FNV' was built into BOOMR.utils", function() { + var url = "http://www.example.org/app/page?key1=value&key2=value&key3=value&key4=value&key5=value#page", + expected = "http://www.example.org/app/page?27hrhl9c#page"; + + assert.equal(BOOMR.utils.hashQueryString(url, false), expected); + }); + + it("Should hash the parameters in the URL and remove the hash when the second argument is true and 'FNV' was built into BOOMR.utils", function() { + var url = "http://www.example.org/app/page?key1=value&key2=value&key3=value&key4=value&key5=value#page", + expected = "http://www.example.org/app/page?27hrhl9c"; + + assert.equal(BOOMR.utils.hashQueryString(url, true), expected); + }); +}); diff --git a/tests/unit/02-utils-ids.js b/tests/unit/02-utils-ids.js new file mode 100644 index 000000000..c307b0665 --- /dev/null +++ b/tests/unit/02-utils-ids.js @@ -0,0 +1,80 @@ +/* eslint-env mocha */ +/* global chai */ + +describe("BOOMR.utils ID functions", function() { + var assert = chai.assert; + + var uuidVersion4RegEx = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i; + + describe("BOOMR.utils.generateUUID()", function() { + it("Should return a version-4 UUID", function() { + assert.isTrue(BOOMR.utils.generateUUID().match(uuidVersion4RegEx) !== null); + }); + + it("Should return a version-4 UUID (1k loop)", function() { + for (var i = 0; i < 1000; i++) { + assert.isTrue(BOOMR.utils.generateUUID().match(uuidVersion4RegEx) !== null); + } + }); + + it("Should return a new UUID each time (1k loop)", function() { + var uuids = []; + + for (var i = 0; i < 1000; i++) { + var uuid = BOOMR.utils.generateUUID(); + + if (uuids[uuid]) { + assert.fail("Found a previously-generated UUID: " + uuid); + } + + uuids[uuid] = 1; + } + }); + }); + + describe("BOOMR.utils.generateId()", function() { + it("Should return a 40-character ID when no length is given", function() { + var id = BOOMR.utils.generateId(); + + assert.isDefined(id); + assert.strictEqual(id.length, 40); + }); + + it("Should return a 8-character ID when asked for", function() { + var id = BOOMR.utils.generateId(8); + + assert.isDefined(id); + assert.strictEqual(id.length, 8); + }); + + it("Should always return the character length asked for", function() { + for (var i = 1; i <= 40; i++) { + var id = BOOMR.utils.generateId(i); + + assert.isDefined(id); + assert.strictEqual(id.length, i); + } + }); + + it("Should return 40 characters if asked for more", function() { + var id = BOOMR.utils.generateId(50); + + assert.isDefined(id); + assert.strictEqual(id.length, 40); + }); + + it("Should return a new ID each time (1k loop)", function() { + var ids = []; + + for (var i = 0; i < 1000; i++) { + var id = BOOMR.utils.generateId(40); + + if (ids[id]) { + assert.fail("Found a previously-generated ID: " + id); + } + + ids[id] = 1; + } + }); + }); +}); diff --git a/tests/unit/02-utils-inarray.js b/tests/unit/02-utils-inarray.js new file mode 100644 index 000000000..04ce8374a --- /dev/null +++ b/tests/unit/02-utils-inarray.js @@ -0,0 +1,42 @@ +/* eslint-env mocha */ +/* global chai */ + +describe("BOOMR.utils.inarray()", function() { + var assert = chai.assert; + + it("Should return false when undefined is passed as the value argument", function() { + assert.isFalse(BOOMR.utils.inArray()); + }); + + it("Should return false when undefined is passed as the array argument", function() { + assert.isFalse(BOOMR.utils.inArray(1)); + }); + + it("Should return false when the array is empty", function() { + assert.isFalse(BOOMR.utils.inArray(1, [])); + }); + + it("Should return false when the element is not in the array", function() { + assert.isFalse(BOOMR.utils.inArray(1, [2])); + }); + + it("Should return false when the element is not exacty equal (===) to an element in the array", function() { + assert.isFalse(BOOMR.utils.inArray(1, ["1"])); + }); + + it("Should return true when the element is alone in the array", function() { + assert.isTrue(BOOMR.utils.inArray("a", ["a"])); + }); + + it("Should return true when the element is in the array twice", function() { + assert.isTrue(BOOMR.utils.inArray("a", ["a", "aa"])); + }); + + it("Should return true when the element is amongst other elements in the array", function() { + assert.isTrue(BOOMR.utils.inArray("a", ["b", "a", "c"])); + }); + + it("Should return true when the element is at the end of the array", function() { + assert.isTrue(BOOMR.utils.inArray("a", ["b", "c", "a"])); + }); +}); diff --git a/tests/unit/02-utils-is-ua-same-site-none-compatible.js b/tests/unit/02-utils-is-ua-same-site-none-compatible.js new file mode 100644 index 000000000..c31c17b4b --- /dev/null +++ b/tests/unit/02-utils-is-ua-same-site-none-compatible.js @@ -0,0 +1,89 @@ +/* eslint-env mocha */ +/* global chai */ + +describe("BOOMR.utils.isUASameSiteNoneCompatible()", function() { + var assert = chai.assert; + + // Incompatible Chrome + it("Should return false if a Chrome browser can't create safely cookies with SameSite=None", function() { + assert.isFalse(BOOMR.utils.isUASameSiteNoneCompatible("Mozilla/5.0 (Linux; Android 7.1.1; Moto E (4) Build/NCQS26.69-64-3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Mobile Safari/537.36")); + assert.isFalse(BOOMR.utils.isUASameSiteNoneCompatible("Mozilla/5.0 (X11; CrOS i686 9334.72.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.140 Safari/537.36")); + }); + + // Incompatible Chromium + it("Should return false if a Chromium browser can't create safely cookies with SameSite=None", function() { + assert.isFalse(BOOMR.utils.isUASameSiteNoneCompatible("Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/65.0.3325.181 Chrome/65.0.3325.181 Safari/537.36")); + }); + + // Incompatible UCBrowser + it("Should return false if an UC browser can't create safely cookies with SameSite=None", function() { + assert.isFalse(BOOMR.utils.isUASameSiteNoneCompatible("Mozilla/5.0 (Linux; U; Android 8.0.0; en-US; SM-G930F Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.108 UCBrowser/12.12.8.1206 Mobile Safari/537.36")); + assert.isFalse(BOOMR.utils.isUASameSiteNoneCompatible("Mozilla/5.0 (iPhone; CPU iPhone OS 13_5_1 like Mac OS X; en-US) AppleWebKit/537.51.1 (KHTML, like Gecko) Mobile/17F80 UCBrowser/11.3.5.1203 Mobile")); + }); + + // Incompatible Safari on Mac OS 10.4.* + it("Should return false if Safari on Mac OS 10.4.* can't create safely cookies with SameSite=None", function() { + assert.isFalse(BOOMR.utils.isUASameSiteNoneCompatible("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1 Safari/605.1.15")); + }); + + // Incompatible embeded browser on Mac OS 10.4.* + it("Should return false if embeded browser on Mac OS 10.4.* can't create safely cookies with SameSite=None", function() { + assert.isFalse(BOOMR.utils.isUASameSiteNoneCompatible("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/605.1.15 (KHTML, like Gecko)")); + assert.isFalse(BOOMR.utils.isUASameSiteNoneCompatible("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/605.1.15 (KHTML, like Gecko)")); + }); + + // Incompatible all browsers on iOS and iPad OS 12 + it("Should return false if iOS and iPad OS 12 for all browsers can't create safely cookies with SameSite=None", function() { + assert.isFalse(BOOMR.utils.isUASameSiteNoneCompatible("Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1 Mobile/15E148 Safari/604.1")); + assert.isFalse(BOOMR.utils.isUASameSiteNoneCompatible("Mozilla/5.0 (iPhone; CPU iPhone OS 12_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0 Mobile/15E148 Safari/604.1")); + assert.isFalse(BOOMR.utils.isUASameSiteNoneCompatible("Mozilla/5.0 (iPhone; CPU iPhone OS 12_3_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1.1 Mobile/15E148 Safari/604.1")); + }); + + // Compatible Chromium + it("Should return true if a Chromium browser can create safely cookies with SameSite=None", function() { + assert.isTrue(BOOMR.utils.isUASameSiteNoneCompatible("Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/81.0.4044.138 Chrome/81.0.4044.138 Safari/537.36")); + assert.isTrue(BOOMR.utils.isUASameSiteNoneCompatible("Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/81.0.4044.138 Chrome/81.0.4044.138 Safari/537.36")); + assert.isTrue(BOOMR.utils.isUASameSiteNoneCompatible("Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/78.0.3904.108 Chrome/78.0.3904.108 Safari/537.36")); + assert.isTrue(BOOMR.utils.isUASameSiteNoneCompatible("Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/77.0.3865.90 Chrome/77.0.3865.90 Safari/537.36")); + }); + + // Compatible Chrome + it("Should return true if a Chrome browser can create safely cookies with SameSite=None", function() { + assert.isTrue(BOOMR.utils.isUASameSiteNoneCompatible("Mozilla/5.0 (Linux; Android 5.1; QTAIR7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 Safari/537.36")); + assert.isTrue(BOOMR.utils.isUASameSiteNoneCompatible("Mozilla/5.0 (Linux; Android 6.0; VFD 500 Build/MRA58K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.89 Mobile Safari/537.36")); + assert.isTrue(BOOMR.utils.isUASameSiteNoneCompatible("Mozilla/5.0 (Linux; Android 10; SAMSUNG SM-G975U) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/11.0 Chrome/75.0.3770.143 Mobile Safari/537.36")); + assert.isTrue(BOOMR.utils.isUASameSiteNoneCompatible("Mozilla/5.0 (Linux; Android 9; moto g(7) supra) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.92 Mobile Safari/537.36")); + }); + + // Compatible iOS and iPad OS + it("Should return true if iOS and iPad OS is not 12 and all browsers can create safely cookies with SameSite=None", function() { + assert.isTrue(BOOMR.utils.isUASameSiteNoneCompatible("Mozilla/5.0 (iPad; CPU OS 9_0_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13A404 Safari/601.1")); + assert.isTrue(BOOMR.utils.isUASameSiteNoneCompatible("Mozilla/5.0 (iPad; CPU OS 13_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) GSA/89.2.287201133 Mobile/15E148 Safari/604.1")); + assert.isTrue(BOOMR.utils.isUASameSiteNoneCompatible("Mozilla/5.0 (iPad; CPU OS 11_2_6 like Mac OS X) AppleWebKit/604.1.34 (KHTML, like Gecko) CriOS/76.0.3809.123 Mobile/15D100 Safari/604.1")); + assert.isTrue(BOOMR.utils.isUASameSiteNoneCompatible("Mozilla/5.0 (iPad; CPU OS 13_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/79.0.3945.73 Mobile/15E148 Safari/604.1")); + assert.isTrue(BOOMR.utils.isUASameSiteNoneCompatible("Mozilla/5.0 (iPad; CPU OS 13_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.2 Mobile/15E148 Safari/604.1")); + assert.isTrue(BOOMR.utils.isUASameSiteNoneCompatible("Mozilla/5.0 (iPhone; CPU OS 13_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) FxiOS/27.0 Mobile/15E148 Safari/605.1.15")); + }); + + // Compatible UCBrowser + it("Should return true if an UC browser can create safely cookies with SameSite=None", function() { + assert.isTrue(BOOMR.utils.isUASameSiteNoneCompatible("Mozilla/5.0 (Linux; U; Android 7.0; en-US; QMobile Q Infinity E Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.108 UCBrowser/13.1.8.1295 Mobile Safari/537.36")); + assert.isTrue(BOOMR.utils.isUASameSiteNoneCompatible("Mozilla/5.0 (Linux; U; Android 10; en-US; SM-A505F Build/QP1A.190711.020) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.108 UCBrowser/13.2.0.1296 Mobile Safari/537.36")); + }); + + // Compatible for Safari not on Mac OS 10.4.* + it("Should return true if Safari not on Mac OS 10.4.* can create safely cookies with SameSite=None", function() { + assert.isTrue(BOOMR.utils.isUASameSiteNoneCompatible("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1.1 Safari/605.1.15")); + assert.isTrue(BOOMR.utils.isUASameSiteNoneCompatible("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.1 Safari/605.1.15")); + assert.isTrue(BOOMR.utils.isUASameSiteNoneCompatible("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Safari/605.1.15")); + assert.isTrue(BOOMR.utils.isUASameSiteNoneCompatible("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.2 Safari/605.1.15")); + assert.isTrue(BOOMR.utils.isUASameSiteNoneCompatible("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.2 Safari/605.1.15")); + }); + + // Compatible embeded browser on different from Mac OS 10.4.* + it("Should return true if embeded browser on Mac OS different from 10.4.* can create safely cookies with SameSite=None", function() { + assert.isTrue(BOOMR.utils.isUASameSiteNoneCompatible("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/605.1.15 (KHTML, like Gecko)")); + assert.isTrue(BOOMR.utils.isUASameSiteNoneCompatible("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko)")); + assert.isTrue(BOOMR.utils.isUASameSiteNoneCompatible("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/601.7.8 (KHTML, like Gecko)")); + }); +}); diff --git a/tests/unit/02-utils-isarray.js b/tests/unit/02-utils-isarray.js new file mode 100644 index 000000000..ac349a75a --- /dev/null +++ b/tests/unit/02-utils-isarray.js @@ -0,0 +1,20 @@ +/* eslint-env mocha */ +/* global chai */ + +describe("BOOMR.utils.isArray()", function() { + var assert = chai.assert; + + it("Should return false if input is not an array", function() { + assert.isFalse(BOOMR.utils.isArray(null)); + assert.isFalse(BOOMR.utils.isArray(undefined)); + assert.isFalse(BOOMR.utils.isArray("a")); + assert.isFalse(BOOMR.utils.isArray(1)); + assert.isFalse(BOOMR.utils.isArray({})); + assert.isFalse(BOOMR.utils.isArray(function() {})); + }); + + it("Should return true if input is an array", function() { + assert.isTrue(BOOMR.utils.isArray([])); + assert.isTrue(BOOMR.utils.isArray([1, 2, 3, null, undefined])); + }); +}); diff --git a/tests/unit/02-utils-isinteger.js b/tests/unit/02-utils-isinteger.js new file mode 100644 index 000000000..d4a225168 --- /dev/null +++ b/tests/unit/02-utils-isinteger.js @@ -0,0 +1,67 @@ +/* eslint-env mocha */ +/* global chai */ + +/* Test cases from MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isInteger#Examples +Number.isInteger(0); // true +Number.isInteger(1); // true +Number.isInteger(-100000); // true +Number.isInteger(99999999999999999999999); // true + +Number.isInteger(0.1); // false +Number.isInteger(Math.PI); // false + +Number.isInteger(NaN); // false +Number.isInteger(Infinity); // false +Number.isInteger(-Infinity); // false +Number.isInteger('10'); // false +Number.isInteger(true); // false +Number.isInteger(false); // false +Number.isInteger([1]); // false +*/ + +describe("BOOMR.utils.isInteger()", function() { + var assert = chai.assert; + + // Blow away the original isInteger method to exercise the polyfill + // in BOOMR.utils.isInteger(). + var origIsInteger = Number.isInteger; + + Number.isInteger = undefined; + + it("Should not have access to the native Number.isInteger function", function() { + assert.isUndefined(Number.isInteger); + }); + + it("Should return false when input is not an integer", function(){ + assert.isUndefined(Number.isInteger); + assert.isFalse(BOOMR.utils.isInteger("-123")); + assert.isFalse(BOOMR.utils.isInteger("123")); + assert.isFalse(BOOMR.utils.isInteger("stringvalue")); + assert.isFalse(BOOMR.utils.isInteger(0.1)); + assert.isFalse(BOOMR.utils.isInteger(Math.PI)); + assert.isFalse(BOOMR.utils.isInteger(NaN)); + assert.isFalse(BOOMR.utils.isInteger(Infinity)); + assert.isFalse(BOOMR.utils.isInteger(-Infinity)); + assert.isFalse(BOOMR.utils.isInteger(true)); + assert.isFalse(BOOMR.utils.isInteger(false)); + assert.isFalse(BOOMR.utils.isInteger([1])); + }); + + it("Should return true when input is an integer", function() { + assert.isUndefined(Number.isInteger); + assert.isTrue(BOOMR.utils.isInteger(1)); + assert.isTrue(BOOMR.utils.isInteger(0)); + assert.isTrue(BOOMR.utils.isInteger(-100000)); + assert.isTrue(BOOMR.utils.isInteger(99999999999999999999999)); + }); + + it("Should have restored accesss to the native Number.isInteger function", function() { + if (typeof origIsInteger !== "undefined") { + Number.isInteger = origIsInteger; + assert.isDefined(Number.isInteger); + } + else { + this.skip(); + } + }); +}); diff --git a/tests/unit/02-utils-isnative.js b/tests/unit/02-utils-isnative.js new file mode 100644 index 000000000..fa0efca49 --- /dev/null +++ b/tests/unit/02-utils-isnative.js @@ -0,0 +1,43 @@ +/* eslint-env mocha */ +/* global chai */ + +describe("BOOMR.utils.isNative()", function() { + var assert = chai.assert; + + it("Should return false if input is not an function", function() { + assert.isFalse(BOOMR.utils.isNative(null)); + assert.isFalse(BOOMR.utils.isNative(undefined)); + assert.isFalse(BOOMR.utils.isNative()); + assert.isFalse(BOOMR.utils.isNative("")); + assert.isFalse(BOOMR.utils.isNative(1)); + assert.isFalse(BOOMR.utils.isNative(true)); + assert.isFalse(BOOMR.utils.isNative(false)); + assert.isFalse(BOOMR.utils.isNative(NaN)); + }); + + it("Should return true if input is a native function", function() { + assert.isTrue(BOOMR.utils.isNative(Object)); + assert.isTrue(BOOMR.utils.isNative(Number)); + assert.isTrue(BOOMR.utils.isNative(parseInt)); + }); + + it("Should return false if input is not a native function", function() { + var nonNative = function(bar) { + return false; + }; + + assert.isFalse(BOOMR.utils.isNative(nonNative)); + }); + + it("Should return false for a polyfill", function() { + var _parseInt = window.parseInt; + + window.parseInt = function(bar) { + return false; + }; + + assert.isFalse(BOOMR.utils.isNative(window.parseInt)); + + window.parseInt = _parseInt; + }); +}); diff --git a/tests/unit/02-utils-localstorage.js b/tests/unit/02-utils-localstorage.js new file mode 100644 index 000000000..49d55d099 --- /dev/null +++ b/tests/unit/02-utils-localstorage.js @@ -0,0 +1,184 @@ +/* eslint-env mocha */ +/* global chai */ + +describe("BOOMR.utils localStorage", function() { + var assert = chai.assert; + + var storageName = "myStorage"; + + /* + NOTE: + + These tests can only run in a client-server setup with a properly + configured FQDN for the server. + + Please read: + RFC 2109 (https://www.ietf.org/rfc/rfc2109.txt) + and this thread on the chromium bugtracker: + https://code.google.com/p/chromium/issues/detail?id=535 + + In your development environment please configure your localhost with a fully + qualified domain name locally: + + In a UNIX/Mac/Linux environment you can add a name for 127.0.0.1 to + your /etc/hosts such as: + 127.0.0.1 www.example.org www + + You can do the same under windows, however the path to the file is a + little different: + + Valid for Windown Vista/7/2008/2012: C:\Windows\System32\drivers\etc\hosts + + We (as in the boomerang team) are not responsible for any accidental or + direct damages and or damage claims. See LICENSE for further information. + */ + + if (window.location.protocol === "file:") { + return; + } + + // need to skip some test in browsers without support + var canSetStorage = (function() { + var name = "_boomr_csls", + result = false; + + try { + window.localStorage.setItem(name, name); + result = (window.localStorage.getItem(name) === name); + window.localStorage.removeItem(name); + } + catch (ignore) { + // Empty + } + + return result; + })(); + + describe("BOOMR.utils.getLocalStorage()", function() { + it("Should have an exisiting BOOMR.utils.getLocalStorage function", function() { + assert.isFunction(BOOMR.utils.getLocalStorage); + }); + + it("Should return null when calling getLocalStorage() with empty arguments", function() { + assert.isNull(BOOMR.utils.getLocalStorage()); + }); + + it("Should return null when calling getLocalStorage with empty String", function() { + assert.isNull(BOOMR.utils.getLocalStorage("")); + }); + + it("Should return null when calling with null as first argument", function() { + assert.isNull(BOOMR.utils.getLocalStorage(null)); + }); + + if (canSetStorage) { + it("Should return undefined when calling with non existing storage name", function() { + assert.isUndefined(BOOMR.utils.getLocalStorage("some-non-existing-storage")); + }); + } + else { + it("Should return null when localStorage is not supported ", function() { + assert.isNull(BOOMR.utils.getLocalStorage("any-storage")); + }); + } + }); + + describe("BOOMR.utils.setLocalStorage()", function() { + it("Should have an exisiting BOOMR.utils.setLocalStorage function", function() { + assert.isFunction(BOOMR.utils.setLocalStorage); + }); + + it("Should return false if no name was passed as first argument to setLocalStorage()", function() { + assert.isFalse(BOOMR.utils.setLocalStorage()); + }); + + if (canSetStorage) { + it("Should return false when setting only storage name", function() { + assert.isFalse(BOOMR.utils.setLocalStorage(storageName)); + }); + + it("Should return true when setting storage with value", function() { + assert.isTrue(BOOMR.utils.setLocalStorage(storageName, {key: "value"})); + BOOMR.utils.removeLocalStorage(storageName); + }); + + it("Should return the value that we've set previously", function() { + var value = { key: "value", key2: true, key3: 123, key4: {subkey: "value"} }; + + BOOMR.utils.setLocalStorage(storageName, value); + assert.deepEqual(BOOMR.utils.getLocalStorage(storageName), value); + BOOMR.utils.removeLocalStorage(storageName); + }); + + it("Should return the EXACT value string that we've set previously", function() { + var value = "1"; + var value_strict_false = 1; + + BOOMR.utils.setLocalStorage(storageName, { key: value }); + assert.strictEqual(BOOMR.utils.getLocalStorage(storageName).key, value); + assert.notStrictEqual(BOOMR.utils.getLocalStorage(storageName).key, value_strict_false); + BOOMR.utils.removeLocalStorage(storageName); + }); + + it("Should return the value that we've set previously with a large expiry", function() { + var value = { key: "value", key2: true, key3: 123, key4: {subkey: "value"} }; + + BOOMR.utils.setLocalStorage(storageName, value, 5 * 60); + assert.deepEqual(BOOMR.utils.getLocalStorage(storageName), value); + BOOMR.utils.removeLocalStorage(storageName); + }); + + it("Should return undefined for a storage that we've set previously with a zero expiry", function() { + var value = { key: "value", key2: true, key3: 123, key4: {subkey: "value"} }; + + BOOMR.utils.setLocalStorage(storageName, value, 0); + assert.isUndefined(BOOMR.utils.getLocalStorage(storageName)); + }); + + it("Should return undefined for a storage that we've set previously with a negative expiry", function() { + var value = { key: "value", key2: true, key3: 123, key4: {subkey: "value"} }; + + BOOMR.utils.setLocalStorage(storageName, value, -1); + assert.isUndefined(BOOMR.utils.getLocalStorage(storageName)); + }); + + it("Should return false when trying to set a storage item bigger than 50000 characters", function() { + var value = ""; + + for (var index = 0; index <= 50000; index++) { + value += "1"; + } + + assert.isFalse(BOOMR.utils.setLocalStorage("failStorage", {key: value})); + }); + } + else { + it("Should return false when localStorage is not supported", function() { + assert.isFalse(BOOMR.utils.setLocalStorage(storageName, {key: "value"})); + }); + } + }); + + describe("BOOMR.utils.removeLocalStorage()", function() { + it("Should return false when given no argurments", function() { + assert.isFalse(BOOMR.utils.removeLocalStorage()); + }); + + if (canSetStorage) { + it("Should return true when removing storage with value", function() { + assert.isTrue(BOOMR.utils.removeLocalStorage(storageName)); + }); + + it("Should remove a storage item we've set previously", function() { + assert.isTrue(BOOMR.utils.setLocalStorage(storageName, { key: "value" })); + assert.isTrue(BOOMR.utils.removeLocalStorage(storageName)); + assert.isUndefined(BOOMR.utils.getLocalStorage(storageName)); + }); + } + else { + it("Should return false when localStorage is not supported", function() { + assert.isFalse(BOOMR.utils.removeLocalStorage(storageName)); + }); + } + }); +}); diff --git a/tests/unit/02-utils-makeselector.js b/tests/unit/02-utils-makeselector.js new file mode 100644 index 000000000..c17c29287 --- /dev/null +++ b/tests/unit/02-utils-makeselector.js @@ -0,0 +1,270 @@ +/* eslint-env mocha */ +/* global chai */ + +function testSelector(nodes, nodeDict) { + var nodeList = {}; + + // iterate through nodes list to construct a tree of parent and child nodes + for (var attr = 0; attr < nodes.length; attr++) { + // if element type, create element node with corresponding tagname + if (nodes[attr].type === "element") { + var node = document.createElement(nodes[attr].tagname); + + // add ID if exists + if (nodes[attr].id) { + node.id = nodes[attr].id; + } + + // add class if exists + if (nodes[attr].className) { + node.className = nodes[attr].className; + } + } + + // if text type, create text node with buffer text + else if (nodes[attr].type === "text") { + var node = document.createTextNode("text"); + } + + // otherwise node is null + else { + var node = null; + } + + // add created node object to nodeList + nodeList[attr] = node; + + // if not the first node in the list, not null, + // and preceding node not null, set this node's child to preceding node + if (attr > 0 && nodeList[attr] !== null && nodeList[attr - 1] !== null) { + nodeList[attr].appendChild(nodeList[attr - 1]); + } + } + + // first node in nodeList is lowest tier child, so find its selector + return BOOMR.utils.makeSelector(nodeList[0]); +} + +describe("BOOMR.utils.makeSelector", function() { + var assert = chai.assert; + + it("single node with ID", function() { + var nodes = [ + {"type": "element", "tagname": "div", "id": "test"} + ]; + + var selector = testSelector(nodes); + + assert.deepEqual("div#test", selector); + }); + + it("node with class and parent with ID", function() { + var nodes = [ + {"type": "element", "tagname": "div", "className": "test"}, + {"type": "element", "tagname": "div", "id": "yum"} + ]; + + var selector = testSelector(nodes); + + assert.deepEqual("div#yum div.test", selector); + }); + + it("node with class and parent with class and ID", function() { + var nodes = [ + {"type": "element", "tagname": "div", "className": "test"}, + {"type": "element", "tagname": "div", "className": "test2", "id": "yum"} + ]; + + var selector = testSelector(nodes); + + assert.deepEqual("div#yum div.test", selector); + }); + + it("node with class and parent with class", function() { + var nodes = [ + {"type": "element", "tagname": "div", "className": "test"}, + {"type": "element", "tagname": "img", "className": "test2"} + ]; + + var selector = testSelector(nodes); + + assert.deepEqual("img.test2 div.test", selector); + }); + + it("check three nodes, stops at first ID when more parents exist", function() { + var nodes = [ + {"type": "element", "tagname": "ul", "className": "test"}, + {"type": "element", "tagname": "img", "className": "test2"}, + {"type": "element", "tagname": "div", "id": "test3"}, + {"type": "element", "tagname": "ul", "className": "test"} + ]; + + var selector = testSelector(nodes); + + assert.deepEqual("div#test3 img.test2 ul.test", selector); + }); + + it("check four nodes", function() { + var nodes = [ + {"type": "element", "tagname": "ul", "className": "test"}, + {"type": "element", "tagname": "img", "className": "test2"}, + {"type": "element", "tagname": "div", "className": "test3"}, + {"type": "element", "tagname": "ul", "id": "id4"}, + {"type": "element", "tagname": "div", "className": "test5", "id": "nextID"} + ]; + + var selector = testSelector(nodes); + + assert.deepEqual("ul#id4 div.test3 img.test2 ul.test", selector); + }); + + it("check five nodes, check asterick, check textNode", function() { + var nodes = [ + {"type": "text"}, + {"type": "element", "tagname": "ul", "className": "test"}, + {"type": "element", "tagname": "img", "className": "test2"}, + {"type": "element", "tagname": "div", "className": "test3"}, + {"type": "element", "tagname": "ul", "className": "test4"}, + {"type": "element", "tagname": "span", "id": "id5"}, + {"type": "element", "tagname": "div", "className": "test5", "id": "nextID"} + ]; + + var selector = testSelector(nodes); + + assert.deepEqual("span#id5 * div.test3 img.test2 ul.test", selector); + }); + + it("check asterick with > 5 nodes, check no ID or class", function() { + var nodes = [ + {"type": "element", "tagname": "span"}, + {"type": "element", "tagname": "ul", "className": "test"}, + {"type": "element", "tagname": "img", "className": "test2"}, + {"type": "element", "tagname": "div"}, + {"type": "element", "tagname": "ul", "className": "test4"}, + {"type": "element", "tagname": "span"}, + {"type": "element", "tagname": "div", "className": "test5", "id": "nextID"} + ]; + + var selector = testSelector(nodes); + + assert.deepEqual("div#nextID * img.test2 ul.test span", selector); + }); + + it("same as above but final node no ID", function() { + var nodes = [ + {"type": "element", "tagname": "span"}, + {"type": "element", "tagname": "ul", "className": "test"}, + {"type": "element", "tagname": "img", "className": "test2"}, + {"type": "element", "tagname": "div"}, + {"type": "element", "tagname": "ul", "className": "test4"}, + {"type": "element", "tagname": "span"}, + {"type": "element", "tagname": "div", "className": "test5"} + ]; + + var selector = testSelector(nodes); + + assert.deepEqual("div.test5 * img.test2 ul.test span", selector); + }); + + it("same as above but ends at BODY", function() { + var nodes = [ + {"type": "element", "tagname": "span"}, + {"type": "element", "tagname": "ul", "className": "test"}, + {"type": "element", "tagname": "img", "className": "test2"}, + {"type": "element", "tagname": "div"}, + {"type": "element", "tagname": "ul", "className": "test4"}, + {"type": "element", "tagname": "span"}, + {"type": "element", "tagname": "body"} + ]; + + var selector = testSelector(nodes); + + assert.deepEqual("span * img.test2 ul.test span", selector); + }); + + it("same as above but ends at BODY as 5th parent", function() { + var nodes = [ + {"type": "element", "tagname": "span"}, + {"type": "element", "tagname": "ul", "className": "test"}, + {"type": "element", "tagname": "img", "className": "test2"}, + {"type": "element", "tagname": "div"}, + {"type": "element", "tagname": "ul", "className": "test4"}, + {"type": "element", "tagname": "body"} + ]; + + var selector = testSelector(nodes); + + assert.deepEqual("ul.test4 * img.test2 ul.test span", selector); + }); + + it("same as above but ends at BODY as 4th parent", function() { + var nodes = [ + {"type": "element", "tagname": "span"}, + {"type": "element", "tagname": "ul", "className": "test"}, + {"type": "element", "tagname": "img", "className": "test2"}, + {"type": "element", "tagname": "div"}, + {"type": "element", "tagname": "body"} + ]; + + var selector = testSelector(nodes); + + assert.deepEqual("div img.test2 ul.test span", selector); + }); + + it("same as above but ends at BODY as 3rd parent", function() { + var nodes = [ + {"type": "element", "tagname": "span"}, + {"type": "element", "tagname": "ul", "className": "test"}, + {"type": "element", "tagname": "img", "className": "test2"}, + {"type": "element", "tagname": "body"} + ]; + + var selector = testSelector(nodes); + + assert.deepEqual("img.test2 ul.test span", selector); + }); + + it("check null in middle", function() { + var nodes = [ + {"type": "element", "tagname": "span"}, + {"type": "element", "tagname": "ul", "className": "test"}, + {"type": "element", "tagname": "img", "className": "test2"}, + {"type": "element", "tagname": "div"}, + {}, + {"type": "element", "tagname": "ul", "className": "test4"}, + {"type": "element", "tagname": "span"}, + {"type": "element", "tagname": "div", "className": "test5"} + ]; + + var selector = testSelector(nodes); + + assert.deepEqual("div img.test2 ul.test span", selector); + }); + + it("check body", function() { + var nodes = [ + {"type": "element", "tagname": "span"}, + {"type": "element", "tagname": "ul", "className": "test"}, + {"type": "element", "tagname": "img", "className": "test2"}, + {"type": "element", "tagname": "div"}, + {"type": "element", "tagname": "ul", "className": "test4"}, + {"type": "element", "tagname": "span"}, + {"type": "element", "tagname": "body"} + ]; + + var selector = testSelector(nodes); + + assert.deepEqual("span * img.test2 ul.test span", selector); + }); + + it("check single textNode", function() { + var nodes = [ + {"type": "text", "tagname": "span"}, + {"type": "element", "tagname": "body"} + ]; + + var selector = testSelector(nodes); + + assert.deepEqual("", selector); + }); +}); diff --git a/tests/unit/02-utils-objecttostring.js b/tests/unit/02-utils-objecttostring.js new file mode 100644 index 000000000..8b7bd031f --- /dev/null +++ b/tests/unit/02-utils-objecttostring.js @@ -0,0 +1,61 @@ +/* eslint-env mocha */ +/* global chai */ + +describe("BOOMR.utils.objectToString()", function() { + var assert = chai.assert; + + it("Should have an existing function BOOMR.utils.objectToString()", function() { + assert.isFunction(BOOMR.utils.objectToString); + }); + + it("Should return a string representation of {\"key\": \"value\" } as key=value ", function() { + var object = {"key": "value"}, + expected = "key=value"; + + assert.equal(BOOMR.utils.objectToString(object), expected); + }); + + it("Should return a string representation of {\"key\": \"value\", \"key2\": \"value2\" } as seperated by the default seperator (\"\\n\\t\") as key=value,key2=value2", function() { + var object = {"key": "value", "key2": "value2"}, + expected = "key=value\n\tkey2=value2"; + + assert.equal(BOOMR.utils.objectToString(object), expected); + }); + + it("Should return a string representation of {\"key\": \"value\", \"key2\": \"value2\" } as seperated by the custom seperator \"|\" as key=value|key2=value2", function() { + var object = {"key": "value", "key2": "value2"}, + expected = "key=value|key2=value2"; + + assert.equal(BOOMR.utils.objectToString(object, "|"), expected); + }); + + it("Should return a string representation of a nested object as a flat key value string with default seperator(\",\") as key=value\n\tarray=value2%2Cvalue3 ", function() { + var object = {"key": "value", "array": ["value2", "value3"]}, + expected = "key=value\n\tarray=value2,value3"; + + assert.equal(BOOMR.utils.objectToString(object), expected); + }); + + it("Should return a string representation of a nested array as a flat key value string with default seperator (\",\") as \"1,2,3%2C4,5,6\" ", function() { + var object = + ["1", "2", ["3,4"], [ ["5", "6"] ] ], + expected = "1,2,3,4,5,6"; + + assert.equal(BOOMR.utils.objectToString(object, null, 3), expected); + }); + + it("Should escape special characters using encodeURIComponent", function() { + var object = { "key": "//file" }, + expected = "key=%2F%2Ffile"; + + assert.equal(BOOMR.utils.objectToString(object, "&"), expected); + }); + + it("Should validate the old YUI Boomerang test", function() { + var o = { one: 1, two: 2, three: "3rd", four: null, five: undefined, six: 0, seven: 1.2, eight: "a=b", nine: [1, 2] }; + var expected = "one=1&two=2&three=3rd&four=null&five=undefined&six=0&seven=1.2&eight=" + encodeURIComponent("a=b") + "&nine=" + encodeURIComponent("1,2"); + + assert.strictEqual(expected, BOOMR.utils.objectToString(o, "&")); + assert.strictEqual(decodeURIComponent(expected.replace(/&/g, "\n\t")), BOOMR.utils.objectToString(o)); + }); +}); diff --git a/tests/unit/02-utils-pluginconfig.js b/tests/unit/02-utils-pluginconfig.js new file mode 100644 index 000000000..baeed0826 --- /dev/null +++ b/tests/unit/02-utils-pluginconfig.js @@ -0,0 +1,86 @@ +/* eslint-env mocha */ +/* global chai */ + +describe("BOOMR.utils.pluginConfig()", function() { + var assert = chai.assert; + + var pluginName = "myPlugin"; + + it("Should return false if no config was set", function() { + assert.isFalse(BOOMR.utils.pluginConfig({}, undefined, "", [])); + assert.isFalse(BOOMR.utils.pluginConfig({}, null, "", [])); + assert.isFalse(BOOMR.utils.pluginConfig({}, false, "", [])); + }); + + it("Should return false if config was set but no matching plugin name was defined in that config", function() { + var config = { otherPlugin: { existingKey: "value" } }; + + assert.isFalse(BOOMR.utils.pluginConfig({}, config, pluginName, [])); + assert.isFalse(BOOMR.utils.pluginConfig({}, config, "", [])); + }); + + it("Should return false if no configuration was set, no valid config-keys were defined, but a value to a key was requested", function() { + assert.isFalse(BOOMR.utils.pluginConfig({}, {}, pluginName, ["notExistingKey"])); + }); + + it("Should return false if a configuration was set but no valid key from that configuration was requested", function() { + var config = { myPlugin: { existingKey: "value" } }; + + assert.isFalse(BOOMR.utils.pluginConfig({}, config, pluginName, ["notExistingKey"])); + }); + + it("Should return false if a key was requested that is inbounds to what is expected but was not set by the user", function() { + var config = { myPlugin: { otherKey: "value" } }; + + assert.isFalse(BOOMR.utils.pluginConfig({}, config, pluginName, ["existingKey"])); + }); + + it("Should return true if a key was requested that exists and was configured and matches expected return value", function() { + var config = { myPlugin: { key: "value" } }; + + assert.isTrue(BOOMR.utils.pluginConfig({}, config, pluginName, ["key"])); + }); + + it("Should update the impl object with the config option, even if it didn't exist before", function() { + var impl = {}; + var config = { myPlugin: { key: "value2" } }; + + assert.isTrue(BOOMR.utils.pluginConfig(impl, config, pluginName, ["key"])); + assert.strictEqual(impl.key, "value2"); + }); + + it("Should update the impl object with the config option, overwriting the existing value", function() { + var impl = { key: "value1" }; + var config = { myPlugin: { key: "value2" } }; + + assert.isTrue(BOOMR.utils.pluginConfig(impl, config, pluginName, ["key"])); + assert.strictEqual(impl.key, "value2"); + }); + + it("Should validate the old YUI Boomerang test", function() { + // these were carried over from YUI tests + var o = {}; + var config = { ABC: { one: 1, two: [2], three: "3rd", four: 4.1, five: false } }; + + assert.isFalse(BOOMR.utils.pluginConfig(o, config, "DEF", [])); + assert.isFalse(BOOMR.utils.pluginConfig(o, config, "ABC", [])); + assert.isFalse(BOOMR.utils.pluginConfig(o, config, "DEF", ["one", "two"])); + assert.isTrue(BOOMR.utils.pluginConfig(o, config, "ABC", ["one", "two"])); + + assert.strictEqual(1, o.one); + assert.isArray(o.two); + assert.equal(1, o.two.length); + assert.equal(2, o.two[0]); + assert.isUndefined(o.three); + + assert.isTrue(BOOMR.utils.pluginConfig(o, config, "ABC", ["five"])); + + assert.strictEqual(1, o.one); + assert.isArray(o.two); + assert.equal(1, o.two.length); + assert.equal(2, o.two[0]); + assert.isUndefined(o.three); + assert.isDefined(o.five); + assert.isFalse(o.five); + }); +}); diff --git a/tests/unit/02-utils.js b/tests/unit/02-utils.js new file mode 100644 index 000000000..1bb9abdfa --- /dev/null +++ b/tests/unit/02-utils.js @@ -0,0 +1,40 @@ +/* eslint-env mocha */ +/* global chai */ + +describe("BOOMR.utils exports", function() { + var assert = chai.assert; + + it("Should have an existing BOOMR.utils object", function() { + assert.isObject(BOOMR.utils); + }); + + it("Should have an existing Function BOOMR.utils.cleanUpURL()", function() { + assert.isFunction(BOOMR.utils.cleanupURL); + }); + + it("Should have an existing Function BOOMR.utils.hashQueryString()", function() { + assert.isFunction(BOOMR.utils.hashQueryString); + }); + + it("Should have an existing Function BOOMR.utils.pluginConfig()", function() { + assert.isFunction(BOOMR.utils.pluginConfig); + }); + + it("Should have an existing Function BOOMR.utils.hashString()", function() { + assert.isFunction(BOOMR.utils.hashString); + }); + + describe("isObjectEmpty()", function() { + it("Should return false for non-empty objects", function() { + assert.isFalse(BOOMR.utils.isObjectEmpty({ a: 1 })); + + assert.isFalse(BOOMR.utils.isObjectEmpty({ a: 1, b: 2 })); + }); + + it("Should return true for empty objects", function() { + assert.isTrue(BOOMR.utils.isObjectEmpty({})); + + assert.isTrue(BOOMR.utils.isObjectEmpty()); + }); + }); +}); diff --git a/tests/unit/03-checkdocumentdomain.js b/tests/unit/03-checkdocumentdomain.js new file mode 100644 index 000000000..54cfdce78 --- /dev/null +++ b/tests/unit/03-checkdocumentdomain.js @@ -0,0 +1,18 @@ +/* eslint-env mocha */ +/* global chai */ + +describe("BOOMR_check_doc_domain", function() { + var assert = chai.assert; + + it("Should have a function called \"BOOMR_check_doc_domain\" in global scope", function() { + assert.isFunction(window.BOOMR_check_doc_domain); + }); + + it("Should return undefined when run from the \"main window\"", function() { + assert.isUndefined(window.BOOMR_check_doc_domain()); + }); + + it("Should return undefined when passing a single word domain ie. localhost", function() { + assert.isUndefined(window.BOOMR_check_doc_domain("localhost")); + }); +}); diff --git a/tests/unit/04-plugins-auto-xhr.js b/tests/unit/04-plugins-auto-xhr.js new file mode 100644 index 000000000..ca3d7d288 --- /dev/null +++ b/tests/unit/04-plugins-auto-xhr.js @@ -0,0 +1,76 @@ +/* eslint-env mocha */ +/* global chai */ + +describe("BOOMR.plugins.AutoXHR", function() { + var assert = chai.assert; + + beforeEach(function() { + if (!BOOMR.plugins.AutoXHR) { + return this.skip(); + } + }); + + describe("exports", function() { + it("Should have a AutoXHR object", function() { + assert.isObject(BOOMR.plugins.AutoXHR); + }); + + it("Should have a is_complete() function", function() { + assert.isFunction(BOOMR.plugins.AutoXHR.is_complete); + }); + }); + + describe("matchesAlwaysSendXhr()", function() { + it("Should return false if not given a URL", function() { + assert.equal(BOOMR.plugins.AutoXHR.matchesAlwaysSendXhr(), false); + assert.equal(BOOMR.plugins.AutoXHR.matchesAlwaysSendXhr(null), false); + assert.equal(BOOMR.plugins.AutoXHR.matchesAlwaysSendXhr(false), false); + assert.equal(BOOMR.plugins.AutoXHR.matchesAlwaysSendXhr(undefined), false); + }); + + it("Should return false if configuration is not specified", function() { + assert.equal(BOOMR.plugins.AutoXHR.matchesAlwaysSendXhr("http://"), false); + assert.equal(BOOMR.plugins.AutoXHR.matchesAlwaysSendXhr("http://", null), false); + assert.equal(BOOMR.plugins.AutoXHR.matchesAlwaysSendXhr("http://", false), false); + assert.equal(BOOMR.plugins.AutoXHR.matchesAlwaysSendXhr("http://", undefined), false); + }); + + it("Should return true if configuration is set to true", function() { + assert.equal(BOOMR.plugins.AutoXHR.matchesAlwaysSendXhr("http://", true), true); + }); + + it("Should return true if configuration is a function that returns true", function() { + assert.equal(BOOMR.plugins.AutoXHR.matchesAlwaysSendXhr("http://", function() { + return true; + }), true); + }); + + it("Should return false if configuration is a function that returns false", function() { + assert.equal(BOOMR.plugins.AutoXHR.matchesAlwaysSendXhr("http://", function() { + return false; + }), false); + }); + + it("Should return false if configuration is a function that returns undefined", function() { + assert.equal(BOOMR.plugins.AutoXHR.matchesAlwaysSendXhr("http://", function() { + return; + }), false); + }); + + it("Should return true if configuration is a list of strings where one matches", function() { + assert.equal(BOOMR.plugins.AutoXHR.matchesAlwaysSendXhr("http://", ["a", "http://", "b"]), true); + }); + + it("Should return false if configuration is a list of strings where none matches", function() { + assert.equal(BOOMR.plugins.AutoXHR.matchesAlwaysSendXhr("http://", ["a", "https://", "b"]), false); + }); + + it("Should return true if configuration is a list of regular expressions where one matches", function() { + assert.equal(BOOMR.plugins.AutoXHR.matchesAlwaysSendXhr("http://", [/a/, /http:\/\//, /b/]), true); + }); + + it("Should return false if configuration is a list of regular expressions where none matches", function() { + assert.equal(BOOMR.plugins.AutoXHR.matchesAlwaysSendXhr("http://", [/a/, /https:\/\//, /b/]), false); + }); + }); +}); diff --git a/tests/unit/04-plugins-compression.js b/tests/unit/04-plugins-compression.js new file mode 100644 index 000000000..3bbbb6b61 --- /dev/null +++ b/tests/unit/04-plugins-compression.js @@ -0,0 +1,138 @@ +/* eslint-env mocha */ +/* global chai */ + +describe("BOOMR.utils.Compression", function() { + var assert = chai.assert; + + beforeEach(function() { + if (!BOOMR.utils || !BOOMR.utils.Compression) { + return this.skip(); + } + }); + + describe("BOOMR.utils", function() { + it("Should have a Compression object", function() { + assert.isObject(BOOMR.utils.Compression); + }); + }); + + describe("jsUrl()", function() { + // + // Tests adapted from + // https://github.com/Sage/jsurl + // + function t(value, result) { + // ensure the value is converted properly + var valueJsUrl = BOOMR.utils.Compression.jsUrl(value); + + assert.strictEqual(valueJsUrl, result, "stringify " + (typeof value !== "object" ? value : JSON.stringify(value))); + + // round-trip + assert.strictEqual(BOOMR.utils.Compression.jsUrl(BOOMR.utils.Compression.jsUrlDecompress(valueJsUrl)), result, "roundtrip " + (typeof value !== "object" ? value : JSON.stringify(value))); + } + + it("Should convert undefined", function() { + // undefined + t(undefined, undefined); + }); + + it("Should convert a function", function() { + // a function + t(function() { + foo(); + }, undefined); + }); + + it("Should convert null", function() { + // null + t(null, "~null"); + }); + + it("Should convert true/false", function() { + // true/false + t(false, "~false"); + t(true, "~true"); + }); + + it("Should convert 0", function() { + // numbers + t(0, "~0"); + }); + + it("Should convert 1", function() { + t(1, "~1"); + }); + + it("Should convert -1.5", function() { + t(-1.5, "~-1.5"); + }); + + it("Should convert unicode", function() { + // a string with unicode characters + t("hello world\u203c", "~'hello*20world**203c"); + }); + + it("Should convert characters that need to be escaped", function() { + // a string with many different characters that need to be escaped + t(" !\"#$%&'()*+,-./09:;<=>?@AZ[\\]^_`az{|}~", "~'*20*21*22*23!*25*26*27*28*29*2a*2b*2c-.*2f09*3a*3b*3c*3d*3e*3f*40AZ*5b*5c*5d*5e_*60az*7b*7c*7d*7e"); + }); + + it("Should convert NaN, Infinity, etc", function() { + // JSON.stringify converts special numeric values to null + t(NaN, "~null"); + t(Infinity, "~null"); + t(-Infinity, "~null"); + }); + + it("Should convert a simple array", function() { + // empty array + t([], "~(~)"); + }); + + it("Should convert a complex array", function() { + // an array of different values + t([ + function() { + foo(); + }, + null, + false, + 0, + "hello world\u203c" + ], "~(~null~null~false~0~'hello*20world**203c)"); + }); + + it("Should convert an empty object", function() { + // empty object + t({}, "~()"); + }); + + it("Should convert a complex object", function() { + // an object with different properties + t({ + a: undefined, + b: function() { + foo(); + }, + c: null, + d: false, + e: 0, + f: "hello world\u203c" + }, "~(c~null~d~false~e~0~f~'hello*20world**203c)"); + }); + + it("Should convert an object with different types of properties", function() { + t({ + a: [ + [1, 2], + [], {}], + b: [], + c: { + d: "hello", + e: {}, + f: [] + } + }, "~(a~(~(~1~2)~(~)~())~b~(~)~c~(d~'hello~e~()~f~(~)))"); + }); + }); +}); diff --git a/tests/unit/04-plugins-continuity.js b/tests/unit/04-plugins-continuity.js new file mode 100644 index 000000000..d9a0741d5 --- /dev/null +++ b/tests/unit/04-plugins-continuity.js @@ -0,0 +1,1825 @@ +/* eslint-env mocha */ +/* global chai */ + +describe("BOOMR.plugins.Continuity", function() { + var assert = chai.assert; + + beforeEach(function() { + if (!BOOMR.plugins.Continuity) { + return this.skip(); + } + }); + + describe("exports", function() { + it("Should have a Continuity object", function() { + assert.isObject(BOOMR.plugins.Continuity); + }); + + it("Should have a is_complete() function", function() { + assert.isFunction(BOOMR.plugins.Continuity.is_complete); + }); + }); + + describe("compressBucketLog()", function() { + describe("Mode 0 (small numbers)", function() { + it("Should return an empty string when not given valid input ()", function() { + assert.equal("", BOOMR.plugins.Continuity.compressBucketLog(0, false), 0, 0); + }); + + it("Should return an empty string when given an empty object", function() { + assert.equal("", BOOMR.plugins.Continuity.compressBucketLog(0, false, {}, 0, 0)); + }); + + it("Should return an empty string when given an empty array", function() { + assert.equal("", BOOMR.plugins.Continuity.compressBucketLog(0, false, [], 0, 0)); + }); + + it("Should return an empty string for a single value 0", function() { + assert.equal("", BOOMR.plugins.Continuity.compressBucketLog(0, false, { + 0: 0 + }, 0, 0)); + }); + + it("Should return a single character for a single value 1", function() { + assert.equal("01", BOOMR.plugins.Continuity.compressBucketLog(0, false, { + 0: 1 + }, 0, 0)); + }); + + it("Should return a single character for a single value 10", function() { + assert.equal("0a", BOOMR.plugins.Continuity.compressBucketLog(0, false, { + 0: 10 + }, 0, 0)); + }); + + it("Should return a single character for a single value 36", function() { + assert.equal("0A", BOOMR.plugins.Continuity.compressBucketLog(0, false, { + 0: 36 + }, 0, 0)); + }); + + it("Should return a single character for a single value 62", function() { + assert.equal("0-", BOOMR.plugins.Continuity.compressBucketLog(0, false, { + 0: 62 + }, 0, 0)); + }); + + it("Should return a single character for a single value 63", function() { + assert.equal("0_", BOOMR.plugins.Continuity.compressBucketLog(0, false, { + 0: 63 + }, 0, 0)); + }); + + it("Should return a single character for a single value 64", function() { + assert.equal("0.1s.", BOOMR.plugins.Continuity.compressBucketLog(0, false, { + 0: 64 + }, 0, 0)); + }); + + it("Should return a single character for a single value 100", function() { + assert.equal("0.2s.", BOOMR.plugins.Continuity.compressBucketLog(0, false, { + 0: 100 + }, 0, 0)); + }); + + it("Should return multiple values > 63", function() { + assert.equal("0.2s..5k..8c.", BOOMR.plugins.Continuity.compressBucketLog(0, false, { + 0: 100, + 1: 200, + 2: 300 + }, 0, 2)); + }); + + it("Should return a list for two consecutive values", function() { + assert.equal("012", BOOMR.plugins.Continuity.compressBucketLog(0, false, { + 0: 1, + 1: 2 + }, 0, 1)); + }); + + it("Should return a list for 10 consecutive values", function() { + assert.equal("0123456789a", BOOMR.plugins.Continuity.compressBucketLog(0, false, { + 0: 1, + 1: 2, + 2: 3, + 3: 4, + 4: 5, + 5: 6, + 6: 7, + 7: 8, + 8: 9, + 9: 10 + }, 0, 9)); + }); + + it("Should return a list with holes filled in for missing values", function() { + assert.equal("01002", BOOMR.plugins.Continuity.compressBucketLog(0, false, { + 0: 1, + 3: 2 + }, 0, 3)); + }); + + it("Should return a list with repeating numbers 1", function() { + assert.equal("00*4*12", BOOMR.plugins.Continuity.compressBucketLog(0, false, { + 0: 0, + 1: 1, + 2: 1, + 3: 1, + 4: 1, + 5: 2 + }, 0, 5)); + }); + + it("Should return a list with repeating numbers 2", function() { + assert.equal("00*5*1", BOOMR.plugins.Continuity.compressBucketLog(0, false, { + 0: 0, + 1: 1, + 2: 1, + 3: 1, + 4: 1, + 5: 1 + }, 0, 5)); + }); + + it("Should return a list with repeating numbers 3", function() { + assert.equal("0*4*1*4*2", BOOMR.plugins.Continuity.compressBucketLog(0, false, { + 0: 1, + 1: 1, + 2: 1, + 3: 1, + 4: 2, + 5: 2, + 6: 2, + 7: 2 + }, 0, 7)); + }); + + it("Should return a list with repeating numbers 4", function() { + assert.equal("0*8*1", BOOMR.plugins.Continuity.compressBucketLog(0, false, { + 0: 1, + 1: 1, + 2: 1, + 3: 1, + 4: 1, + 5: 1, + 6: 1, + 7: 1 + }, 0, 7)); + }); + + it("Should return a list with repeating numbers 5", function() { + assert.equal("0*8*a", BOOMR.plugins.Continuity.compressBucketLog(0, false, { + 0: 10, + 1: 10, + 2: 10, + 3: 10, + 4: 10, + 5: 10, + 6: 10, + 7: 10 + }, 0, 7)); + }); + + it("Should return a list with repeating numbers 6", function() { + assert.equal("0*8*.2s.", BOOMR.plugins.Continuity.compressBucketLog(0, false, { + 0: 100, + 1: 100, + 2: 100, + 3: 100, + 4: 100, + 5: 100, + 6: 100, + 7: 100 + }, 0, 7)); + }); + + it("Should return a list with repeating numbers 7", function() { + assert.equal("0*4*.2s.*4*1", BOOMR.plugins.Continuity.compressBucketLog(0, false, { + 0: 100, + 1: 100, + 2: 100, + 3: 100, + 4: 1, + 5: 1, + 6: 1, + 7: 1 + }, 0, 7)); + }); + + it("Should return a list when the first index is missing", function() { + assert.equal("0012", BOOMR.plugins.Continuity.compressBucketLog(0, false, { + 1: 1, + 2: 2 + }, 0, 2)); + }); + + it("Should return a list when the first 2 indicies are missing", function() { + assert.equal("00023", BOOMR.plugins.Continuity.compressBucketLog(0, false, { + 2: 2, + 3: 3 + }, 0, 3)); + }); + + it("Should return a list when the first 3 indicies are missing", function() { + assert.equal("000034", BOOMR.plugins.Continuity.compressBucketLog(0, false, { + 3: 3, + 4: 4 + }, 0, 4)); + }); + + it("Should return a list when the first 4 indicies are missing", function() { + assert.equal("0*4*045", BOOMR.plugins.Continuity.compressBucketLog(0, false, { + 4: 4, + 5: 5 + }, 0, 5)); + }); + + it("Should return a list when the last index is missing", function() { + assert.equal("001", BOOMR.plugins.Continuity.compressBucketLog(0, false, { + 0: 0, + 1: 1 + }, 0, 2)); + }); + + it("Should return a list when the last index is missing and it's a large number", function() { + assert.equal("001", BOOMR.plugins.Continuity.compressBucketLog(0, false, { + 0: 0, + 1: 1 + }, 0, 100)); + }); + + it("Should return a list when the last 2 indicies are missing", function() { + assert.equal("0010", BOOMR.plugins.Continuity.compressBucketLog(0, false, { + 0: 0, + 1: 1 + }, 0, 3)); + }); + + it("Should return a list when the last 3 indicies are missing", function() { + assert.equal("00100", BOOMR.plugins.Continuity.compressBucketLog(0, false, { + 0: 0, + 1: 1 + }, 0, 4)); + }); + + it("Should return a list when the last 4 indicies are missing", function() { + assert.equal("001", BOOMR.plugins.Continuity.compressBucketLog(0, false, { + 0: 0, + 1: 1 + }, 0, 5)); + }); + + it("Should return a list when there are missing indicies in the middle", function() { + assert.equal("01*9*01", BOOMR.plugins.Continuity.compressBucketLog(0, false, { + 0: 1, + 10: 1 + }, 0, 10)); + }); + + it("Should return a list when there are missing indicies everywhere", function() { + assert.equal("0*a*01*9*01", BOOMR.plugins.Continuity.compressBucketLog(0, false, { + 10: 1, + 20: 1 + }, 0, 30)); + }); + + it("Should return a list when there are missing indicies at the end, and they should be left off", function() { + assert.equal("011", BOOMR.plugins.Continuity.compressBucketLog(0, false, { + 0: 1, + 1: 1 + }, 0, 9999)); + }); + }); + + describe("Mode 0 (small numbers) (backfill)", function() { + it("Should return a single character for a single value 10", function() { + assert.equal("0a", BOOMR.plugins.Continuity.compressBucketLog(0, true, { + 0: 10 + }, 0, 0)); + }); + + it("Should return a single character for a single value 36", function() { + assert.equal("0A", BOOMR.plugins.Continuity.compressBucketLog(0, true, { + 0: 36 + }, 0, 0)); + }); + + it("Should return a single character for a single value 62", function() { + assert.equal("0-", BOOMR.plugins.Continuity.compressBucketLog(0, true, { + 0: 62 + }, 0, 0)); + }); + + it("Should return a single character for a single value 63", function() { + assert.equal("0_", BOOMR.plugins.Continuity.compressBucketLog(0, true, { + 0: 63 + }, 0, 0)); + }); + + it("Should return a single character for a single value 64", function() { + assert.equal("0.1s.", BOOMR.plugins.Continuity.compressBucketLog(0, true, { + 0: 64 + }, 0, 0)); + }); + + it("Should return a single character for a single value 100", function() { + assert.equal("0.2s.", BOOMR.plugins.Continuity.compressBucketLog(0, true, { + 0: 100 + }, 0, 0)); + }); + + it("Should return multiple values > 63", function() { + assert.equal("0.2s..5k..8c.", BOOMR.plugins.Continuity.compressBucketLog(0, true, { + 0: 100, + 1: 200, + 2: 300 + }, 0, 2)); + }); + + it("Should return a list for two consecutive values", function() { + assert.equal("012", BOOMR.plugins.Continuity.compressBucketLog(0, true, { + 0: 1, + 1: 2 + }, 0, 1)); + }); + + it("Should return a list for 10 consecutive values", function() { + assert.equal("0123456789a", BOOMR.plugins.Continuity.compressBucketLog(0, true, { + 0: 1, + 1: 2, + 2: 3, + 3: 4, + 4: 5, + 5: 6, + 6: 7, + 7: 8, + 8: 9, + 9: 10 + }, 0, 9)); + }); + + it("Should return a list with backfill for missing values", function() { + assert.equal("01112", BOOMR.plugins.Continuity.compressBucketLog(0, true, { + 0: 1, + 3: 2 + }, 0, 3)); + }); + + it("Should return a list with repeating numbers 1", function() { + assert.equal("00*4*12", BOOMR.plugins.Continuity.compressBucketLog(0, true, { + 0: 0, + 1: 1, + 2: 1, + 3: 1, + 4: 1, + 5: 2 + }, 0, 5)); + }); + + it("Should return a list with repeating numbers 2", function() { + assert.equal("00*5*1", BOOMR.plugins.Continuity.compressBucketLog(0, true, { + 0: 0, + 1: 1, + 2: 1, + 3: 1, + 4: 1, + 5: 1 + }, 0, 5)); + }); + + it("Should return a list with repeating numbers 3", function() { + assert.equal("0*4*1*4*2", BOOMR.plugins.Continuity.compressBucketLog(0, true, { + 0: 1, + 1: 1, + 2: 1, + 3: 1, + 4: 2, + 5: 2, + 6: 2, + 7: 2 + }, 0, 7)); + }); + + it("Should return a list with repeating numbers 4", function() { + assert.equal("0*8*1", BOOMR.plugins.Continuity.compressBucketLog(0, true, { + 0: 1, + 1: 1, + 2: 1, + 3: 1, + 4: 1, + 5: 1, + 6: 1, + 7: 1 + }, 0, 7)); + }); + + it("Should return a list with repeating numbers 5", function() { + assert.equal("0*8*a", BOOMR.plugins.Continuity.compressBucketLog(0, true, { + 0: 10, + 1: 10, + 2: 10, + 3: 10, + 4: 10, + 5: 10, + 6: 10, + 7: 10 + }, 0, 7)); + }); + + it("Should return a list with repeating numbers 6", function() { + assert.equal("0*8*.2s.", BOOMR.plugins.Continuity.compressBucketLog(0, true, { + 0: 100, + 1: 100, + 2: 100, + 3: 100, + 4: 100, + 5: 100, + 6: 100, + 7: 100 + }, 0, 7)); + }); + + it("Should return a list with repeating numbers 7", function() { + assert.equal("0*4*.2s.*4*1", BOOMR.plugins.Continuity.compressBucketLog(0, true, { + 0: 100, + 1: 100, + 2: 100, + 3: 100, + 4: 1, + 5: 1, + 6: 1, + 7: 1 + }, 0, 7)); + }); + + it("Should return a list when the first index is missing", function() { + assert.equal("0012", BOOMR.plugins.Continuity.compressBucketLog(0, true, { + 1: 1, + 2: 2 + }, 0, 2)); + }); + + it("Should return a list when the first 2 indicies are missing", function() { + assert.equal("00023", BOOMR.plugins.Continuity.compressBucketLog(0, true, { + 2: 2, + 3: 3 + }, 0, 3)); + }); + + it("Should return a list when the first 3 indicies are missing", function() { + assert.equal("000034", BOOMR.plugins.Continuity.compressBucketLog(0, true, { + 3: 3, + 4: 4 + }, 0, 4)); + }); + + it("Should return a list when the first 4 indicies are missing", function() { + assert.equal("0*4*045", BOOMR.plugins.Continuity.compressBucketLog(0, true, { + 4: 4, + 5: 5 + }, 0, 5)); + }); + + it("Should return a list when the last index is missing", function() { + assert.equal("0011", BOOMR.plugins.Continuity.compressBucketLog(0, true, { + 0: 0, + 1: 1 + }, 0, 2)); + }); + + it("Should return a list when the last index is missing and it's a large number", function() { + assert.equal("00*2s*1", BOOMR.plugins.Continuity.compressBucketLog(0, true, { + 0: 0, + 1: 1 + }, 0, 100)); + }); + + it("Should return a list when the last 2 indicies are missing", function() { + assert.equal("00111", BOOMR.plugins.Continuity.compressBucketLog(0, true, { + 0: 0, + 1: 1 + }, 0, 3)); + }); + + it("Should return a list when the last 3 indicies are missing", function() { + assert.equal("00*4*1", BOOMR.plugins.Continuity.compressBucketLog(0, true, { + 0: 0, + 1: 1 + }, 0, 4)); + }); + + it("Should return a list when the last 4 indicies are missing", function() { + assert.equal("00*5*1", BOOMR.plugins.Continuity.compressBucketLog(0, true, { + 0: 0, + 1: 1 + }, 0, 5)); + }); + + it("Should return a list when there are missing indicies in the middle", function() { + assert.equal("0*b*1", BOOMR.plugins.Continuity.compressBucketLog(0, true, { + 0: 1, + 10: 1 + }, 0, 10)); + }); + + it("Should return a list when there are missing indicies everywhere", function() { + assert.equal("0*a*0*l*1", BOOMR.plugins.Continuity.compressBucketLog(0, true, { + 10: 1, + 20: 1 + }, 0, 30)); + }); + + it("Should return a list when there are missing indicies at the end", function() { + assert.equal("0*7ps*1", BOOMR.plugins.Continuity.compressBucketLog(0, true, { + 0: 1, + 1: 1 + }, 0, 9999)); + }); + }); + + describe("Mode 1 (large numbers)", function() { + it("Should return an empty string when not given valid input ()", function() { + assert.equal("", BOOMR.plugins.Continuity.compressBucketLog(1, false), 0, 0); + }); + + it("Should return an empty string when given an empty object", function() { + assert.equal("", BOOMR.plugins.Continuity.compressBucketLog(1, false, {}, 0, 0)); + }); + + it("Should return an empty string when given an empty array", function() { + assert.equal("", BOOMR.plugins.Continuity.compressBucketLog(1, false, [], 0, 0)); + }); + + it("Should return an empty string for a single value 0", function() { + assert.equal("", BOOMR.plugins.Continuity.compressBucketLog(1, false, { + 0: 0 + }, 0, 0)); + }); + + it("Should return a single character for a single value 1", function() { + assert.equal("11", BOOMR.plugins.Continuity.compressBucketLog(1, false, { + 0: 1 + }, 0, 0)); + }); + + it("Should return a single character for a single value 10", function() { + assert.equal("1a", BOOMR.plugins.Continuity.compressBucketLog(1, false, { + 0: 10 + }, 0, 0)); + }); + + it("Should return a single character for a single value 36", function() { + assert.equal("110", BOOMR.plugins.Continuity.compressBucketLog(1, false, { + 0: 36 + }, 0, 0)); + }); + + it("Should return a single character for a single value 62", function() { + assert.equal("11q", BOOMR.plugins.Continuity.compressBucketLog(1, false, { + 0: 62 + }, 0, 0)); + }); + + it("Should return a single character for a single value 63", function() { + assert.equal("11r", BOOMR.plugins.Continuity.compressBucketLog(1, false, { + 0: 63 + }, 0, 0)); + }); + + it("Should return a single character for a single value 64", function() { + assert.equal("11s", BOOMR.plugins.Continuity.compressBucketLog(1, false, { + 0: 64 + }, 0, 0)); + }); + + it("Should return a single character for a single value 100", function() { + assert.equal("12s", BOOMR.plugins.Continuity.compressBucketLog(1, false, { + 0: 100 + }, 0, 0)); + }); + + it("Should return multiple values > 63", function() { + assert.equal("12s,5k,8c", BOOMR.plugins.Continuity.compressBucketLog(1, false, { + 0: 100, + 1: 200, + 2: 300 + }, 0, 2)); + }); + + it("Should return a list for two consecutive values", function() { + assert.equal("11,2", BOOMR.plugins.Continuity.compressBucketLog(1, false, { + 0: 1, + 1: 2 + }, 0, 1)); + }); + + it("Should return a list for 10 consecutive values", function() { + assert.equal("11,2,3,4,5,6,7,8,9,a", BOOMR.plugins.Continuity.compressBucketLog(1, false, { + 0: 1, + 1: 2, + 2: 3, + 3: 4, + 4: 5, + 5: 6, + 6: 7, + 7: 8, + 8: 9, + 9: 10 + }, 0, 9)); + }); + + it("Should return a list with holes filled in for missing values", function() { + assert.equal("11,0,0,2", BOOMR.plugins.Continuity.compressBucketLog(1, false, { + 0: 1, + 3: 2 + }, 0, 3)); + }); + + it("Should return a list with repeating numbers 1", function() { + assert.equal("10,*4*1,2", BOOMR.plugins.Continuity.compressBucketLog(1, false, { + 0: 0, + 1: 1, + 2: 1, + 3: 1, + 4: 1, + 5: 2 + }, 0, 5)); + }); + + it("Should return a list with repeating numbers 2", function() { + assert.equal("10,*5*1", BOOMR.plugins.Continuity.compressBucketLog(1, false, { + 0: 0, + 1: 1, + 2: 1, + 3: 1, + 4: 1, + 5: 1 + }, 0, 5)); + }); + + it("Should return a list with repeating numbers 3", function() { + assert.equal("1*4*1,*4*2", BOOMR.plugins.Continuity.compressBucketLog(1, false, { + 0: 1, + 1: 1, + 2: 1, + 3: 1, + 4: 2, + 5: 2, + 6: 2, + 7: 2 + }, 0, 7)); + }); + + it("Should return a list with repeating numbers 4", function() { + assert.equal("1*8*1", BOOMR.plugins.Continuity.compressBucketLog(1, false, { + 0: 1, + 1: 1, + 2: 1, + 3: 1, + 4: 1, + 5: 1, + 6: 1, + 7: 1 + }, 0, 7)); + }); + + it("Should return a list with repeating numbers 5", function() { + assert.equal("1*8*a", BOOMR.plugins.Continuity.compressBucketLog(1, false, { + 0: 10, + 1: 10, + 2: 10, + 3: 10, + 4: 10, + 5: 10, + 6: 10, + 7: 10 + }, 0, 7)); + }); + + it("Should return a list with repeating numbers 6", function() { + assert.equal("1*8*2s", BOOMR.plugins.Continuity.compressBucketLog(1, false, { + 0: 100, + 1: 100, + 2: 100, + 3: 100, + 4: 100, + 5: 100, + 6: 100, + 7: 100 + }, 0, 7)); + }); + + it("Should return a list with repeating numbers 7", function() { + assert.equal("1*4*2s,*4*1", BOOMR.plugins.Continuity.compressBucketLog(1, false, { + 0: 100, + 1: 100, + 2: 100, + 3: 100, + 4: 1, + 5: 1, + 6: 1, + 7: 1 + }, 0, 7)); + }); + + it("Should return a list when the first index is missing", function() { + assert.equal("10,1,2", BOOMR.plugins.Continuity.compressBucketLog(1, false, { + 1: 1, + 2: 2 + }, 0, 2)); + }); + + it("Should return a list when the first 2 indicies are missing", function() { + assert.equal("10,0,2,3", BOOMR.plugins.Continuity.compressBucketLog(1, false, { + 2: 2, + 3: 3 + }, 0, 3)); + }); + + it("Should return a list when the first 3 indicies are missing", function() { + assert.equal("10,0,0,3,4", BOOMR.plugins.Continuity.compressBucketLog(1, false, { + 3: 3, + 4: 4 + }, 0, 4)); + }); + + it("Should return a list when the first 4 indicies are missing", function() { + assert.equal("1*4*0,4,5", BOOMR.plugins.Continuity.compressBucketLog(1, false, { + 4: 4, + 5: 5 + }, 0, 5)); + }); + + it("Should return a list when the last index is missing", function() { + assert.equal("10,1", BOOMR.plugins.Continuity.compressBucketLog(1, false, { + 0: 0, + 1: 1 + }, 0, 2)); + }); + + it("Should return a list when the last index is missing and it's a large number", function() { + assert.equal("10,1", BOOMR.plugins.Continuity.compressBucketLog(1, false, { + 0: 0, + 1: 1 + }, 0, 100)); + }); + + it("Should return a list when the last 2 indicies are missing", function() { + assert.equal("10,1,0", BOOMR.plugins.Continuity.compressBucketLog(1, false, { + 0: 0, + 1: 1 + }, 0, 3)); + }); + + it("Should return a list when the last 3 indicies are missing", function() { + assert.equal("10,1,0,0", BOOMR.plugins.Continuity.compressBucketLog(1, false, { + 0: 0, + 1: 1 + }, 0, 4)); + }); + + it("Should return a list when the last 4 indicies are missing", function() { + assert.equal("10,1", BOOMR.plugins.Continuity.compressBucketLog(1, false, { + 0: 0, + 1: 1 + }, 0, 5)); + }); + + it("Should return a list when there are missing indicies in the middle", function() { + assert.equal("11,*9*0,1", BOOMR.plugins.Continuity.compressBucketLog(1, false, { + 0: 1, + 10: 1 + }, 0, 10)); + }); + + it("Should return a list when there are missing indicies everywhere", function() { + assert.equal("1*a*0,1,*9*0,1", BOOMR.plugins.Continuity.compressBucketLog(1, false, { + 10: 1, + 20: 1 + }, 0, 30)); + }); + + it("Should return a list when there are missing indicies at the end, and they should be left off", function() { + assert.equal("11,1", BOOMR.plugins.Continuity.compressBucketLog(1, false, { + 0: 1, + 1: 1 + }, 0, 9999)); + }); + }); + + describe("Mode 2 (percentage)", function() { + it("Should return an empty string when not given valid input ()", function() { + assert.equal("", BOOMR.plugins.Continuity.compressBucketLog(2, false), 0, 0); + }); + + it("Should return an empty string when given an empty object", function() { + assert.equal("", BOOMR.plugins.Continuity.compressBucketLog(2, false, {}, 0, 0)); + }); + + it("Should return an empty string when given an empty array", function() { + assert.equal("", BOOMR.plugins.Continuity.compressBucketLog(2, false, [], 0, 0)); + }); + + it("Should return an empty string for a single value 0", function() { + assert.equal("", BOOMR.plugins.Continuity.compressBucketLog(2, false, { + 0: 0 + }, 0, 0)); + }); + + it("Should return a single number for a single value 1", function() { + assert.equal("201", BOOMR.plugins.Continuity.compressBucketLog(2, false, { + 0: 1 + }, 0, 0)); + }); + + it("Should return a single number for a single value 10", function() { + assert.equal("210", BOOMR.plugins.Continuity.compressBucketLog(2, false, { + 0: 10 + }, 0, 0)); + }); + + it("Should return a single number for a single value 36", function() { + assert.equal("236", BOOMR.plugins.Continuity.compressBucketLog(2, false, { + 0: 36 + }, 0, 0)); + }); + + it("Should return a single number for a single value 62", function() { + assert.equal("262", BOOMR.plugins.Continuity.compressBucketLog(2, false, { + 0: 62 + }, 0, 0)); + }); + + it("Should return a single number for a single value 63", function() { + assert.equal("263", BOOMR.plugins.Continuity.compressBucketLog(2, false, { + 0: 63 + }, 0, 0)); + }); + + it("Should return a single number for a single value 64", function() { + assert.equal("264", BOOMR.plugins.Continuity.compressBucketLog(2, false, { + 0: 64 + }, 0, 0)); + }); + + it("Should return a single number for a single value 100", function() { + assert.equal("2__", BOOMR.plugins.Continuity.compressBucketLog(2, false, { + 0: 100 + }, 0, 0)); + }); + + it("Should return a single number for a single value 101", function() { + assert.equal("2__", BOOMR.plugins.Continuity.compressBucketLog(2, false, { + 0: 101 + }, 0, 0)); + }); + + it("Should return a single number for a single value 9999999", function() { + assert.equal("2__", BOOMR.plugins.Continuity.compressBucketLog(2, false, { + 0: 9999999 + }, 0, 0)); + }); + + it("Should return a single number for a single value -1", function() { + assert.equal("200", BOOMR.plugins.Continuity.compressBucketLog(2, false, { + 0: -1 + }, 0, 0)); + }); + + it("Should return multiple values > 63", function() { + assert.equal("2______", BOOMR.plugins.Continuity.compressBucketLog(2, false, { + 0: 100, + 1: 200, + 2: 300 + }, 0, 2)); + }); + + it("Should return a list for two consecutive values", function() { + assert.equal("20102", BOOMR.plugins.Continuity.compressBucketLog(2, false, { + 0: 1, + 1: 2 + }, 0, 1)); + }); + + it("Should return a list for 10 consecutive values", function() { + assert.equal("201020304050607080910", BOOMR.plugins.Continuity.compressBucketLog(2, false, { + 0: 1, + 1: 2, + 2: 3, + 3: 4, + 4: 5, + 5: 6, + 6: 7, + 7: 8, + 8: 9, + 9: 10 + }, 0, 9)); + }); + + it("Should return a list with holes filled in for missing values", function() { + assert.equal("201000002", BOOMR.plugins.Continuity.compressBucketLog(2, false, { + 0: 1, + 3: 2 + }, 0, 3)); + }); + + it("Should return a list with repeating numbers 1", function() { + assert.equal("200*4*0102", BOOMR.plugins.Continuity.compressBucketLog(2, false, { + 0: 0, + 1: 1, + 2: 1, + 3: 1, + 4: 1, + 5: 2 + }, 0, 5)); + }); + + it("Should return a list with repeating numbers 2", function() { + assert.equal("200*5*01", BOOMR.plugins.Continuity.compressBucketLog(2, false, { + 0: 0, + 1: 1, + 2: 1, + 3: 1, + 4: 1, + 5: 1 + }, 0, 5)); + }); + + it("Should return a list with repeating numbers 3", function() { + assert.equal("2*4*01*4*02", BOOMR.plugins.Continuity.compressBucketLog(2, false, { + 0: 1, + 1: 1, + 2: 1, + 3: 1, + 4: 2, + 5: 2, + 6: 2, + 7: 2 + }, 0, 7)); + }); + + it("Should return a list with repeating numbers 4", function() { + assert.equal("2*8*01", BOOMR.plugins.Continuity.compressBucketLog(2, false, { + 0: 1, + 1: 1, + 2: 1, + 3: 1, + 4: 1, + 5: 1, + 6: 1, + 7: 1 + }, 0, 7)); + }); + + it("Should return a list with repeating numbers 5", function() { + assert.equal("2*8*10", BOOMR.plugins.Continuity.compressBucketLog(2, false, { + 0: 10, + 1: 10, + 2: 10, + 3: 10, + 4: 10, + 5: 10, + 6: 10, + 7: 10 + }, 0, 7)); + }); + + it("Should return a list with repeating numbers 6", function() { + assert.equal("2*8*__", BOOMR.plugins.Continuity.compressBucketLog(2, false, { + 0: 100, + 1: 100, + 2: 100, + 3: 100, + 4: 100, + 5: 100, + 6: 100, + 7: 100 + }, 0, 7)); + }); + + it("Should return a list with repeating numbers 7", function() { + assert.equal("2*4*__*4*01", BOOMR.plugins.Continuity.compressBucketLog(2, false, { + 0: 100, + 1: 100, + 2: 100, + 3: 100, + 4: 1, + 5: 1, + 6: 1, + 7: 1 + }, 0, 7)); + }); + + it("Should return a list when the first index is missing", function() { + assert.equal("2000102", BOOMR.plugins.Continuity.compressBucketLog(2, false, { + 1: 1, + 2: 2 + }, 0, 2)); + }); + + it("Should return a list when the first 2 indicies are missing", function() { + assert.equal("200000203", BOOMR.plugins.Continuity.compressBucketLog(2, false, { + 2: 2, + 3: 3 + }, 0, 3)); + }); + + it("Should return a list when the first 3 indicies are missing", function() { + assert.equal("20000000304", BOOMR.plugins.Continuity.compressBucketLog(2, false, { + 3: 3, + 4: 4 + }, 0, 4)); + }); + + it("Should return a list when the first 4 indicies are missing", function() { + assert.equal("2*4*000405", BOOMR.plugins.Continuity.compressBucketLog(2, false, { + 4: 4, + 5: 5 + }, 0, 5)); + }); + + it("Should return a list when the last index is missing", function() { + assert.equal("20001", BOOMR.plugins.Continuity.compressBucketLog(2, false, { + 0: 0, + 1: 1 + }, 0, 2)); + }); + + it("Should return a list when the last index is missing and it's a large number", function() { + assert.equal("20001", BOOMR.plugins.Continuity.compressBucketLog(2, false, { + 0: 0, + 1: 1 + }, 0, 100)); + }); + + it("Should return a list when the last 2 indicies are missing", function() { + assert.equal("2000100", BOOMR.plugins.Continuity.compressBucketLog(2, false, { + 0: 0, + 1: 1 + }, 0, 3)); + }); + + it("Should return a list when the last 3 indicies are missing", function() { + assert.equal("200010000", BOOMR.plugins.Continuity.compressBucketLog(2, false, { + 0: 0, + 1: 1 + }, 0, 4)); + }); + + it("Should return a list when the last 4 indicies are missing", function() { + assert.equal("20001", BOOMR.plugins.Continuity.compressBucketLog(2, false, { + 0: 0, + 1: 1 + }, 0, 5)); + }); + + it("Should return a list when there are missing indicies in the middle", function() { + assert.equal("201*9*0001", BOOMR.plugins.Continuity.compressBucketLog(2, false, { + 0: 1, + 10: 1 + }, 0, 10)); + }); + + it("Should return a list when there are missing indicies everywhere", function() { + assert.equal("2*a*0001*9*0001", BOOMR.plugins.Continuity.compressBucketLog(2, false, { + 10: 1, + 20: 1 + }, 0, 30)); + }); + + it("Should return a list when there are missing indicies at the end, and they should be left off", function() { + assert.equal("20101", BOOMR.plugins.Continuity.compressBucketLog(2, false, { + 0: 1, + 1: 1 + }, 0, 9999)); + }); + }); + }); + + describe("decompressBucketLogNumber()", function() { + it("Should return 0 if given a bad input", function() { + assert.equal(0, BOOMR.plugins.Continuity.decompressBucketLogNumber()); + assert.equal(0, BOOMR.plugins.Continuity.decompressBucketLogNumber("")); + assert.equal(0, BOOMR.plugins.Continuity.decompressBucketLogNumber({})); + assert.equal(0, BOOMR.plugins.Continuity.decompressBucketLogNumber([])); + assert.equal(0, BOOMR.plugins.Continuity.decompressBucketLogNumber()); + }); + + it("Should return 0 - 9 if given '0' through '9'", function() { + assert.equal(0, BOOMR.plugins.Continuity.decompressBucketLogNumber("0")); + assert.equal(1, BOOMR.plugins.Continuity.decompressBucketLogNumber("1")); + assert.equal(2, BOOMR.plugins.Continuity.decompressBucketLogNumber("2")); + assert.equal(3, BOOMR.plugins.Continuity.decompressBucketLogNumber("3")); + assert.equal(4, BOOMR.plugins.Continuity.decompressBucketLogNumber("4")); + assert.equal(5, BOOMR.plugins.Continuity.decompressBucketLogNumber("5")); + assert.equal(6, BOOMR.plugins.Continuity.decompressBucketLogNumber("6")); + assert.equal(7, BOOMR.plugins.Continuity.decompressBucketLogNumber("7")); + assert.equal(8, BOOMR.plugins.Continuity.decompressBucketLogNumber("8")); + assert.equal(9, BOOMR.plugins.Continuity.decompressBucketLogNumber("9")); + }); + + it("Should return 10 - 36 if given 'a' through 'z'", function() { + assert.equal(10, BOOMR.plugins.Continuity.decompressBucketLogNumber("a")); + assert.equal(11, BOOMR.plugins.Continuity.decompressBucketLogNumber("b")); + assert.equal(35, BOOMR.plugins.Continuity.decompressBucketLogNumber("z")); + }); + + it("Should return 37 - 61 if given 'A' through 'Z'", function() { + assert.equal(36, BOOMR.plugins.Continuity.decompressBucketLogNumber("A")); + assert.equal(37, BOOMR.plugins.Continuity.decompressBucketLogNumber("B")); + assert.equal(61, BOOMR.plugins.Continuity.decompressBucketLogNumber("Z")); + }); + + it("Should return 62 if given '_'", function() { + assert.equal(62, BOOMR.plugins.Continuity.decompressBucketLogNumber("_")); + }); + + it("Should return 63 if given '-'", function() { + assert.equal(63, BOOMR.plugins.Continuity.decompressBucketLogNumber("-")); + }); + }); + + describe("decompressLog()", function() { + it("Should return a single event", function() { + assert.deepEqual([{ + type: 1, + time: 2, + x: "3", + y: "4" + }], BOOMR.plugins.Continuity.decompressLog("12,x3,y4")); + }); + + it("Should return multiple events", function() { + assert.deepEqual([{ + type: 1, + time: 2 + }, { + type: 3, + time: 10 + }], BOOMR.plugins.Continuity.decompressLog("12|3a")); + }); + }); + + describe("decompressBucketLog()", function() { + describe("Mode 0 (small numbers)", function() { + it("Should return a single bucket", function() { + var out = [1]; + + assert.deepEqual(out, BOOMR.plugins.Continuity.decompressBucketLog("01", 0)); + }); + + it("Should return two buckets", function() { + var out = [1, 2]; + + assert.deepEqual(out, BOOMR.plugins.Continuity.decompressBucketLog("012", 0)); + }); + + it("Should return 3 buckets", function() { + var out = [1, 2, 3]; + + assert.deepEqual(out, BOOMR.plugins.Continuity.decompressBucketLog("0123", 0)); + }); + + it("Should return buckets with repeating zeros", function() { + var out = [1, 2, 0, 0, 0, 0, 9]; + + assert.deepEqual(out, BOOMR.plugins.Continuity.decompressBucketLog("012*4*09", 0)); + }); + + it("Should return buckets with repeating small number", function() { + var out = [1, 2, 8, 8, 8, 8, 9]; + + assert.deepEqual(out, BOOMR.plugins.Continuity.decompressBucketLog("012*4*89", 0)); + }); + + it("Should return buckets with repeating large number", function() { + var out = [1, 2, 370, 370, 370, 370, 9]; + + assert.deepEqual(out, BOOMR.plugins.Continuity.decompressBucketLog("012*4*.aa.9", 0)); + }); + + it("Should return buckets with a large number", function() { + var out = [370]; + + assert.deepEqual(out, BOOMR.plugins.Continuity.decompressBucketLog("0.aa.", 0)); + }); + + it("Should return buckets with two large numbers", function() { + var out = [370, 10]; + + assert.deepEqual(out, BOOMR.plugins.Continuity.decompressBucketLog("0.aa..a.", 0)); + }); + + it("Should return buckets with a large repeat of a large number", function() { + var out = [10, 10, 10, 10, 10, 10, 10, 10, 10, 10]; + + assert.deepEqual(out, BOOMR.plugins.Continuity.decompressBucketLog("0*a*.a.", 0)); + }); + + it("Should return buckets with a large repeat of a large number and a following number", function() { + var out = [10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 1]; + + assert.deepEqual(out, BOOMR.plugins.Continuity.decompressBucketLog("0*a*.a.1", 0)); + }); + + it("Should return buckets with two repeats", function() { + var out = [1, 1, 1, 1, 1, 2, 2, 2, 2, 2]; + + assert.deepEqual(out, BOOMR.plugins.Continuity.decompressBucketLog("0*5*1*5*2", 0)); + }); + + it("Should return a complex bucket", function() { + var out = [5, 3, 3, 5, 5, 5, 5, 10, 0, 0]; + + assert.deepEqual(out, BOOMR.plugins.Continuity.decompressBucketLog("0*1*5*2*3*4*5*1*.a.*2*0", 0)); + }); + }); + + describe("Mode 1 (large numbers)", function() { + it("Should return a single bucket", function() { + var out = [1]; + + assert.deepEqual(out, BOOMR.plugins.Continuity.decompressBucketLog("11", 0)); + }); + + it("Should return two buckets", function() { + var out = [1, 2]; + + assert.deepEqual(out, BOOMR.plugins.Continuity.decompressBucketLog("11,2", 0)); + }); + + it("Should return 3 buckets", function() { + var out = [1, 2, 3]; + + assert.deepEqual(out, BOOMR.plugins.Continuity.decompressBucketLog("11,2,3", 0)); + }); + + it("Should return buckets with repeating zeros", function() { + var out = [1, 2, 0, 0, 0, 0, 9]; + + assert.deepEqual(out, BOOMR.plugins.Continuity.decompressBucketLog("11,2,*4*0,9", 0)); + }); + + it("Should return buckets with repeating small number", function() { + var out = [1, 2, 8, 8, 8, 8, 9]; + + assert.deepEqual(out, BOOMR.plugins.Continuity.decompressBucketLog("11,2,*4*8,9", 0)); + }); + + it("Should return buckets with repeating large number", function() { + var out = [1, 2, 370, 370, 370, 370, 9]; + + assert.deepEqual(out, BOOMR.plugins.Continuity.decompressBucketLog("11,2,*4*aa,9", 0)); + }); + + it("Should return buckets with a large number", function() { + var out = [370]; + + assert.deepEqual(out, BOOMR.plugins.Continuity.decompressBucketLog("1aa", 0)); + }); + + it("Should return buckets with two large numbers", function() { + var out = [370, 10]; + + assert.deepEqual(out, BOOMR.plugins.Continuity.decompressBucketLog("1aa,a", 0)); + }); + + it("Should return buckets with a large repeat of a large number", function() { + var out = [10, 10, 10, 10, 10, 10, 10, 10, 10, 10]; + + assert.deepEqual(out, BOOMR.plugins.Continuity.decompressBucketLog("1*a*a", 0)); + }); + + it("Should return buckets with a large repeat of a large number and a following number", function() { + var out = [10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 1]; + + assert.deepEqual(out, BOOMR.plugins.Continuity.decompressBucketLog("1*a*a,1", 0)); + }); + + it("Should return buckets with two repeats", function() { + var out = [1, 1, 1, 1, 1, 2, 2, 2, 2, 2]; + + assert.deepEqual(out, BOOMR.plugins.Continuity.decompressBucketLog("1*5*1,*5*2", 0)); + }); + + it("Should return a complex bucket", function() { + var out = [5, 3, 3, 5, 5, 5, 5, 10, 0, 0]; + + assert.deepEqual(out, BOOMR.plugins.Continuity.decompressBucketLog("1*1*5,*2*3,*4*5,*1*a,*2*0", 0)); + }); + }); + + describe("Mode 2 (percentage)", function() { + it("Should return a single bucket", function() { + var out = [1]; + + assert.deepEqual(out, BOOMR.plugins.Continuity.decompressBucketLog("201", 0)); + }); + + it("Should return two buckets", function() { + var out = [1, 2]; + + assert.deepEqual(out, BOOMR.plugins.Continuity.decompressBucketLog("20102", 0)); + }); + + it("Should return 3 buckets", function() { + var out = [1, 2, 3]; + + assert.deepEqual(out, BOOMR.plugins.Continuity.decompressBucketLog("2010203", 0)); + }); + + it("Should return buckets with repeating zeros", function() { + var out = [1, 2, 0, 0, 0, 0, 9]; + + assert.deepEqual(out, BOOMR.plugins.Continuity.decompressBucketLog("20102*4*0009", 0)); + }); + + it("Should return buckets with repeating small number", function() { + var out = [1, 2, 8, 8, 8, 8, 9]; + + assert.deepEqual(out, BOOMR.plugins.Continuity.decompressBucketLog("20102*4*0809", 0)); + }); + + it("Should return buckets with repeating large number", function() { + var out = [1, 2, 100, 100, 100, 100, 9]; + + assert.deepEqual(out, BOOMR.plugins.Continuity.decompressBucketLog("20102*4*__09", 0)); + }); + + it("Should return buckets with a large number", function() { + var out = [100]; + + assert.deepEqual(out, BOOMR.plugins.Continuity.decompressBucketLog("2__", 0)); + }); + + it("Should return buckets with two large numbers", function() { + var out = [100, 10]; + + assert.deepEqual(out, BOOMR.plugins.Continuity.decompressBucketLog("2__10", 0)); + }); + + it("Should return buckets with a large repeat of a large number", function() { + var out = [10, 10, 10, 10, 10, 10, 10, 10, 10, 10]; + + assert.deepEqual(out, BOOMR.plugins.Continuity.decompressBucketLog("2*a*10", 0)); + }); + + it("Should return buckets with a large repeat of a large number and a following number", function() { + var out = [10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 1]; + + assert.deepEqual(out, BOOMR.plugins.Continuity.decompressBucketLog("2*a*1001", 0)); + }); + + it("Should return buckets with two repeats", function() { + var out = [1, 1, 1, 1, 1, 2, 2, 2, 2, 2]; + + assert.deepEqual(out, BOOMR.plugins.Continuity.decompressBucketLog("2*5*01*5*02", 0)); + }); + + it("Should return a complex bucket", function() { + var out = [5, 3, 3, 5, 5, 5, 5, 10, 0, 0]; + + assert.deepEqual(out, BOOMR.plugins.Continuity.decompressBucketLog("2*1*05*2*03*4*05*1*10*2*00", 0)); + }); + }); + }); + + describe("determineTti()", function() { + // + // Minimal / Small bucket calculations with no data + // + describe("Calculations without data {}", function() { + it("Should return no TTI if there are no buckets to analyze", function() { + assert.deepEqual( + { tti: 0, idleIntervals: 0, lastBucketVisited: 0 }, + BOOMR.plugins.Continuity.determineTti(100, 200, 0, -1, 0, {})); + }); + + it("Should return no TTI if there was only a single bucket to analyze", function() { + assert.deepEqual( + { tti: 0, idleIntervals: 1, lastBucketVisited: 0 }, + BOOMR.plugins.Continuity.determineTti(100, 200, 0, 0, 0, {})); + }); + + it("Should return no TTI if there were only 4 buckets to analyze", function() { + assert.deepEqual( + { tti: 0, idleIntervals: 4, lastBucketVisited: 3 }, + BOOMR.plugins.Continuity.determineTti(100, 200, 0, 3, 0, {})); + }); + + it("Should return a TTI at start if there were 5 idle buckets", function() { + assert.deepEqual( + { tti: 100, idleIntervals: 5, lastBucketVisited: 4 }, + BOOMR.plugins.Continuity.determineTti(100, 100, 0, 4, 0, {})); + }); + + it("Should return a TTI at TTVR if there were 5 idle buckets and TTVR was higher than the start", function() { + assert.deepEqual( + { tti: 200, idleIntervals: 5, lastBucketVisited: 4 }, + BOOMR.plugins.Continuity.determineTti(100, 200, 0, 4, 0, {})); + }); + }); + + // + // With Long Tasks + // + describe("Calculations with LongTask data", function() { + it("Should return a TTI at start time if there is no LongTasks", function() { + assert.deepEqual( + { tti: 100, idleIntervals: 5, lastBucketVisited: 4 }, + BOOMR.plugins.Continuity.determineTti(100, 100, 0, 10, 0, { + longtask: [0, 0, 0, 0, 0, 0] + })); + }); + + it("Should return a TTI 100ms after start time if the first bucket is busy", function() { + assert.deepEqual( + { tti: 200, idleIntervals: 5, lastBucketVisited: 5 }, + BOOMR.plugins.Continuity.determineTti(100, 100, 0, 10, 0, { + longtask: [1] + })); + }); + + it("Should return a TTI 200ms after start time if the second bucket is busy", function() { + assert.deepEqual( + { tti: 300, idleIntervals: 5, lastBucketVisited: 6 }, + BOOMR.plugins.Continuity.determineTti(100, 100, 0, 10, 0, { + longtask: [0, 1] + })); + }); + + it("Should return a TTI 500ms after start time if the fifth bucket is busy", function() { + assert.deepEqual( + { tti: 600, idleIntervals: 5, lastBucketVisited: 9 }, + BOOMR.plugins.Continuity.determineTti(100, 100, 0, 10, 0, { + longtask: [0, 0, 0, 0, 1] + })); + }); + + it("Should return a TTI 1000ms after start time if the fifth and tenth buckets were busy", function() { + assert.deepEqual( + { tti: 1100, idleIntervals: 5, lastBucketVisited: 14 }, + BOOMR.plugins.Continuity.determineTti(100, 100, 0, 20, 0, { + longtask: [0, 0, 0, 0, 1, 0, 0, 0, 0, 1] + })); + }); + }); + + // + // With FPS + // + describe("Calculations with FPS data", function() { + it("Should return a TTI at start time if FPS was at 60 for the entire interval", function() { + assert.deepEqual( + { tti: 100, idleIntervals: 5, lastBucketVisited: 4 }, + BOOMR.plugins.Continuity.determineTti(100, 100, 0, 10, 0, { + fps: [6, 6, 6, 6, 6, 6] + })); + }); + + // + // 0 FPS + // + it("Should return a TTI 100ms after start time if the first bucket had 0 FPS", function() { + assert.deepEqual( + { tti: 200, idleIntervals: 5, lastBucketVisited: 5 }, + BOOMR.plugins.Continuity.determineTti(100, 100, 0, 10, 0, { + fps: [0, 6, 6, 6, 6, 6] + })); + }); + + it("Should return a TTI 200ms after start time if the second bucket had 0 FPS", function() { + assert.deepEqual( + { tti: 300, idleIntervals: 5, lastBucketVisited: 6 }, + BOOMR.plugins.Continuity.determineTti(100, 100, 0, 10, 0, { + fps: [6, 0, 6, 6, 6, 6, 6] + })); + }); + + it("Should return a TTI 500ms after start time if the fifth bucket had 0 FPS", function() { + assert.deepEqual( + { tti: 600, idleIntervals: 5, lastBucketVisited: 9 }, + BOOMR.plugins.Continuity.determineTti(100, 100, 0, 10, 0, { + fps: [6, 6, 6, 6, 0, 6, 6, 6, 6, 6] + })); + }); + + it("Should return a TTI 1000ms after start time if the fifth and tenth buckets had 0 FPS", function() { + assert.deepEqual( + { tti: 1100, idleIntervals: 5, lastBucketVisited: 14 }, + BOOMR.plugins.Continuity.determineTti(100, 100, 0, 20, 0, { + fps: [6, 6, 6, 6, 0, 6, 6, 6, 6, 0, 6, 6, 6, 6, 6] + })); + }); + + // + // 2 FPS min + // + it("Should return a TTI 100ms after start time if the first bucket had 10 FPS and the rest were 20 FPS", function() { + assert.deepEqual( + { tti: 200, idleIntervals: 5, lastBucketVisited: 5 }, + BOOMR.plugins.Continuity.determineTti(100, 100, 0, 10, 0, { + fps: [1, 2, 2, 2, 2, 2] + })); + }); + + it("Should return a TTI 200ms after start time if the second bucket had 10 FPS and the rest were 20 FPS", function() { + assert.deepEqual( + { tti: 300, idleIntervals: 5, lastBucketVisited: 6 }, + BOOMR.plugins.Continuity.determineTti(100, 100, 0, 10, 0, { + fps: [2, 1, 2, 2, 2, 2, 2] + })); + }); + + it("Should return a TTI 500ms after start time if the fifth bucket had 10 FPS and the rest were 20 FPS", function() { + assert.deepEqual( + { tti: 600, idleIntervals: 5, lastBucketVisited: 9 }, + BOOMR.plugins.Continuity.determineTti(100, 100, 0, 10, 0, { + fps: [2, 2, 2, 2, 1, 2, 2, 2, 2, 2] + })); + }); + + it("Should return a TTI 1000ms after start time if the fifth and tenth buckets had 10 FPS and the rest were 20 FPS", function() { + assert.deepEqual( + { tti: 1100, idleIntervals: 5, lastBucketVisited: 14 }, + BOOMR.plugins.Continuity.determineTti(100, 100, 0, 20, 0, { + fps: [2, 2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2] + })); + }); + }); + + // + // With Busy Data + // + describe("Calculations with Page Busy data", function() { + it("Should return a TTI at start time if there is no Page Busy", function() { + assert.deepEqual( + { tti: 100, idleIntervals: 5, lastBucketVisited: 4 }, + BOOMR.plugins.Continuity.determineTti(100, 100, 0, 10, 0, { + busy: [0, 0, 0, 0, 0, 0] + })); + }); + + it("Should return a TTI of 0 if the first bucket is busy and there aren't any following buckets filled in yet", function() { + assert.deepEqual( + { tti: 0, idleIntervals: 0, lastBucketVisited: 0 }, + BOOMR.plugins.Continuity.determineTti(100, 100, 0, 10, 0, { + busy: [51] + })); + }); + + it("Should return a TTI 100ms after start time if the first bucket is busy", function() { + assert.deepEqual( + { tti: 200, idleIntervals: 5, lastBucketVisited: 5 }, + BOOMR.plugins.Continuity.determineTti(100, 100, 0, 10, 0, { + busy: [51, 0, 0, 0, 0, 0] + })); + }); + + it("Should return a TTI of 0 if the first bucket is busy and there aren't any following buckets filled in yet", function() { + assert.deepEqual( + { tti: 0, idleIntervals: 0, lastBucketVisited: 1 }, + BOOMR.plugins.Continuity.determineTti(100, 100, 0, 10, 0, { + busy: [0, 51] + })); + }); + + it("Should return a TTI 200ms after start time if the second bucket is busy", function() { + assert.deepEqual( + { tti: 300, idleIntervals: 5, lastBucketVisited: 6 }, + BOOMR.plugins.Continuity.determineTti(100, 100, 0, 10, 0, { + busy: [0, 51, 0, 0, 0, 0, 0] + })); + }); + + it("Should return a TTI of 0 if the fifth bucket is busy and there aren't any following buckets filled in yet", function() { + assert.deepEqual( + { tti: 0, idleIntervals: 0, lastBucketVisited: 4 }, + BOOMR.plugins.Continuity.determineTti(100, 100, 0, 10, 0, { + busy: [0, 0, 0, 0, 51] + })); + }); + + it("Should return a TTI 500ms after start time if the fifth bucket is busy", function() { + assert.deepEqual( + { tti: 600, idleIntervals: 5, lastBucketVisited: 9 }, + BOOMR.plugins.Continuity.determineTti(100, 100, 0, 10, 0, { + busy: [0, 0, 0, 0, 51, 0, 0, 0, 0, 0] + })); + }); + + it("Should return a TTI 0 if the fifth bucket and tenth buckets are busy and there aren't any following buckets filled in yet", function() { + assert.deepEqual( + { tti: 0, idleIntervals: 0, lastBucketVisited: 9 }, + BOOMR.plugins.Continuity.determineTti(100, 100, 0, 20, 0, { + busy: [0, 0, 0, 0, 51, 0, 0, 0, 0, 51] + })); + }); + + it("Should return a TTI 1000ms after start time if the fifth and tenth buckets were busy", function() { + assert.deepEqual( + { tti: 1100, idleIntervals: 5, lastBucketVisited: 14 }, + BOOMR.plugins.Continuity.determineTti(100, 100, 0, 20, 0, { + busy: [0, 0, 0, 0, 51, 0, 0, 0, 0, 51, 0, 0, 0, 0, 0] + })); + }); + + it("Should return no TTI if the Page Busy data hasn't been filled in for the period yet", function() { + assert.deepEqual( + { tti: 0, idleIntervals: 4, lastBucketVisited: 3 }, + BOOMR.plugins.Continuity.determineTti(100, 100, 0, 20, 0, { + busy: [0, 0, 0, 0] + })); + }); + + it("Should have a TTI of 100ms if Page Busy data is later filled in", function() { + var results = BOOMR.plugins.Continuity.determineTti(100, 100, 0, 20, 0, { + busy: [0, 0, 0, 0] + }); + + assert.deepEqual( + { tti: 0, idleIntervals: 4, lastBucketVisited: 3 }, + results); + + results = BOOMR.plugins.Continuity.determineTti(100, 100, results.lastBucketVisited + 1, 20, results.idleIntervals, { + busy: [0, 0, 0, 0, 0] + }); + + assert.deepEqual( + { tti: 100, idleIntervals: 5, lastBucketVisited: 4 }, + results); + }); + }); + + // + // With Delayed Interactions + // + describe("Calculations with Delayed Interaction data", function() { + it("Should return a TTI at start time if there is no delayed interaction", function() { + assert.deepEqual( + { tti: 100, idleIntervals: 5, lastBucketVisited: 4 }, + BOOMR.plugins.Continuity.determineTti(100, 100, 0, 10, 0, { + interdly: [0, 0, 0, 0, 0, 0] + })); + }); + + it("Should return a TTI 100ms after start time if the first bucket had a delayed interaction", function() { + assert.deepEqual( + { tti: 200, idleIntervals: 5, lastBucketVisited: 5 }, + BOOMR.plugins.Continuity.determineTti(100, 100, 0, 10, 0, { + interdly: [1] + })); + }); + + it("Should return a TTI 200ms after start time if the second bucket had a delayed interaction", function() { + assert.deepEqual( + { tti: 300, idleIntervals: 5, lastBucketVisited: 6 }, + BOOMR.plugins.Continuity.determineTti(100, 100, 0, 10, 0, { + interdly: [0, 1] + })); + }); + + it("Should return a TTI 500ms after start time if the fifth bucket had a delayed interaction", function() { + assert.deepEqual( + { tti: 600, idleIntervals: 5, lastBucketVisited: 9 }, + BOOMR.plugins.Continuity.determineTti(100, 100, 0, 10, 0, { + interdly: [0, 0, 0, 0, 1] + })); + }); + + it("Should return a TTI 1000ms after start time if the fifth and tenth buckets had delayed interactions", function() { + assert.deepEqual( + { tti: 1100, idleIntervals: 5, lastBucketVisited: 14 }, + BOOMR.plugins.Continuity.determineTti(100, 100, 0, 20, 0, { + interdly: [0, 0, 0, 0, 1, 0, 0, 0, 0, 1] + })); + }); + }); + + // + // With previous Idle Intervals + // + describe("Calculations with previous Idle Intervals calculated", function() { + it("Should utilize startBucket and idleIntervals from a previous calculation where idle is at bucket 0", function() { + // call it once with two idle buckets + var results = BOOMR.plugins.Continuity.determineTti(100, 100, 0, 1, 0, { + longtask: [0, 0] + }); + + assert.deepEqual( + { tti: 0, idleIntervals: 2, lastBucketVisited: 1 }, + results); + + // call again with 2 more idle buckets + results = BOOMR.plugins.Continuity.determineTti(100, 100, results.lastBucketVisited + 1, 3, results.idleIntervals, { + longtask: [0, 0, 0, 0] + }); + + assert.deepEqual( + { tti: 0, idleIntervals: 4, lastBucketVisited: 3 }, + results); + + // call again with 6 more idle buckets + results = BOOMR.plugins.Continuity.determineTti(100, 100, results.lastBucketVisited + 1, 9, results.idleIntervals, { + longtask: [0, 0, 0, 0, 0, 0] + }); + + assert.deepEqual( + { tti: 100, idleIntervals: 5, lastBucketVisited: 4 }, + results); + }); + + it("Should utilize startBucket and idleIntervals from a previous calculation where idle is at bucket 4", function() { + // call it once with two idle buckets + var results = BOOMR.plugins.Continuity.determineTti(100, 100, 0, 1, 0, { + longtask: [0, 0] + }); + + assert.deepEqual( + { tti: 0, idleIntervals: 2, lastBucketVisited: 1 }, + results); + + // call again with 2 more idle buckets + results = BOOMR.plugins.Continuity.determineTti(100, 100, results.lastBucketVisited + 1, 3, results.idleIntervals, { + longtask: [0, 0, 0, 0] + }); + + assert.deepEqual( + { tti: 0, idleIntervals: 4, lastBucketVisited: 3 }, + results); + + // call again with 6 more buckets, with busyness at 5 + results = BOOMR.plugins.Continuity.determineTti(100, 100, results.lastBucketVisited + 1, 9, results.idleIntervals, { + longtask: [0, 0, 0, 0, 1, 0, 0, 0, 0, 0] + }); + + assert.deepEqual( + { tti: 600, idleIntervals: 5, lastBucketVisited: 9 }, + results); + }); + }); + }); + + describe("compressClsScore and decompressClsScore", function() { + it("Properly compressed and decompressed topScore of 0.050", function() { + var topScore = 0.050; + + var newTopScore = BOOMR.plugins.Continuity.compressClsScore(topScore); + + newTopScore = BOOMR.plugins.Continuity.decompressClsScore(newTopScore); + + assert.equal(topScore, newTopScore); + }); + + it("Properly compressed and decompressed topScore of 0.153", function() { + var topScore = 0.153; + + var newTopScore = BOOMR.plugins.Continuity.compressClsScore(topScore); + + newTopScore = BOOMR.plugins.Continuity.decompressClsScore(newTopScore); + + assert.equal(topScore, newTopScore); + }); + }); + + describe("compressClsSources and decompressClsSources", function() { + it("Properly compressed and decompressed clsSources array", function() { + var clsSources = [{ + value: 0.010, + startTime: 2068, + sources: [{ + selector: "img", + previousRect: {x: 1608, y: 202, width: 800, height: 297}, + currentRect: {x: 1608, y: 499, width: 800, height: 297} + }] + }, + { + value: 0.153, + startTime: 3067, + sources: [ + { + selector: "img", + previousRect: {x: 0, y: 120, width: 200, height: 74}, + currentRect: {x: 0, y: 1010, width: 200, height: 75} + }, + { + selector: "img", + previousRect: {x: 0, y: 202, width: 1600, height: 594}, + currentRect: {x: 0, y: 1093, width: 1600, height: 549} + }, + { + selector: "img", + previousRect: {x: 1608, y: 499, width: 800, height: 297}, + currentRect: {x: 1608, y: 1389, width: 800, height: 253} + } + ] + }]; + + var newClsSources = BOOMR.plugins.Continuity.compressClsSources(clsSources); + + newClsSources = BOOMR.plugins.Continuity.decompressClsSources(newClsSources); + + assert.deepEqual(clsSources, newClsSources); + }); + }); +}); diff --git a/tests/unit/04-plugins-errors.js b/tests/unit/04-plugins-errors.js new file mode 100644 index 000000000..c2ecbe1f5 --- /dev/null +++ b/tests/unit/04-plugins-errors.js @@ -0,0 +1,630 @@ +/* eslint-env mocha */ +/* global chai */ + +describe("BOOMR.plugins.Errors", function() { + var assert = chai.assert; + + beforeEach(function() { + if (!BOOMR.plugins.Errors) { + return this.skip(); + } + }); + + describe("exports", function() { + it("Should have a Errors object", function() { + assert.isObject(BOOMR.plugins.Errors); + }); + + it("Should have a is_complete() function", function() { + assert.isFunction(BOOMR.plugins.Errors.is_complete); + }); + + it("Should be complete at this point", function() { + assert.isTrue(BOOMR.plugins.Errors.is_complete()); + }); + }); + + describe("findDuplicateError()", function() { + it("Should return a null when not given an array", function() { + assert.isUndefined(BOOMR.plugins.Errors.findDuplicateError(null, {})); + assert.isUndefined(BOOMR.plugins.Errors.findDuplicateError(undefined, {})); + assert.isUndefined(BOOMR.plugins.Errors.findDuplicateError(false, {})); + assert.isUndefined(BOOMR.plugins.Errors.findDuplicateError(1, {})); + assert.isUndefined(BOOMR.plugins.Errors.findDuplicateError("s", {})); + }); + + it("Should return a null when not given an object", function() { + assert.isUndefined(BOOMR.plugins.Errors.findDuplicateError([])); + assert.isUndefined(BOOMR.plugins.Errors.findDuplicateError([], undefined)); + }); + + it("Should find the same BoomerangError object in the array", function() { + var be = new BOOMR.plugins.Errors.BoomerangError({ + code: 1, + message: "error", + functionName: "foo" + }); + var ary = [be]; + + assert.equal(BOOMR.plugins.Errors.findDuplicateError(ary, be), be); + }); + + it("Should find a BoomerangError object with the same properties", function() { + var be1 = new BOOMR.plugins.Errors.BoomerangError({ + code: 1, + message: "error", + functionName: "foo" + }); + var be2 = new BOOMR.plugins.Errors.BoomerangError({ + code: 1, + message: "error", + functionName: "foo" + }); + var ary = [be1]; + + assert.equal(BOOMR.plugins.Errors.findDuplicateError(ary, be2), be1); + }); + + it("Should not find anything when a BoomerangError objects have different properties", function() { + var be1 = new BOOMR.plugins.Errors.BoomerangError({ + code: 1, + message: "error", + functionName: "foo" + }); + var be2 = new BOOMR.plugins.Errors.BoomerangError({ + code: 1, + message: "error1", + functionName: "foo" + }); + var ary = [be1]; + + assert.isUndefined(BOOMR.plugins.Errors.findDuplicateError(ary, be2)); + }); + }); + + describe("mergeDuplicateErrors()", function() { + it("Should not throw an error when not given an array", function() { + assert.isUndefined(BOOMR.plugins.Errors.mergeDuplicateErrors(null, {})); + assert.isUndefined(BOOMR.plugins.Errors.mergeDuplicateErrors(undefined, {})); + assert.isUndefined(BOOMR.plugins.Errors.mergeDuplicateErrors(false, {})); + assert.isUndefined(BOOMR.plugins.Errors.mergeDuplicateErrors(1, {})); + assert.isUndefined(BOOMR.plugins.Errors.mergeDuplicateErrors("s", {})); + }); + + it("Should not modify the array when not given an object", function() { + var ary = []; + + assert.isUndefined(BOOMR.plugins.Errors.mergeDuplicateErrors(ary)); + assert.isUndefined(BOOMR.plugins.Errors.mergeDuplicateErrors(ary, undefined)); + + assert.deepEqual(ary, []); + }); + + it("Should increment the count of the same BoomerangError object in the array", function() { + var be = new BOOMR.plugins.Errors.BoomerangError({ + code: 1, + message: "error", + functionName: "foo" + }); + var ary = [be]; + + assert.equal(be.count, 1); + + assert.isDefined(BOOMR.plugins.Errors.mergeDuplicateErrors(ary, be, true)); + + assert.equal(be.count, 2); + }); + + it("Should increment the count of a BoomerangError object with the same properties", function() { + var be1 = new BOOMR.plugins.Errors.BoomerangError({ + code: 1, + message: "error", + functionName: "foo" + }); + var be2 = new BOOMR.plugins.Errors.BoomerangError({ + code: 1, + message: "error", + functionName: "foo" + }); + var ary = [be1]; + + assert.equal(be1.count, 1); + + assert.isDefined(BOOMR.plugins.Errors.mergeDuplicateErrors(ary, be2, true)); + + assert.equal(be1.count, 2); + }); + + it("Should not increment the count when a BoomerangError objects ahve different properties", function() { + var be1 = new BOOMR.plugins.Errors.BoomerangError({ + code: 1, + message: "error", + functionName: "foo" + }); + var be2 = new BOOMR.plugins.Errors.BoomerangError({ + code: 1, + message: "error1", + functionName: "foo" + }); + var ary = [be1]; + + assert.equal(be1.count, 1); + + assert.isUndefined(BOOMR.plugins.Errors.mergeDuplicateErrors(ary, be2)); + + assert.equal(be1.count, 1); + assert.equal(be2.count, 1); + }); + }); + + describe("compressErrors()", function() { + it("Should minimize a BoomerangError that has no data", function() { + var err = new BOOMR.plugins.Errors.BoomerangError(); + + var c = BOOMR.plugins.Errors.compressErrors([err])[0]; + + assert.isUndefined(c.n); + assert.isUndefined(c.f); + assert.isUndefined(c.e); + assert.isUndefined(c.s); + assert.isUndefined(c.v); + assert.isUndefined(c.t); + assert.isUndefined(c.m); + assert.isUndefined(c.c); + }); + + it("Should minimize a BoomerangError that has defaults for via, source and type", function() { + var err = new BOOMR.plugins.Errors.BoomerangError({ + via: BOOMR.plugins.Errors.VIA_APP, + source: BOOMR.plugins.Errors.SOURCE_APP, + type: "Error" + }); + + var c = BOOMR.plugins.Errors.compressErrors([err])[0]; + + assert.isUndefined(c.n); + assert.isUndefined(c.f); + assert.isUndefined(c.e); + assert.isUndefined(c.s); + assert.isUndefined(c.v); + assert.isUndefined(c.t); + assert.isUndefined(c.m); + assert.isUndefined(c.c); + }); + + it("Should minimize a BoomerangError that has values for everything", function() { + var now = BOOMR.now(); + + var err = new BOOMR.plugins.Errors.BoomerangError({ + count: 2, + frames: [{ + functionName: "Foo", + lineNumber: 10, + columnNumber: 10, + fileName: "/foo.html" + }], + via: BOOMR.plugins.Errors.VIA_CONSOLE, + source: BOOMR.plugins.Errors.SOURCE_BOOMERANG, + type: "TypeError", + message: "OHNO", + code: 10, + timestamp: now + }); + + var c = BOOMR.plugins.Errors.compressErrors([err])[0]; + + assert.equal(c.n, 2); + + var frame = c.f[0]; + + assert.equal(frame.f, "Foo"); + assert.equal(frame.l, 10); + assert.equal(frame.c, 10); + assert.equal(frame.w, "/foo.html"); + + assert.equal(c.s, BOOMR.plugins.Errors.SOURCE_BOOMERANG); + assert.equal(c.v, BOOMR.plugins.Errors.VIA_CONSOLE); + assert.equal(c.t, "TypeError"); + assert.equal(c.m, "OHNO"); + assert.equal(c.c, 10); + assert.equal(c.d, now.toString(36)); + }); + }); + + describe("decompressErrors()", function() { + it("Should decompress a BoomerangError that has no data", function() { + var err = new BOOMR.plugins.Errors.BoomerangError(); + + var c = BOOMR.plugins.Errors.compressErrors([err])[0]; + var d = BOOMR.plugins.Errors.decompressErrors([c])[0]; + + assert.equal(d.count, 1); + assert.equal(d.events.length, 0); + assert.equal(d.frames.length, 0); + assert.equal(d.source, BOOMR.plugins.Errors.SOURCE_APP); + assert.equal(d.stack, ""); + assert.equal(d.type, "Error"); + assert.equal(d.via, BOOMR.plugins.Errors.VIA_APP); + }); + + it("Should decompress a BoomerangError that has values for everything", function() { + var err = new BOOMR.plugins.Errors.BoomerangError({ + count: 2, + frames: [{ + functionName: "Foo", + lineNumber: 10, + columnNumber: 10, + fileName: "/foo.html" + }], + via: BOOMR.plugins.Errors.VIA_CONSOLE, + source: BOOMR.plugins.Errors.SOURCE_BOOMERANG, + type: "TypeError", + message: "OHNO", + code: 10 + }); + + var c = BOOMR.plugins.Errors.compressErrors([err])[0]; + var d = BOOMR.plugins.Errors.decompressErrors([c])[0]; + + assert.equal(d.count, 2); + assert.equal(d.events.length, 0); + assert.equal(d.frames.length, 1); + assert.equal(d.frames[0].functionName, "Foo"); + assert.equal(d.frames[0].lineNumber, 10); + assert.equal(d.frames[0].columnNumber, 10); + assert.equal(d.frames[0].fileName, "/foo.html"); + assert.equal(d.source, BOOMR.plugins.Errors.SOURCE_BOOMERANG); + assert.include(d.stack, "OHNO"); + assert.include(d.stack, "/foo.html"); + assert.include(d.stack, "Foo"); + assert.include(d.stack, "10:10"); + assert.equal(d.type, "TypeError"); + assert.equal(d.message, "OHNO"); + assert.equal(d.code, 10); + assert.equal(d.via, BOOMR.plugins.Errors.VIA_CONSOLE); + }); + }); + + describe("normalizeToString()", function() { + it("Should return 'undefined' for undefined", function() { + assert.strictEqual(BOOMR.plugins.Errors.normalizeToString(), "undefined"); + assert.strictEqual(BOOMR.plugins.Errors.normalizeToString(undefined), "undefined"); + }); + + it("Should return 'null' for null", function() { + assert.strictEqual(BOOMR.plugins.Errors.normalizeToString(null), "null"); + }); + + it("Should return '(empty string)' for an empty string", function() { + assert.strictEqual(BOOMR.plugins.Errors.normalizeToString(""), "(empty string)"); + }); + + it("Should return the string for a string", function() { + assert.strictEqual(BOOMR.plugins.Errors.normalizeToString("a"), "a"); + assert.strictEqual(BOOMR.plugins.Errors.normalizeToString("abc"), "abc"); + assert.strictEqual(BOOMR.plugins.Errors.normalizeToString("abc123"), "abc123"); + }); + + it("Should return a number for a number", function() { + assert.strictEqual(BOOMR.plugins.Errors.normalizeToString(1), "1"); + assert.strictEqual(BOOMR.plugins.Errors.normalizeToString(1.2), "1.2"); + assert.strictEqual(BOOMR.plugins.Errors.normalizeToString(1000), "1000"); + }); + + it("Should return '0' for 0", function() { + assert.strictEqual(BOOMR.plugins.Errors.normalizeToString(0), "0"); + }); + + it("Should return 'false' for false", function() { + assert.strictEqual(BOOMR.plugins.Errors.normalizeToString(false), "false"); + }); + + it("Should return 'NaN' for NaN", function() { + assert.strictEqual(BOOMR.plugins.Errors.normalizeToString(NaN), "NaN"); + }); + + it("Should return '(function)' for a function", function() { + assert.strictEqual(BOOMR.plugins.Errors.normalizeToString(function(){}), "(function)"); + }); + + it("Should return '' for an empty array", function() { + assert.strictEqual(BOOMR.plugins.Errors.normalizeToString([]), ""); + }); + + it("Should return '1' for an array with one element", function() { + assert.strictEqual(BOOMR.plugins.Errors.normalizeToString([1]), "1"); + }); + + it("Should return '1,2' for a an array with two elements", function() { + assert.strictEqual(BOOMR.plugins.Errors.normalizeToString([1, 2]), "1,2"); + }); + + it("Should return 'a' for a a string array", function() { + assert.strictEqual(BOOMR.plugins.Errors.normalizeToString(["a"]), "a"); + }); + + it("Should return 'a,bc' for a a string array with two elements", function() { + assert.strictEqual(BOOMR.plugins.Errors.normalizeToString(["a", "bc"]), "a,bc"); + }); + }); + + describe("BoomerangError", function() { + it("Should create an empty object when given no properties", function() { + var be = new BOOMR.plugins.Errors.BoomerangError(); + + assert.isUndefined(be.code); + assert.isUndefined(be.message); + assert.isUndefined(be.functionName); + assert.isUndefined(be.fileName); + assert.isUndefined(be.lineNumber); + assert.isUndefined(be.columnNumber); + assert.isUndefined(be.stack); + assert.isUndefined(be.type); + assert.isUndefined(be.via); + + // defaults + assert.strictEqual(be.source, BOOMR.plugins.Errors.SOURCE_APP); + assert.deepEqual(be.events, []); + assert.strictEqual(be.count, 1); + }); + + it("Should have the same properites as given", function() { + var config = { + code: 1, + message: "a", + functionName: "b", + fileName: "c", + lineNumber: 2, + columnNumber: 3, + stack: "d", + type: "Error", + via: 4, + source: 5, + events: [1, 2, 3] + }; + + var be = new BOOMR.plugins.Errors.BoomerangError(config); + + assert.strictEqual(be.code, 1); + assert.strictEqual(be.message, "a"); + assert.strictEqual(be.functionName, "b"); + assert.strictEqual(be.fileName, "c"); + assert.strictEqual(be.lineNumber, 2); + assert.strictEqual(be.columnNumber, 3); + assert.strictEqual(be.stack, "d"); + assert.strictEqual(be.type, "Error"); + assert.strictEqual(be.via, 4); + assert.strictEqual(be.source, 5); + assert.deepEqual(be.events, [1, 2, 3]); + assert.strictEqual(be.count, 1); + }); + + describe("equals()", function() { + it("Should return true for two objects with the same properties", function() { + var config = { + code: 1, + message: "a", + functionName: "b", + fileName: "c", + lineNumber: 2, + columnNumber: 3, + stack: "d", + type: 4, + via: 5, + source: 6, + events: [1, 2, 3] + }; + + var be1 = new BOOMR.plugins.Errors.BoomerangError(config); + var be2 = new BOOMR.plugins.Errors.BoomerangError(config); + + assert.strictEqual(be1.equals(be2), true); + assert.strictEqual(be2.equals(be1), true); + }); + + it("Should return false if one property differs", function() { + var config = { + code: 1, + message: "a", + functionName: "b", + fileName: "c", + lineNumber: 2, + columnNumber: 3, + stack: "d", + type: 4, + via: 5, + source: 6, + events: [1, 2, 3] + }; + + var be1 = new BOOMR.plugins.Errors.BoomerangError(config); + + // change 1 property + config.code = 2; + var be2 = new BOOMR.plugins.Errors.BoomerangError(config); + + assert.strictEqual(be1.equals(be2), false); + assert.strictEqual(be2.equals(be1), false); + }); + + it("Should return true if just the counts differ", function() { + var config = { + code: 1 + }; + + var be1 = new BOOMR.plugins.Errors.BoomerangError(config); + var be2 = new BOOMR.plugins.Errors.BoomerangError(config); + + be1.count = 2; + + assert.strictEqual(be1.equals(be2), true); + assert.strictEqual(be2.equals(be1), true); + }); + + it("Should return true if just the events differ", function() { + var config = { + code: 1, + events: [] + }; + + var be1 = new BOOMR.plugins.Errors.BoomerangError(config); + + config.events = [1]; + var be2 = new BOOMR.plugins.Errors.BoomerangError(config); + + assert.strictEqual(be1.equals(be2), true); + assert.strictEqual(be2.equals(be1), true); + }); + }); + + describe("fromError()", function() { + it("Should return null if not given an error", function() { + assert.strictEqual(BOOMR.plugins.Errors.BoomerangError.fromError(), null); + assert.strictEqual(BOOMR.plugins.Errors.BoomerangError.fromError(null), null); + assert.strictEqual(BOOMR.plugins.Errors.BoomerangError.fromError(undefined), null); + }); + + it("Should return an object if given an Error raised in an Anonymous function", function() { + var err; + var ln = 0; + + try { + throw new Error("Test"); + } + catch (err2) { + err = err2; + + // 6 lines up + ln = err.lineNumber ? (err.lineNumber - 6) : 0; + } + + var be = BOOMR.plugins.Errors.BoomerangError.fromError(err, BOOMR.plugins.Errors.VIA_APP, BOOMR.plugins.Errors.SOURCE_APP); + + // character count may differ amongst browsers + if (be.columnNumber) { + assert.closeTo(be.columnNumber, 15, 15); + } + + assert.strictEqual(be.count, 1); + + if (be.fileName) { + // fails in IE10-11 and non-Chromium Edge due to bug in error plugin `parseV8OrIE` + assert.include(be.fileName, "04-plugins-errors.js"); + } + + // not all browsers will emit functionName + if (be.functionName) { + assert.isTrue(be.functionName.indexOf("Context.") !== -1 || + be.functionName.indexOf("context.") !== -1 || + be.functionName.indexOf("Anonymous") !== -1); + } + + if (ln !== 0 && be.lineNumber) { + assert.closeTo(be.lineNumber, ln, 10); + } + + assert.strictEqual(be.message, "Test"); + assert.strictEqual(be.source, BOOMR.plugins.Errors.SOURCE_APP); + assert.strictEqual(be.type, "Error"); + + if (be.stack) { + assert.include(be.stack, "04-plugins-errors.js"); + assert.include(be.stack, "mocha.js"); + } + }); + + it("Should return an object if given an Error raised in a named function", function namedFunction() { + var err; + var ln = 0; + + try { + throw new Error("Test"); + } + catch (err2) { + err = err2; + + // 6 lines up + ln = err.lineNumber ? (err.lineNumber - 6) : 0; + } + + var be = BOOMR.plugins.Errors.BoomerangError.fromError(err, BOOMR.plugins.Errors.VIA_APP, BOOMR.plugins.Errors.SOURCE_APP); + + // character count may differ amongst browsers + if (be.columnNumber) { + assert.closeTo(be.columnNumber, 15, 15); + } + + assert.strictEqual(be.count, 1); + + if (be.fileName) { + // fails in IE10-11 and non-Chromium Edge, is "http://boomerang-test.local:4002/vendor/mocha/mocha.js" + assert.include(be.fileName, "04-plugins-errors.js"); + } + + // not all browsers will emit functionName + if (be.functionName) { + assert.include(be.functionName, "namedFunction"); + } + + if (ln !== 0 && be.lineNumber) { + assert.closeTo(be.lineNumber, ln, 10); + } + + assert.strictEqual(be.message, "Test"); + assert.strictEqual(be.source, BOOMR.plugins.Errors.SOURCE_APP); + assert.strictEqual(be.type, "Error"); + + if (be.stack) { + assert.include(be.stack, "04-plugins-errors.js"); + assert.include(be.stack, "mocha.js"); + } + }); + + it("Should strip Boomerang functions from an Error", function() { + var stack = "Error: Test\n" + + " at okFunction1 (okFile1:1:1)\n" + // keep + " at okFunction2 (okFile2:2:2)\n" + // keep + " at createStackForSend (boomr:1)\n" + + " at BOOMR.window.console.error (boomr:1)\n" + + " at BOOMR.plugins.Errors.init (a.html:1)\n" + + " at BOOMR.window.onerror (boomr:1)\n" + + " at BOOMR_plugins_errors_console (boomr:1)\n" + + " at okFunction3 (okFile3:3:3)\n" + // keep + " at BOOMR_plugins_errors_console (boomr:1)\n" + + " at okFunction4 (okFile4:4:4)\n" + // keep + " at Object.send (/a/boomerang/b/:1:2)\n" + + " at wrap/< (/a/boomerang/b/:1:2)\n" + + " at Anonymous function (/a/boomerang/b/:1:2)\n" + + " at Object.send (/a/noboomr/b/:1:2)\n" + // keep + " at wrap/< (/a/noboomr/b/:1:2)"; // keep + var err = { + stack: stack + }; + + var parsed = BOOMR.plugins.Errors.BoomerangError.fromError(err); + + // only 4 frames should be left + assert.equal(parsed.frames.length, 6); + + // first 4 matches are all okFunctionN at okFileN:N:n + for (var i = 1; i <= 4; i++) { + assert.equal(parsed.frames[i - 1].functionName, "okFunction" + i); + assert.equal(parsed.frames[i - 1].fileName, "okFile" + i); + assert.equal(parsed.frames[i - 1].lineNumber, i); + assert.equal(parsed.frames[i - 1].columnNumber, i); + } + + // then Object.send and wrap match + assert.equal(parsed.frames[4].functionName, "Object.send"); + assert.equal(parsed.frames[4].fileName, "/a/noboomr/b/"); + assert.equal(parsed.frames[4].lineNumber, 1); + assert.equal(parsed.frames[4].columnNumber, 2); + + assert.equal(parsed.frames[5].functionName, "wrap/<"); + assert.equal(parsed.frames[5].fileName, "/a/noboomr/b/"); + assert.equal(parsed.frames[5].lineNumber, 1); + assert.equal(parsed.frames[5].columnNumber, 2); + }); + }); + }); +}); diff --git a/tests/unit/04-plugins-mq.js b/tests/unit/04-plugins-mq.js new file mode 100644 index 000000000..29402a3d3 --- /dev/null +++ b/tests/unit/04-plugins-mq.js @@ -0,0 +1,178 @@ +/* eslint-env mocha */ +/* global chai */ + +describe("BOOMR.plugins.mq", function() { + var assert = chai.assert; + + beforeEach(function() { + if (!BOOMR.window || !BOOMR.window.BOOMR_mq) { + return this.skip(); + } + }); + + describe("exports", function() { + it("Should have a BOOMR_mq object", function() { + assert.isObject(BOOMR.window.BOOMR_mq); + }); + + it("Should have a push() function", function() { + assert.isFunction(BOOMR.window.BOOMR_mq.push); + }); + }); + + describe("push()", function() { + it("Should handle degenerate cases", function() { + assert.doesNotThrow(function() { + BOOMR.window.BOOMR_mq.push( + null, + undefined, + false, + true, + "null", + "undefined", + "false", + "true", + "", + 0, + 1, + 27, + [], + {}, + ["foo"], + ["foo.bar"], + ["foo.bar.baz"] + ); + }); + }); + + describe("array pattern", function() { + it("Should call methods on BOOMR", function(done) { + BOOMR.method = function() { + done(); + }; + + BOOMR.window.BOOMR_mq.push(["method"]); + }); + + it("Should call namespaced methods on BOOMR", function(done) { + BOOMR.method = function() { + done(); + }; + + BOOMR.window.BOOMR_mq.push(["BOOMR.method"]); + }); + + it("Should pass all arguments", function(done) { + BOOMR.method = function() { + assert.lengthOf(arguments, 3); + assert.equal(arguments[0], 0); + assert.equal(arguments[1], 1); + assert.equal(arguments[2], 2); + done(); + }; + + BOOMR.window.BOOMR_mq.push(["method", 0, 1, 2]); + }); + + it("Should support `push` with multiple arguments", function(done) { + var results = []; + + BOOMR.method1 = function() { + results.push("method1"); + }; + + BOOMR.method2 = function() { + results.push("method2"); + }; + + BOOMR.method3 = function() { + assert.lengthOf(results, 2); + assert.equal(results[0], "method1"); + assert.equal(results[1], "method2"); + done(); + }; + + BOOMR.window.BOOMR_mq.push( + ["method1"], + ["method2"], + ["method3"] + ); + }); + + it("Should step into objects on BOOMR", function(done) { + BOOMR.obj = { + method: function() { + done(); + } + }; + BOOMR.window.BOOMR_mq.push(["obj.method"]); + }); + + it("Should step into functions on BOOMR", function(done) { + BOOMR.func = function() {}; + + BOOMR.func.method = function() { + done(); + }; + + BOOMR.window.BOOMR_mq.push(["func.method"]); + }); + + it("Should use appropriate context", function(done) { + BOOMR.obj = { + method1: function() { + this.method2(); + }, + method2: function() { + done(); + } + }; + BOOMR.window.BOOMR_mq.push(["obj.method1"]); + }); + }); + + describe("object pattern", function() { + it("Should support `arguments`", function(done) { + BOOMR.method = function() { + done(); + }; + + BOOMR.window.BOOMR_mq.push({ + arguments: ["method"] + }); + }); + it("Should support `callback`", function(done) { + BOOMR.method = function() { + return 123; + }; + + BOOMR.window.BOOMR_mq.push({ + arguments: ["method"], + callback: function() { + assert.lengthOf(arguments, 1); + assert.equal(arguments[0], 123); + done(); + } + }); + }); + it("Should support `thisArg`", function(done) { + function Item(value) { + this.value = value; + } + + Item.prototype.callback = function() { + assert.equal(this.value, 123); + done(); + }; + + BOOMR.method = function() {}; + + BOOMR.window.BOOMR_mq.push({ + arguments: ["method"], + callback: Item.prototype.callback, + thisArg: new Item(123) + }); + }); + }); + }); +}); diff --git a/tests/unit/04-plugins-restiming.js b/tests/unit/04-plugins-restiming.js new file mode 100644 index 000000000..4a7732984 --- /dev/null +++ b/tests/unit/04-plugins-restiming.js @@ -0,0 +1,1665 @@ +/* eslint-env mocha */ +/* global chai */ + +describe("BOOMR.plugins.ResourceTiming", function() { + var assert = chai.assert; + + beforeEach(function() { + if (!BOOMR.plugins.ResourceTiming) { + return this.skip(); + } + }); + + describe("exports", function() { + it("Should have a ResourceTiming object", function() { + assert.isObject(BOOMR.plugins.ResourceTiming); + }); + + it("Should have a is_complete() function", function() { + assert.isFunction(BOOMR.plugins.ResourceTiming.is_complete); + }); + + it("Should have a is_supported() function", function() { + assert.isFunction(BOOMR.plugins.ResourceTiming.is_supported); + }); + + it("Should always be complete", function() { + assert.isTrue(BOOMR.plugins.ResourceTiming.is_complete()); + }); + + it("Should always be complete", function() { + BOOMR.plugins.ResourceTiming.init(); + assert.isTrue(BOOMR.plugins.ResourceTiming.is_complete()); + }); + }); + + // + // .toBase36 + // + describe("toBase36()", function() { + it("Should return the base 36 equivalent of 100", function() { + assert.equal(BOOMR.plugins.ResourceTiming.toBase36(100), "2s"); + }); + + it("Should return an empty string if the input is not a number or a number", function() { + assert.equal(BOOMR.plugins.ResourceTiming.toBase36(), ""); + }); + + it("Should return the input string if given a string", function() { + assert.equal(BOOMR.plugins.ResourceTiming.toBase36(""), ""); + assert.equal(BOOMR.plugins.ResourceTiming.toBase36("a"), "a"); + }); + + it("Should return an empty string if the input is 0", function() { + assert.equal(BOOMR.plugins.ResourceTiming.toBase36(0), ""); + }); + }); + + // + // .trimTiming + // + describe("trimTiming()", function() { + it("Should handle 0", function() { + assert.equal(BOOMR.plugins.ResourceTiming.trimTiming(0), 0); + }); + + it("Should handle undefined", function() { + assert.equal(BOOMR.plugins.ResourceTiming.trimTiming(), 0); + }); + + it("Should handle non-numbers", function() { + assert.equal(BOOMR.plugins.ResourceTiming.trimTiming("a"), 0); + }); + + it("Should round to the nearest number", function() { + assert.equal(BOOMR.plugins.ResourceTiming.trimTiming(0, 0), 0); + assert.equal(BOOMR.plugins.ResourceTiming.trimTiming(100, 0), 100); + assert.equal(BOOMR.plugins.ResourceTiming.trimTiming(100.5, 0), 101); + assert.equal(BOOMR.plugins.ResourceTiming.trimTiming(100.01, 0), 100); + assert.equal(BOOMR.plugins.ResourceTiming.trimTiming(100.99, 0), 101); + }); + + it("Should round when given a navtiming offset", function() { + assert.equal(BOOMR.plugins.ResourceTiming.trimTiming(100), 100); + assert.equal(BOOMR.plugins.ResourceTiming.trimTiming(100, 1), 99); + assert.equal(BOOMR.plugins.ResourceTiming.trimTiming(100.12, 1.12), 99); + assert.equal(BOOMR.plugins.ResourceTiming.trimTiming(100, 100), 0); + assert.equal(BOOMR.plugins.ResourceTiming.trimTiming(100, 101), -1); + }); + }); + + // + // .convertToTrie + // + describe("convertToTrie()", function() { + describe("splitAt = every letter", function() { + it("Should convert a single node", function() { + var data = {"abc": "abc"}; + var expected = { + "a": { + "b": { + "c": "abc" + } + } + }; + + assert.deepEqual(BOOMR.plugins.ResourceTiming.convertToTrie(data), expected); + }); + + it("Should convert a two-node tree whose nodes don't intersect", function() { + var data = {"abc": "abc", "xyz": "xyz"}; + var expected = { + "a": { + "b": { + "c": "abc" + } + }, + "x": { + "y": { + "z": "xyz" + } + } + }; + + assert.deepEqual(BOOMR.plugins.ResourceTiming.convertToTrie(data), expected); + }); + + it("Should convert a complex tree", function() { + var data = {"abc": "abc", "abcd": "abcd", "ab": "ab"}; + var expected = { + "a": { + "b": { + "|": "ab", + "c": { + "|": "abc", + "d": "abcd" + } + } + } + }; + + assert.deepEqual(BOOMR.plugins.ResourceTiming.convertToTrie(data), expected); + }); + + it("Should break 'href' for NoScript", function() { + var data = {"href": "abc"}; + var expected = { + "h": { + "\n": { + "r": { + "e": { + "f": "abc" + } + } + } + } + }; + + assert.deepEqual(BOOMR.plugins.ResourceTiming.convertToTrie(data), expected); + }); + + it("Should break 'src' for NoScript", function() { + var data = {"src": "abc"}; + var expected = { + "s": { + "\n": { + "r": { + "c": "abc" + } + } + } + }; + + assert.deepEqual(BOOMR.plugins.ResourceTiming.convertToTrie(data), expected); + }); + + it("Should break 'action' for NoScript", function() { + var data = {"action": "abc"}; + var expected = { + "a": { + "\n": { + "c": { + "t": { + "i": { + "o": { + "n": "abc" + } + } + } + } + } + } + }; + + assert.deepEqual(BOOMR.plugins.ResourceTiming.convertToTrie(data), expected); + }); + + it("Should update XSS words from config", function() { + BOOMR.init({ + ResourceTiming: { + enabled: true, + xssBreakWords: [ + /(h)(ref)/gi, + /(s)(rc)/gi, + /(a)(ction)/gi, + /(m)(oo)/gi + ] + } + }); + + var data = {"moo": "abc"}; + var expected = { + "m": { + "\n": { + "o": { + "o": "abc" + } + } + } + }; + + assert.deepEqual(BOOMR.plugins.ResourceTiming.convertToTrie(data), expected); + }); + }); + + describe("splitAt = path", function() { + it("Should convert a single node", function() { + var data = {"http://abc.com/def/g": "abc"}; + var expected = { + "http://": { + "abc.com/": { + "def/": { + "g": "abc" + } + } + } + }; + + assert.deepEqual(BOOMR.plugins.ResourceTiming.convertToTrie(data, true), expected); + }); + + it("Should convert a single node with no path", function() { + var data = {"http://abc.com": "abc"}; + var expected = { + "http://": { + "abc.com": "abc" + } + }; + + assert.deepEqual(BOOMR.plugins.ResourceTiming.convertToTrie(data, true), expected); + }); + + it("Should convert a two-node tree whose nodes don't intersect", function() { + var data = {"http://abc.com/abc": "abc", "http://abc.com/xyz": "xyz"}; + var expected = { + "http://": { + "abc.com/": { + "abc": "abc", + "xyz": "xyz" + } + } + }; + + assert.deepEqual(BOOMR.plugins.ResourceTiming.convertToTrie(data, true), expected); + }); + + it("Should convert a complex tree", function() { + var data = {"http://abc.com/abc": "abc", "http://abc.com/abcd": "abcd", "http://abc.com/ab": "ab"}; + var expected = { + "http://": { + "abc.com/": { + "ab": "ab", + "abc": "abc", + "abcd": "abcd" + } + } + }; + + assert.deepEqual(BOOMR.plugins.ResourceTiming.convertToTrie(data, true), expected); + }); + + it("Should convert a large tree", function() { + var data = { + "http://abc.com/def/g": "g", + "http://abc.com/def/g/": "g/", + "http://abc.com/def/g/hij": "hij", + "http://abc.com/def/g/klm": "klm", + "http://abc.com/def/g/klm/": "klm/", + "http://abc.com/def/g/klm/nop.html": "nop.html" + }; + + var expected = { + "http://": { + "abc.com/": { + "def/": { + "g": "g", + "g/": { + "": "g/", + "hij": "hij", + "klm": "klm", + "klm/": { + "": "klm/", + "nop.html": "nop.html" + } + } + } + } + } + }; + + assert.deepEqual(BOOMR.plugins.ResourceTiming.convertToTrie(data, true), expected); + }); + + it("Should break 'href' for NoScript", function() { + var data = {"http://abc.com/hrefhref/href": "abc"}; + var expected = { + "http://": { + "abc.com/": { + "h": { + "\n": { + "refh": { + "\n": { + "ref/": { + "h": { + "\n": { + "ref": "abc" + } + } + } + } + } + } + } + } + } + }; + + assert.deepEqual(BOOMR.plugins.ResourceTiming.convertToTrie(data, true), expected); + }); + + it("Should break 'src' for NoScript", function() { + var data = {"http://abc.com/src": "abc"}; + var expected = { + "http://": { + "abc.com/": { + "s": { + "\n": { + "rc": "abc" + } + } + } + } + }; + + assert.deepEqual(BOOMR.plugins.ResourceTiming.convertToTrie(data, true), expected); + }); + + it("Should break 'action' for NoScript", function() { + var data = {"http://abc.com/action": "abc"}; + var expected = { + "http://": { + "abc.com/": { + "a": { + "\n": { + "ction": "abc" + } + } + } + } + }; + + assert.deepEqual(BOOMR.plugins.ResourceTiming.convertToTrie(data, true), expected); + }); + + it("Should update XSS words from config", function() { + BOOMR.init({ + ResourceTiming: { + enabled: true, + xssBreakWords: [ + /(h)(ref)/gi, + /(s)(rc)/gi, + /(a)(ction)/gi, + /(m)(oo)/gi + ] + } + }); + + var data = {"http://abc.com/moo": "abc"}; + var expected = { + "http://": { + "abc.com/": { + "m": { + "\n": { + "oo": "abc" + } + } + } + } + }; + + assert.deepEqual(BOOMR.plugins.ResourceTiming.convertToTrie(data, true), expected); + }); + }); + }); + + // + // .optimizeTrie + // + describe("optimizeTrie()", function() { + describe("splitAt = every letter", function() { + it("Should optimize a single-node tree", function() { + var data = {"abc": "abc"}; + var expected = { + "abc": "abc" + }; + + var trie = BOOMR.plugins.ResourceTiming.convertToTrie(data); + + assert.deepEqual(BOOMR.plugins.ResourceTiming.optimizeTrie(trie, true), expected); + }); + + it("Should optimize a simple tree", function() { + var data = {"abc": "abc", "xyz": "xyz"}; + var expected = { + "abc": "abc", + "xyz": "xyz" + }; + + var trie = BOOMR.plugins.ResourceTiming.convertToTrie(data); + + assert.deepEqual(BOOMR.plugins.ResourceTiming.optimizeTrie(trie, true), expected); + }); + + it("Should optimize a complex tree", function() { + var data = {"abc": "abc", "abcd": "abcd", "ab": "ab"}; + var expected = { + "ab": + { + "|": "ab", + "c": { + "|": "abc", + "d": "abcd" + } + } + }; + + var trie = BOOMR.plugins.ResourceTiming.convertToTrie(data); + + assert.deepEqual(BOOMR.plugins.ResourceTiming.optimizeTrie(trie, true), expected); + }); + + it("Should optimize a single-node tree with more characters", function() { + var data = {"abcde": "abcde"}; + var expected = { + "abcde": "abcde" + }; + + var trie = BOOMR.plugins.ResourceTiming.convertToTrie(data); + + assert.deepEqual(BOOMR.plugins.ResourceTiming.optimizeTrie(trie, true), expected); + }); + + it("Should break 'href' for NoScript", function() { + var data = {"href": "abc" }; + var expected = { + "h": { + "ref": "abc" + } + }; + + var trie = BOOMR.plugins.ResourceTiming.convertToTrie(data); + + var optTrie = BOOMR.plugins.ResourceTiming.optimizeTrie(trie, true); + var optTrieJson = JSON.stringify(optTrie); + + assert.deepEqual(optTrie, expected); + + assert.equal(optTrieJson.indexOf("href"), -1); + }); + + it("Should break 'href', 'action' and 'src' for NoScript", function() { + var data = {"href": "abc", "123action123": "abc", "_src_abc_action123": "abc" }; + var expected = { + "_s": { + "rc_abc_a": { + "ction123": "abc" + } + }, + "123a": { + "ction123": "abc" + }, + "h": { + "ref": "abc" + } + }; + + var trie = BOOMR.plugins.ResourceTiming.convertToTrie(data); + + var optTrie = BOOMR.plugins.ResourceTiming.optimizeTrie(trie, true); + var optTrieJson = JSON.stringify(optTrie); + + assert.deepEqual(optTrie, expected); + + assert.equal(optTrieJson.indexOf("href"), -1); + }); + }); + + describe("splitAt = path", function() { + it("Should optimize a single-node tree", function() { + var data = {"http://abc.com/def/g": "abc"}; + var expected = { + "http://abc.com/def/g": "abc" + }; + + var trie = BOOMR.plugins.ResourceTiming.convertToTrie(data, true); + + assert.deepEqual(BOOMR.plugins.ResourceTiming.optimizeTrie(trie, true), expected); + }); + + it("Should convert a two-node tree whose nodes don't intersect", function() { + var data = {"http://abc.com/abc": "abc", "http://abc.com/xyz": "xyz"}; + var expected = { + "http://abc.com/": { + "abc": "abc", + "xyz": "xyz" + } + }; + + var trie = BOOMR.plugins.ResourceTiming.convertToTrie(data, true); + + assert.deepEqual(BOOMR.plugins.ResourceTiming.optimizeTrie(trie, true), expected); + }); + + it("Should convert a complex tree", function() { + var data = {"http://abc.com/abc": "abc", "http://abc.com/abcd": "abcd", "http://abc.com/ab": "ab"}; + var expected = { + "http://abc.com/": { + "ab": "ab", + "abc": "abc", + "abcd": "abcd" + } + }; + + var trie = BOOMR.plugins.ResourceTiming.convertToTrie(data, true); + + assert.deepEqual(BOOMR.plugins.ResourceTiming.optimizeTrie(trie, true), expected); + }); + + it("Should convert a large tree", function() { + var data = { + "http://abc.com/def/g": "g", + "http://abc.com/def/g/": "g/", + "http://abc.com/def/g/hij": "hij", + "http://abc.com/def/g/klm": "klm", + "http://abc.com/def/g/klm/": "klm/", + "http://abc.com/def/g/klm/nop.html": "nop.html" + }; + + var expected = { + "http://abc.com/def/": { + "g": "g", + "g/": { + "": "g/", + "hij": "hij", + "klm": "klm", + "klm/": { + "": "klm/", + "nop.html": "nop.html" + } + } + } + }; + + var trie = BOOMR.plugins.ResourceTiming.convertToTrie(data, true); + + assert.deepEqual(BOOMR.plugins.ResourceTiming.optimizeTrie(trie, true), expected); + }); + + it("Should break 'href' for NoScript", function() { + var data = {"http://abc.com/hrefhref/href": "abc"}; + var expected = { + "http://abc.com/h": { + "refh": { + "ref/h": { + "ref": "abc" + } + } + } + }; + + var trie = BOOMR.plugins.ResourceTiming.convertToTrie(data, true); + + var optTrie = BOOMR.plugins.ResourceTiming.optimizeTrie(trie, true); + var optTrieJson = JSON.stringify(optTrie); + + assert.deepEqual(optTrie, expected); + + assert.equal(optTrieJson.indexOf("href"), -1); + }); + + it("Should break 'src' for NoScript", function() { + var data = {"http://abc.com/src": "abc"}; + var expected = { + "http://abc.com/s": { + "rc": "abc" + } + }; + + var trie = BOOMR.plugins.ResourceTiming.convertToTrie(data, true); + + var optTrie = BOOMR.plugins.ResourceTiming.optimizeTrie(trie, true); + var optTrieJson = JSON.stringify(optTrie); + + assert.deepEqual(optTrie, expected); + + assert.equal(optTrieJson.indexOf("src"), -1); + }); + + it("Should break 'action' for NoScript", function() { + var data = {"http://abc.com/action": "abc"}; + var expected = { + "http://abc.com/a": { + "ction": "abc" + } + }; + + var trie = BOOMR.plugins.ResourceTiming.convertToTrie(data, true); + + var optTrie = BOOMR.plugins.ResourceTiming.optimizeTrie(trie, true); + var optTrieJson = JSON.stringify(optTrie); + + assert.deepEqual(optTrie, expected); + + assert.equal(optTrieJson.indexOf("action"), -1); + }); + + it("Should break 'href', 'action' and 'src' for NoScript", function() { + var data = { + "http://abc.com/href": "abc", + "http://abc.com/123action123": "abc", + "http://abc.com/_src_abc_action123": "abc" + }; + + var expected = { + "http://abc.com/": { + "_s": { + "rc_abc_a": { + "ction123": "abc" + } + }, + "123a": { + "ction123": "abc" + }, + "h": { + "ref": "abc" + } + } + }; + + var trie = BOOMR.plugins.ResourceTiming.convertToTrie(data, true); + + var optTrie = BOOMR.plugins.ResourceTiming.optimizeTrie(trie, true); + var optTrieJson = JSON.stringify(optTrie); + + assert.deepEqual(optTrie, expected); + + assert.equal(optTrieJson.indexOf("href"), -1); + assert.equal(optTrieJson.indexOf("action"), -1); + assert.equal(optTrieJson.indexOf("src"), -1); + }); + }); + }); + + describe("findPerformanceEntriesForFrame()", function() { + it("Should get the correct entries for this page", function() { + // + // NOTE: If you change the resources for this test suite (index.html), this test will + // need to be updated. + // + + var entries = BOOMR.plugins.ResourceTiming.findPerformanceEntriesForFrame(window, true); + + // NOTE: what is tested depends on the environment, whether it supports ResourceTiming or not + if (!BOOMR.plugins.ResourceTiming.is_supported()) { + assert.strictEqual(0, entries.length); + + return; + } + + // first entry is faked navigationTiming data + assert.strictEqual(0, entries[0].startTime); + assert.strictEqual(BOOMR.utils.cleanupURL(document.URL), entries[0].name); + + // we could add more, but this should cover it + var entriesToFind = [ + { url: "/tests/vendor/mocha/mocha.css", initiatorType: "link" }, + { url: "/tests/vendor/mocha/mocha.js", initiatorType: "script" }, + { url: "/tests/vendor/assertive-chai/dist/assertive-chai.js", initiatorType: "script" } + ]; + + // we don't know what order these will come in, so grep thru the list + for (var i = 0; i < entriesToFind.length; i++) { + var entryToFind = entriesToFind[i]; + + var found = false; + + for (var j = 0; j < entries.length; j++) { + if (entries[j].name.indexOf(entryToFind.url) && + entries[j].initiatorType && + entries[j].initiatorType === entryToFind.initiatorType) { + found = true; + break; + } + } + + assert.isTrue(found); + } + }); + }); + + describe("getCompressedResourceTiming()", function() { + it("Should get the ResourceTiming trie", function() { + // + // NOTE: If you change the resources for this test suite (test-boomerang.html), this test will + // need to be updated. + // + + var trie = BOOMR.plugins.ResourceTiming.getCompressedResourceTiming(); + + // NOTE: what is tested depends on the environment, whether it supports ResourceTiming or not + if (!BOOMR.plugins.ResourceTiming.is_supported()) { + assert.deepEqual({}, trie.restiming); + + return; + } + + // get the first key, which is our base URL + var baseUrl = ""; + + for (var key in trie.restiming) { + baseUrl = key; + } + + // first entry is faked navigationTiming data + assert.isObject(trie.restiming[baseUrl]); + + // + // NOTE: this test doesn't work in Karma in C/IE/FF, only in real C/IE/FF and PhantomJS + // This is due to different RT entries getting downloaded in the different environment + // + if (typeof trie.restiming[baseUrl]["build/boomerang-latest-debug.js"] === "string") { + // build/ boomerang .js file + assert.isString(trie.restiming[baseUrl]["build/boomerang-latest-debug.js"]); + assert.equal("3", trie.restiming[baseUrl]["build/boomerang-latest-debug.js"][0]); + + // other entries will be under vendor + assert.isObject(trie.restiming[baseUrl]["vendor/"]); + } + }); + }); + + describe("calculateResourceTimingUnion()", function() { + it("Should return 0 if given an empty list", function() { + assert.deepEqual(0, BOOMR.plugins.ResourceTiming.calculateResourceTimingUnion()); + }); + + it("Should return the duration of a single resource", function() { + assert.deepEqual(100, BOOMR.plugins.ResourceTiming.calculateResourceTimingUnion([{ + fetchStart: 10, + responseStart: 110 + }])); + }); + + it("Should return the duration of two resources that don't overlap", function() { + assert.deepEqual(200, BOOMR.plugins.ResourceTiming.calculateResourceTimingUnion([{ + fetchStart: 10, + responseStart: 110 + }, { + fetchStart: 120, + responseEnd: 220 + }])); + }); + + it("Should return the duration of three resources that don't overlap", function() { + assert.deepEqual(300, BOOMR.plugins.ResourceTiming.calculateResourceTimingUnion([{ + fetchStart: 10, + responseStart: 110 + }, { + fetchStart: 120, + responseEnd: 220 + }, { + fetchStart: 530, + responseEnd: 630 + }])); + }); + + it("Should return the duration of two resources, one of which is completely within the other one", function() { + assert.deepEqual(100, BOOMR.plugins.ResourceTiming.calculateResourceTimingUnion([{ + fetchStart: 10, + responseStart: 110 + }, { + fetchStart: 50, + responseEnd: 100 + }])); + }); + + it("Should return the duration of two resources, one of which is partially within the other one", function() { + assert.deepEqual(200, BOOMR.plugins.ResourceTiming.calculateResourceTimingUnion([{ + fetchStart: 10, + responseStart: 110 + }, { + fetchStart: 100, + responseEnd: 210 + }])); + }); + + it("Should return the duration of three resources, one of which is partially within the other one", function() { + assert.deepEqual(300, BOOMR.plugins.ResourceTiming.calculateResourceTimingUnion([{ + fetchStart: 10, + responseStart: 110 + }, { + fetchStart: 100, + responseEnd: 210 + }, { + fetchStart: 300, + responseEnd: 400 + }])); + }); + + it("Should return the duration of three resources, two of which are completely within the other one", function() { + assert.deepEqual(300, BOOMR.plugins.ResourceTiming.calculateResourceTimingUnion([{ + fetchStart: 10, + responseStart: 110 + }, { + fetchStart: 100, + responseEnd: 210 + }, { + fetchStart: 300, + responseEnd: 400 + }])); + }); + + it("Should return the duration of three resources, one of which are completely within the other one, and one of which is completely within the other", function() { + assert.deepEqual(200, BOOMR.plugins.ResourceTiming.calculateResourceTimingUnion([{ + fetchStart: 10, + responseStart: 110 + }, { + fetchStart: 50, + responseEnd: 100 + }, { + fetchStart: 100, + responseEnd: 210 + }])); + }); + + it("Should return the duration of three resources, with each overlapping the other", function() { + assert.deepEqual(300, BOOMR.plugins.ResourceTiming.calculateResourceTimingUnion([{ + fetchStart: 10, + responseStart: 110 + }, { + fetchStart: 100, + responseEnd: 210 + }, { + fetchStart: 200, + responseEnd: 310 + }])); + }); + + it("Should return the duration of three resources, with the later two overlapping each other partially", function() { + assert.deepEqual(200, BOOMR.plugins.ResourceTiming.calculateResourceTimingUnion([{ + fetchStart: 10, + responseStart: 110 + }, { + fetchStart: 210, + responseEnd: 310 + }, { + fetchStart: 300, + responseEnd: 310 + }])); + }); + + it("Should return the duration of three resources, with the later two overlapping each other completely", function() { + assert.deepEqual(200, BOOMR.plugins.ResourceTiming.calculateResourceTimingUnion([{ + fetchStart: 10, + responseStart: 110 + }, { + fetchStart: 210, + responseEnd: 310 + }, { + fetchStart: 250, + responseEnd: 260 + }])); + }); + + it("Should return the duration of three resources of the same duration", function() { + assert.deepEqual(1, BOOMR.plugins.ResourceTiming.calculateResourceTimingUnion([{ + fetchStart: 0, + responseStart: 1 + }, { + fetchStart: 0, + responseEnd: 1 + }, { + fetchStart: 0, + responseEnd: 1 + }])); + }); + + it("Should return the duration of 6 resources", function() { + assert.deepEqual(74, BOOMR.plugins.ResourceTiming.calculateResourceTimingUnion([{ + fetchStart: 0, + responseStart: 45 + }, { + fetchStart: 100, + responseEnd: 102 + }, { + fetchStart: 100, + responseEnd: 103 + }, { + fetchStart: 100, + responseEnd: 102 + }, { + fetchStart: 100, + responseEnd: 102 + }, { + fetchStart: 100, + responseEnd: 129 + }])); + }); + + it("Should return the duration of 7 resources", function() { + assert.deepEqual(74, BOOMR.plugins.ResourceTiming.calculateResourceTimingUnion([{ + fetchStart: 0, + responseStart: 45 + }, { + fetchStart: 100, + responseEnd: 102 + }, { + fetchStart: 100, + responseEnd: 103 + }, { + fetchStart: 100, + responseEnd: 102 + }, { + fetchStart: 100, + responseEnd: 102 + }, { + fetchStart: 100, + responseEnd: 129 + }, { + fetchStart: 1, + responseEnd: 44 + }])); + }); + + it("Should return the duration of 8 resources", function() { + assert.deepEqual(74, BOOMR.plugins.ResourceTiming.calculateResourceTimingUnion([{ + fetchStart: 0, + responseStart: 45 + }, { + fetchStart: 100, + responseEnd: 102 + }, { + fetchStart: 100, + responseEnd: 103 + }, { + fetchStart: 100, + responseEnd: 102 + }, { + fetchStart: 100, + responseEnd: 102 + }, { + fetchStart: 100, + responseEnd: 129 + }, { + fetchStart: 1, + responseEnd: 44 + }, { + fetchStart: 2, + responseEnd: 43 + }])); + }); + + it("Should return the duration of 9 resources", function() { + assert.deepEqual(74, BOOMR.plugins.ResourceTiming.calculateResourceTimingUnion([{ + fetchStart: 0, + responseStart: 45 + }, { + fetchStart: 100, + responseEnd: 102 + }, { + fetchStart: 100, + responseEnd: 103 + }, { + fetchStart: 100, + responseEnd: 102 + }, { + fetchStart: 100, + responseEnd: 102 + }, { + fetchStart: 100, + responseEnd: 129 + }, { + fetchStart: 1, + responseEnd: 44 + }, { + fetchStart: 2, + responseEnd: 43 + }, { + fetchStart: 3, + responseEnd: 42 + }])); + }); + + it("Should return the duration of 10 resources", function() { + assert.deepEqual(74, BOOMR.plugins.ResourceTiming.calculateResourceTimingUnion([{ + fetchStart: 0, + responseStart: 45 + }, { + fetchStart: 100, + responseEnd: 102 + }, { + fetchStart: 100, + responseEnd: 103 + }, { + fetchStart: 100, + responseEnd: 102 + }, { + fetchStart: 100, + responseEnd: 102 + }, { + fetchStart: 100, + responseEnd: 129 + }, { + fetchStart: 1, + responseEnd: 44 + }, { + fetchStart: 2, + responseEnd: 43 + }, { + fetchStart: 3, + responseEnd: 42 + }, { + fetchStart: 4, + responseEnd: 44 + }])); + }); + + it("Should return the duration of 10 resources", function() { + assert.deepEqual(74, BOOMR.plugins.ResourceTiming.calculateResourceTimingUnion([{ + fetchStart: 0, + responseStart: 45 + }, { + fetchStart: 100, + responseEnd: 102 + }, { + fetchStart: 100, + responseEnd: 103 + }, { + fetchStart: 100, + responseEnd: 102 + }, { + fetchStart: 100, + responseEnd: 102 + }, { + fetchStart: 100, + responseEnd: 129 + }, { + fetchStart: 1, + responseEnd: 44 + }, { + fetchStart: 2, + responseEnd: 43 + }, { + fetchStart: 3, + responseEnd: 42 + }, { + fetchStart: 4, + responseEnd: 44 + }, { + fetchStart: 1, + responseEnd: 40 + }])); + }); + + it("Should return the duration of 12 resources", function() { + assert.deepEqual(74, BOOMR.plugins.ResourceTiming.calculateResourceTimingUnion([{ + fetchStart: 0, + responseStart: 45 + }, { + fetchStart: 100, + responseEnd: 102 + }, { + fetchStart: 100, + responseEnd: 103 + }, { + fetchStart: 100, + responseEnd: 102 + }, { + fetchStart: 100, + responseEnd: 102 + }, { + fetchStart: 100, + responseEnd: 129 + }, { + fetchStart: 1, + responseEnd: 44 + }, { + fetchStart: 2, + responseEnd: 43 + }, { + fetchStart: 3, + responseEnd: 42 + }, { + fetchStart: 4, + responseEnd: 41 + }, { + fetchStart: 4, + responseEnd: 44 + }, { + fetchStart: 1, + responseEnd: 40 + }])); + }); + }); + + describe("reduceFetchStarts()", function() { + it("Should return [] if given an empty list", function() { + assert.deepEqual([], BOOMR.plugins.ResourceTiming.reduceFetchStarts([])); + }); + + it("Should return [] if given not a list", function() { + assert.deepEqual([], BOOMR.plugins.ResourceTiming.reduceFetchStarts()); + assert.deepEqual([], BOOMR.plugins.ResourceTiming.reduceFetchStarts(null)); + assert.deepEqual([], BOOMR.plugins.ResourceTiming.reduceFetchStarts(false)); + assert.deepEqual([], BOOMR.plugins.ResourceTiming.reduceFetchStarts(true)); + assert.deepEqual([], BOOMR.plugins.ResourceTiming.reduceFetchStarts(1)); + assert.deepEqual([], BOOMR.plugins.ResourceTiming.reduceFetchStarts(1.0)); + assert.deepEqual([], BOOMR.plugins.ResourceTiming.reduceFetchStarts(-1)); + assert.deepEqual([], BOOMR.plugins.ResourceTiming.reduceFetchStarts(0)); + }); + + it("Should return a single resource if given a single resource", function() { + assert.deepEqual([{ + fetchStart: 10, + responseEnd: 10 + }], BOOMR.plugins.ResourceTiming.reduceFetchStarts([{ + fetchStart: 10, + responseEnd: 10 + }])); + }); + + it("Should return two non-same-start resources", function() { + assert.deepEqual( + [{ fetchStart: 0, responseEnd: 10 }, { fetchStart: 1, responseEnd: 10 }], + BOOMR.plugins.ResourceTiming.reduceFetchStarts([ + { fetchStart: 0, responseEnd: 10 }, { fetchStart: 1, responseEnd: 10 } + ])); + }); + + it("Should return a single resource from two with the same start time", function() { + assert.deepEqual( + [{ fetchStart: 1, responseEnd: 10 }], + BOOMR.plugins.ResourceTiming.reduceFetchStarts([ + { fetchStart: 1, responseEnd: 10 }, { fetchStart: 1, responseEnd: 10 } + ])); + }); + + it("Should return a single resource from two with the same start time where the second one is longer", function() { + assert.deepEqual( + [{ fetchStart: 1, responseEnd: 15 }], + BOOMR.plugins.ResourceTiming.reduceFetchStarts([ + { fetchStart: 1, responseEnd: 10 }, { fetchStart: 1, responseEnd: 15 } + ])); + }); + + it("Should return a single resource from 3 with the same start time", function() { + assert.deepEqual( + [{ fetchStart: 1, responseEnd: 10 }], + BOOMR.plugins.ResourceTiming.reduceFetchStarts([ + { fetchStart: 1, responseEnd: 10 }, { fetchStart: 1, responseEnd: 10 }, { fetchStart: 1, responseEnd: 10 } + ])); + }); + + it("Should return two resources from two with the same start time and one with a different", function() { + assert.deepEqual( + [{ fetchStart: 0, responseEnd: 10 }, { fetchStart: 1, responseEnd: 10 }], + BOOMR.plugins.ResourceTiming.reduceFetchStarts([ + { fetchStart: 0, responseEnd: 10 }, { fetchStart: 1, responseEnd: 10 }, { fetchStart: 1, responseEnd: 10 } + ])); + }); + }); + + describe("compressSize()", function() { + it("Should return an empty string if ResourceTiming2 is not supported", function() { + assert.equal("", BOOMR.plugins.ResourceTiming.compressSize({})); + }); + + // X-O: [0, 0, 0] -> [0, 0, 0] -> [empty] + it("Should return an empty string for cross-origin resources", function() { + assert.equal("", BOOMR.plugins.ResourceTiming.compressSize({ + transferSize: 0, + encodedBodySize: 0, + decodedBodySize: 0 + })); + }); + + // 204: [t, 0, 0] -> [t, 0, 0] -> [e, t-e] + it("Should return [e, t-e, d-e] -> [0, t, 0] -> ',t,0' -> ',t' for 204 responses", function() { + assert.equal(",a", BOOMR.plugins.ResourceTiming.compressSize({ + transferSize: 10, + encodedBodySize: 0, + decodedBodySize: 0 + })); + }); + + // 304: [t: t <=> e, e, d: d>=e] -> [e, t-e, d-e] + it("Should return [e, t-e, d-e] -> [e, dt, dd] -> 'e,dt,0' -> 'e,dt' for 304 responses (t > e, d = e)", function() { + assert.equal("a,5", BOOMR.plugins.ResourceTiming.compressSize({ + transferSize: 15, + encodedBodySize: 10, + decodedBodySize: 10 + })); + }); + + it("Should return [e, t-e, d-e] -> [e, dt, dd] -> 'e,-dt,0' -> 'e,-dt' for 304 responses (t < e, d = e)", function() { + assert.equal("a,-5", BOOMR.plugins.ResourceTiming.compressSize({ + transferSize: 5, + encodedBodySize: 10, + decodedBodySize: 10 + })); + }); + + it("Should return [e, t-e, d-e] -> [e, dt, dd] -> 'e,0,0' -> 'e' for 304 responses (t = e, d = e)", function() { + assert.equal("a", BOOMR.plugins.ResourceTiming.compressSize({ + transferSize: 10, + encodedBodySize: 10, + decodedBodySize: 10 + })); + }); + + it("Should return [e, t-e, d-e] -> [e, dt, dd] -> 'e,dt,dd' for 304 responses (t > e, d > e)", function() { + assert.equal("a,a,5", BOOMR.plugins.ResourceTiming.compressSize({ + transferSize: 20, + encodedBodySize: 10, + decodedBodySize: 15 + })); + }); + + it("Should return [e, t-e, d-e] -> [e, dt, dd] -> 'e,-dt,dd' for 304 responses (t < e, d > e)", function() { + assert.equal("a,-5,5", BOOMR.plugins.ResourceTiming.compressSize({ + transferSize: 5, + encodedBodySize: 10, + decodedBodySize: 15 + })); + }); + + it("Should return [e, t-e, d-e] -> [e, dt, dd] -> 'e,0,dd' -> 'e,,dd' for 304 responses (t = e, d > e)", function() { + assert.equal("a,,5", BOOMR.plugins.ResourceTiming.compressSize({ + transferSize: 10, + encodedBodySize: 10, + decodedBodySize: 15 + })); + }); + + // 200 non-gzipped: [t: t>=e, e, d: d=e] -> [e, t-e] + it("Should return [e, t-e, d-e] -> [e, dt, 0] -> 'e,0' -> 'e' for 200 non-gzipped responses (t = e)", function() { + assert.equal("a", BOOMR.plugins.ResourceTiming.compressSize({ + transferSize: 10, + encodedBodySize: 10, + decodedBodySize: 10 + })); + }); + + it("Should return [e, t-e, d-e] -> [e, dt, 0] -> 'e,dt' for 200 non-gzipped responses (t > e)", function() { + assert.equal("a,5", BOOMR.plugins.ResourceTiming.compressSize({ + transferSize: 15, + encodedBodySize: 10, + decodedBodySize: 10 + })); + }); + + // 200 gzipped: [t: t>=e, e, d: d>=e] -> [e, t-e, d-e] + it("Should return [e, t-e, d-e] -> [e, dt, 0] -> 'e,0,dd' -> 'e,,dd' for 200 gzipped responses (t = e, d > e)", function() { + assert.equal("a,,5", BOOMR.plugins.ResourceTiming.compressSize({ + transferSize: 10, + encodedBodySize: 10, + decodedBodySize: 15 + })); + }); + + it("Should return [e, t-e, d-e] -> [e, dt, 0] -> 'e,dt,dd' for 200 gzipped responses (t > e, d > e)", function() { + assert.equal("a,5,a", BOOMR.plugins.ResourceTiming.compressSize({ + transferSize: 15, + encodedBodySize: 10, + decodedBodySize: 20 + })); + }); + + // retrieved from cache non-gzipped: [0, e, d: d=e] -> [e] + it("Should return [e, t-e, d-e] -> [e, _, dd] -> 'e,_,0' -> 'e,_' for cached non-gzipped responses", function() { + assert.equal("a,_", BOOMR.plugins.ResourceTiming.compressSize({ + transferSize: 0, + encodedBodySize: 10, + decodedBodySize: 10 + })); + }); + + // retrieved from cache gzipped: [0, e, d: d>=e] -> [e, _, d-e] + it("Should return [e, t-e, d-e] -> [e, _, dd] -> 'e,_,dd' -> 'e,_,dd' for cached gzipped responses", function() { + assert.equal("a,_,5", BOOMR.plugins.ResourceTiming.compressSize({ + transferSize: 0, + encodedBodySize: 10, + decodedBodySize: 15 + })); + }); + }); + + describe("decompressSize()", function() { + // X-O: [0, 0, 0] -> [0, 0, 0] -> [empty] + it("Should reverse cross-origin resources", function() { + assert.deepEqual({ + transferSize: 0, + encodedBodySize: 0, + decodedBodySize: 0 + }, BOOMR.plugins.ResourceTiming.decompressSize("")); + }); + + // 204: [t, 0, 0] -> [t, 0, 0] -> [e, t-e] + it("Should reverse [e, t-e, d-e] -> [0, t, 0] -> ',t,0' -> ',t' for 204 responses", function() { + assert.deepEqual({ + transferSize: 10, + encodedBodySize: 0, + decodedBodySize: 0 + }, BOOMR.plugins.ResourceTiming.decompressSize(",a")); + }); + + // 304: [t: t <=> e, e, d: d>=e] -> [e, t-e, d-e] + it("Should reverse [e, t-e, d-e] -> [e, dt, dd] -> 'e,dt,0' -> 'e,dt' for 304 responses (t > e, d = e)", function() { + assert.deepEqual({ + transferSize: 15, + encodedBodySize: 10, + decodedBodySize: 10 + }, BOOMR.plugins.ResourceTiming.decompressSize("a,5")); + }); + + it("Should reverse [e, t-e, d-e] -> [e, dt, dd] -> 'e,-dt,0' -> 'e,-dt' for 304 responses (t < e, d = e)", function() { + assert.deepEqual({ + transferSize: 5, + encodedBodySize: 10, + decodedBodySize: 10 + }, BOOMR.plugins.ResourceTiming.decompressSize("a,-5")); + }); + + it("Should reverse [e, t-e, d-e] -> [e, dt, dd] -> 'e,0,0' -> 'e' for 304 responses (t = e, d = e)", function() { + assert.deepEqual({ + transferSize: 10, + encodedBodySize: 10, + decodedBodySize: 10 + }, BOOMR.plugins.ResourceTiming.decompressSize("a")); + }); + + it("Should reverse [e, t-e, d-e] -> [e, dt, dd] -> 'e,dt,dd' for 304 responses (t > e, d > e)", function() { + assert.deepEqual({ + transferSize: 20, + encodedBodySize: 10, + decodedBodySize: 15 + }, BOOMR.plugins.ResourceTiming.decompressSize("a,a,5")); + }); + + it("Should reverse [e, t-e, d-e] -> [e, dt, dd] -> 'e,-dt,dd' for 304 responses (t < e, d > e)", function() { + assert.deepEqual({ + transferSize: 5, + encodedBodySize: 10, + decodedBodySize: 15 + }, BOOMR.plugins.ResourceTiming.decompressSize("a,-5,5")); + }); + + it("Should reverse [e, t-e, d-e] -> [e, dt, dd] -> 'e,0,dd' -> 'e,,dd' for 304 responses (t = e, d > e)", function() { + assert.deepEqual({ + transferSize: 10, + encodedBodySize: 10, + decodedBodySize: 15 + }, BOOMR.plugins.ResourceTiming.decompressSize("a,,5")); + }); + + // 200 non-gzipped: [t: t>=e, e, d: d=e] -> [e, t-e] + it("Should reverse [e, t-e, d-e] -> [e, dt, 0] -> 'e,0' -> 'e' for 200 non-gzipped responses (t = e)", function() { + assert.deepEqual({ + transferSize: 10, + encodedBodySize: 10, + decodedBodySize: 10 + }, BOOMR.plugins.ResourceTiming.decompressSize("a")); + }); + + it("Should reverse [e, t-e, d-e] -> [e, dt, 0] -> 'e,dt' for 200 non-gzipped responses (t > e)", function() { + assert.deepEqual({ + transferSize: 15, + encodedBodySize: 10, + decodedBodySize: 10 + }, BOOMR.plugins.ResourceTiming.decompressSize("a,5")); + }); + + // 200 gzipped: [t: t>=e, e, d: d>=e] -> [e, t-e, d-e] + it("Should reverse [e, t-e, d-e] -> [e, dt, 0] -> 'e,0,dd' -> 'e,,dd' for 200 gzipped responses (t = e, d > e)", function() { + assert.deepEqual({ + transferSize: 10, + encodedBodySize: 10, + decodedBodySize: 15 + }, BOOMR.plugins.ResourceTiming.decompressSize("a,,5")); + }); + + it("Should reverse [e, t-e, d-e] -> [e, dt, 0] -> 'e,dt,dd' for 200 gzipped responses (t > e, d > e)", function() { + assert.deepEqual({ + transferSize: 15, + encodedBodySize: 10, + decodedBodySize: 20 + }, BOOMR.plugins.ResourceTiming.decompressSize("a,5,a")); + }); + + // retrieved from cache non-gzipped: [0, e, d: d=e] -> [e] + it("Should reverse [e, t-e, d-e] -> [e, _, dd] -> 'e,_,0' -> 'e,_' for cached non-gzipped responses", function() { + assert.deepEqual({ + transferSize: 0, + encodedBodySize: 10, + decodedBodySize: 10 + }, BOOMR.plugins.ResourceTiming.decompressSize("a,_")); + }); + + // retrieved from cache gzipped: [0, e, d: d>=e] -> [e, _, d-e] + it("Should reverse [e, t-e, d-e] -> [e, _, dd] -> 'e,_,dd' -> 'e,_,dd' for cached gzipped responses", function() { + assert.deepEqual({ + transferSize: 0, + encodedBodySize: 10, + decodedBodySize: 15 + }, BOOMR.plugins.ResourceTiming.decompressSize("a,_,5")); + }); + }); + + describe("trimUrl()", function() { + it("Should not change the URL if given an empty second argument", function() { + assert.equal("http://foo.com/bar", BOOMR.plugins.ResourceTiming.trimUrl("http://foo.com/bar")); + }); + + it("Should not change the URL if given no URLs to trim", function() { + assert.equal("http://foo.com/bar", BOOMR.plugins.ResourceTiming.trimUrl("http://foo.com/bar", [])); + }); + + it("Should not change the URL if the list of URLs to trim doesn't contain a match", function() { + assert.equal("http://foo.com/bar", BOOMR.plugins.ResourceTiming.trimUrl("http://foo.com/bar", ["http://other.com"])); + }); + + it("Should trim a URL that is contained as a string in the trim list", function() { + assert.equal("http://foo.com/...", BOOMR.plugins.ResourceTiming.trimUrl("http://foo.com/bar", ["http://foo.com/"])); + }); + + it("Should trim a URL that is contained as a regex in the trim list", function() { + assert.equal("http://foo.com/...", BOOMR.plugins.ResourceTiming.trimUrl("http://foo.com/bar", [/(http:\/\/foo.com\/).*/])); + }); + }); + + describe("getVisibleEntries()", function() { + it("Should get all visible entries for this page", function() { + // + // NOTE: If you change the resources for this test suite (index.html), this test will + // need to be updated. + // + + var entries = BOOMR.plugins.ResourceTiming.getVisibleEntries(window); + + // Running on the command line, so no images on the page + if (document.getElementsByTagName("img").length === 0) { + assert.strictEqual(Object.keys(entries).length, 0); + + return; + } + + var host = location.protocol + "//" + location.host; + var entriesToFind = [ + { url: "/assets/img.jpg?0", top: 50, left: 50, height: 107, width: 107, naturalHeight: 742, naturalWidth: 2000 }, + { url: "/assets/img.jpg?1", top: 120, left: 120, height: 201, width: 201, naturalHeight: 742, naturalWidth: 2000 }, + { url: "/assets/img.jpg?2", top: 400, left: -15, height: 742, width: 2000 } + ]; + + assert.strictEqual(Object.keys(entries).length, entriesToFind.length); + + for (var i = 0; i < entriesToFind.length; i++) { + var entryToFind = entriesToFind[i]; + + assert.isTrue(entries.hasOwnProperty(host + entryToFind.url)); + + var foundEntry = entries[host + entryToFind.url]; + + assert.strictEqual(foundEntry[0], entryToFind.height); + assert.strictEqual(foundEntry[1], entryToFind.width); + assert.strictEqual(foundEntry[2], entryToFind.top); + assert.strictEqual(foundEntry[3], entryToFind.left); + + if (entryToFind.naturalHeight) { + assert.strictEqual(foundEntry[4], entryToFind.naturalHeight); + assert.strictEqual(foundEntry[5], entryToFind.naturalWidth); + } + else { + assert.strictEqual(foundEntry.length, 4); + } + } + }); + }); + + describe("accumulateServerTimingEntries", function() { + it("Should increment our count collector", function() { + var serverTimingCollection = {}; + + BOOMR.plugins.ResourceTiming.accumulateServerTimingEntries(serverTimingCollection, [{name: "n1", description: "d1"}, {name: "n2", description: "d1"}]); + BOOMR.plugins.ResourceTiming.accumulateServerTimingEntries(serverTimingCollection, [{name: "n2", description: "d1"}, {name: "n2", description: "d2"}]); + assert.deepEqual(serverTimingCollection, { + n1: { + count: 1, + counts: { + d1: 1 + } + }, + n2: { + count: 3, + counts: { + d1: 2, + d2: 1 + } + } + }); + }); + }); + + describe("compressServerTiming", function() { + it("Should create a lookup from our count collector", function() { + assert.deepEqual(BOOMR.plugins.ResourceTiming.compressServerTiming({ + m0: { + count: 0, + counts: {} + }, + m1: { + count: 1, + counts: { + d1: 1 + } + }, m2: { + count: 5, + counts: { + d2a: 3, + d2b: 2 + } + } + }), [ + ["m2", "d2a", "d2b"], + ["m1", "d1"], + ["m0"] + ]); + }); + it("Should special case exactly one empty description", function() { + assert.deepEqual(BOOMR.plugins.ResourceTiming.compressServerTiming({ + m0: { + count: 1, + counts: { + "": 1 + } + } + }), [ + "m0" + ]); + }); + }); + + describe("indexServerTiming", function() { + it("Should index a lookup", function() { + assert.deepEqual(BOOMR.plugins.ResourceTiming.indexServerTiming([ + ["metric0", "d1"], + ["metric1", "d2a", "d2b"] + ]), { + metric0: { + index: 0, + descriptions: { + d1: 0 + } + }, + metric1: { + index: 1, + descriptions: { + d2a: 0, + d2b: 1 + } + } + }); + }); + + it("Should special case exactly one empty description", function() { + assert.deepEqual(BOOMR.plugins.ResourceTiming.indexServerTiming([ + "metric0" + ]), { + metric0: { + index: 0, + descriptions: { + "": 0 + } + } + }); + }); + }); + + describe("identifyServerTimingEntry", function() { + it("Should create identifiers", function() { + assert.equal(BOOMR.plugins.ResourceTiming.identifyServerTimingEntry(0, 0), ""); + assert.equal(BOOMR.plugins.ResourceTiming.identifyServerTimingEntry(0, 1), ":.1"); + assert.equal(BOOMR.plugins.ResourceTiming.identifyServerTimingEntry(1, 0), ":1"); + assert.equal(BOOMR.plugins.ResourceTiming.identifyServerTimingEntry(1, 1), ":1.1"); + }); + }); + + describe("decompressServerTiming", function() { + var optimized = [["m0", "d0a", "d0b"], ["m1", "d1a", "d1b"], "m2"]; + + it("Should resolve to m0-d0a", function() { + ["123", "123:0", "123:.0", "123:0.0"].forEach(function(compressed) { + assert.deepEqual(BOOMR.plugins.ResourceTiming.decompressServerTiming(optimized, compressed), { + name: "m0", + description: "d0a", + duration: 123 + }); + }); + }); + it("Should resolve to m0-d0b", function() { + ["123:.1", "123:0.1"].forEach(function(compressed) { + assert.deepEqual(BOOMR.plugins.ResourceTiming.decompressServerTiming(optimized, compressed), { + name: "m0", + description: "d0b", + duration: 123 + }); + }); + }); + it("Should resolve to m1-d1a", function() { + ["123:1", "123:1.0"].forEach(function(compressed) { + assert.deepEqual(BOOMR.plugins.ResourceTiming.decompressServerTiming(optimized, compressed), { + name: "m1", + description: "d1a", + duration: 123 + }); + }); + }); + it("Should resolve to m1-d1b", function() { + ["123:1.1"].forEach(function(compressed) { + assert.deepEqual(BOOMR.plugins.ResourceTiming.decompressServerTiming(optimized, compressed), { + name: "m1", + description: "d1b", + duration: 123 + }); + }); + }); + it("Should resolve to m1-", function() { + ["123:2"].forEach(function(compressed) { + assert.deepEqual(BOOMR.plugins.ResourceTiming.decompressServerTiming(optimized, compressed), { + name: "m2", + description: "", + duration: 123 + }); + }); + }); + }); +}); diff --git a/tests/unit/06-mutationhandler.js b/tests/unit/06-mutationhandler.js new file mode 100644 index 000000000..fc37631d9 --- /dev/null +++ b/tests/unit/06-mutationhandler.js @@ -0,0 +1,50 @@ +/* eslint-env mocha */ +/* global chai,describe,it */ + +describe("BOOMR.plugins.AutoXHR.MutationHandler", function() { + var assert = chai.assert; + var t = BOOMR_test; + + beforeEach(function() { + if (!BOOMR.plugins.AutoXHR) { + return this.skip(); + } + }); + + it("Should return interesting true from wait_for_node if container has network bound resources", function() { + if (t.isMutationObserverSupported()) { + // Get current handler + var handler = BOOMR.plugins.AutoXHR.getMutationHandler(); + var imgPath = "/pages/07-autoxhr/support/img.jpg"; + // Pre-Fill pending events so it will find this on recursing the wait_for_node call for the child image + var index = handler.pending_events.push({ + complete: false, + url: null, + resource: { + timing: {} + }, + resources: [], + nodes_to_wait: 0 + }); + + var element = document.createElement("div"); + var img = document.createElement("img"); + + img.src = imgPath; + element.appendChild(img); + + // Expect size of interesting to be 1 based on the number of network bound items in the container (1 img tag) + var expect = 1; + var interesting = handler.wait_for_node(element, 0); + + // Return test + assert.equal(interesting, expect); + + // Validating that pending_events was filled correctly + assert.include(handler.pending_events[index - 1].resource.url, imgPath); + assert.equal(handler.pending_events[index - 1].resources.length, 1); + assert.deepEqual(handler.pending_events[index - 1].resources[0], img); + assert.equal(handler.pending_events[index - 1].urls[img.src], 1); + } + }); +}); diff --git a/tests/unit/index.html b/tests/unit/index.html new file mode 100644 index 000000000..34f8e7ed5 --- /dev/null +++ b/tests/unit/index.html @@ -0,0 +1,66 @@ + + + + + Mocha + + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/unit/polyfills.js b/tests/unit/polyfills.js new file mode 100644 index 000000000..b7f7b6c77 --- /dev/null +++ b/tests/unit/polyfills.js @@ -0,0 +1,89 @@ +// via https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map +// Production steps of ECMA-262, Edition 5, 15.4.4.19 +// Reference: http://es5.github.io/#x15.4.4.19 +if (!Array.prototype.map) { + /* eslint-disable no-extend-native */ + Array.prototype.map = function(callback, thisArg) { + /* eslint-enable no-extend-native */ + var T, A, k; + + if (this === null) { + throw new TypeError(" this is null or not defined"); + } + + // 1. Let O be the result of calling ToObject passing the |this| + // value as the argument. + var O = Object(this); + + // 2. Let lenValue be the result of calling the Get internal + // method of O with the argument "length". + // 3. Let len be ToUint32(lenValue). + var len = O.length >>> 0; + + // 4. If IsCallable(callback) is false, throw a TypeError exception. + // See: http://es5.github.com/#x9.11 + if (typeof callback !== "function") { + throw new TypeError(callback + " is not a function"); + } + + // 5. If thisArg was supplied, let T be thisArg; else let T be undefined. + if (arguments.length > 1) { + T = thisArg; + } + + // 6. Let A be a new array created as if by the expression new Array(len) + // where Array is the standard built-in constructor with that name and + // len is the value of len. + A = new Array(len); + + // 7. Let k be 0 + k = 0; + + // 8. Repeat, while k < len + while (k < len) { + var kValue, mappedValue; + + // a. Let Pk be ToString(k). + // This is implicit for LHS operands of the in operator + // b. Let kPresent be the result of calling the HasProperty internal + // method of O with argument Pk. + // This step can be combined with c + // c. If kPresent is true, then + if (k in O) { + // i. Let kValue be the result of calling the Get internal + // method of O with argument Pk. + kValue = O[k]; + + // ii. Let mappedValue be the result of calling the Call internal + // method of callback with T as the this value and argument + // list containing kValue, k, and O. + mappedValue = callback.call(T, kValue, k, O); + + // iii. Call the DefineOwnProperty internal method of A with arguments + // Pk, Property Descriptor + // { Value: mappedValue, + // Writable: true, + // Enumerable: true, + // Configurable: true }, + // and false. + + // In browsers that support Object.defineProperty, use the following: + // Object.defineProperty(A, k, { + // value: mappedValue, + // writable: true, + // enumerable: true, + // configurable: true + // }); + + // For best browser support, use the following: + A[k] = mappedValue; + } + + // d. Increase k by 1. + k++; + } + + // 9. return A + return A; + }; +} diff --git a/zzz_last_plugin.js b/zzz_last_plugin.js deleted file mode 100644 index 049c5d909..000000000 --- a/zzz_last_plugin.js +++ /dev/null @@ -1 +0,0 @@ -BOOMR.t_end = new Date().getTime();