From 67692568bd0ee940171e7d2bb04961fa364de0ee Mon Sep 17 00:00:00 2001 From: Danny Gleckler Date: Mon, 10 Jul 2023 16:44:11 -0400 Subject: [PATCH 01/43] Add d2l-test binary --- src/server/wtr-config.js => bin/test.js | 84 +++++++++++-------- package-lock.json | 3 + package.json | 5 +- src/server/headed-mode-plugin.js | 1 + src/server/index.js | 2 +- src/server/visual-diff-plugin.js | 1 + src/server/visual-diff-reporter.js | 2 +- ...wtr-vdiff.config.js => d2l-test.config.js} | 10 +-- test/server/wtr-config.test.js | 6 +- 9 files changed, 68 insertions(+), 46 deletions(-) rename src/server/wtr-config.js => bin/test.js (76%) mode change 100644 => 100755 rename test/browser/{wtr-vdiff.config.js => d2l-test.config.js} (60%) diff --git a/src/server/wtr-config.js b/bin/test.js old mode 100644 new mode 100755 similarity index 76% rename from src/server/wtr-config.js rename to bin/test.js index 05d68208..17b2d1c9 --- a/src/server/wtr-config.js +++ b/bin/test.js @@ -1,32 +1,44 @@ +#!/usr/bin/env node + +import { ConfigLoaderError, readConfig } from '@web/config-loader'; +import { defaultReporter, startTestRunner } from '@web/test-runner'; import commandLineArgs from 'command-line-args'; -import { defaultReporter } from '@web/test-runner'; -import { headedMode } from './headed-mode-plugin.js'; +import { headedMode } from '../src/server/headed-mode-plugin.js'; import { playwrightLauncher } from '@web/test-runner-playwright'; -import { visualDiff } from './visual-diff-plugin.js'; -import { visualDiffReporter } from './visual-diff-reporter.js'; +import { visualDiff } from '../src/server/visual-diff-plugin.js'; +import { visualDiffReporter } from '../src/server/visual-diff-reporter.js'; const optionDefinitions = [ // @web/test-runner options { name: 'files', type: String, multiple: true }, - { name: 'group', type: String }, + { name: 'group', type: String, defaultOption: true }, { name: 'manual', type: Boolean }, - { name: 'playwright', type: Boolean }, + { name: 'config', type: String }, { name: 'watch', type: Boolean }, // custom options - { name: 'chromium', type: Boolean }, + { name: 'chrome', type: Boolean }, { name: 'filter', alias: 'f', type: String, multiple: true }, { name: 'firefox', type: Boolean }, { name: 'golden', type: Boolean }, { name: 'grep', alias: 'g', type: String }, { name: 'timeout', type: Number }, - { name: 'webkit', type: Boolean }, + { name: 'safari', type: Boolean }, ]; const cliArgs = commandLineArgs(optionDefinitions, { partial: true }); +const testConfig = await readConfig('d2l-test.config', cliArgs.config).catch(err => { + if (err instanceof ConfigLoaderError) { + throw new Error(err.message); + } else { + throw err; + } +}) || {}; + +//const DISALLOWED_ARGS = ['browsers', 'playwright', '_unknown']; const DEFAULT_PATTERN = type => `./test/**/*.${type}.js`; -const DEFAULT_VDIFF = false; -const ALLOWED_BROWSERS = ['chromium', 'firefox', 'webkit']; +const ALLOWED_BROWSERS = ['chrome', 'firefox', 'safari']; +const BROWSER_MAP = { chrome: 'chromium', safari: 'webkit' }; export class WTRConfig { @@ -35,13 +47,13 @@ export class WTRConfig { constructor(cliArgs) { this.#cliArgs = cliArgs || {}; - const requestedBrowsers = ALLOWED_BROWSERS.filter(b => cliArgs?.[b]); + this.#cliArgs.group ??= 'unit'; + const requestedBrowsers = ALLOWED_BROWSERS.filter(b => cliArgs?.[b]).map(b => BROWSER_MAP[b] || b); this.#requestedBrowsers = requestedBrowsers.length && requestedBrowsers; } get #defaultConfig() { return { - files: this.#getPattern('test'), nodeResolve: true, testRunnerHtml: testFramework => ` @@ -64,7 +76,7 @@ export class WTRConfig { return { name: 'vdiff', files: this.#getPattern('vdiff'), - browsers: this.getBrowsers(['chromium']), + browsers: ['chrome'], testRunnerHtml: testFramework => ` @@ -126,7 +138,7 @@ export class WTRConfig { } #getPattern(type) { - const pattern = this.#cliArgs.files || this.pattern(type); + const pattern = [].concat(this.#cliArgs.files || this.pattern(type)); // replace filename wildcards with all filter strings // e.g. If filter is ['button', 'list'], pattern './test/*.test.*' becomes: @@ -134,37 +146,27 @@ export class WTRConfig { if (this.#cliArgs.filter) { return this.#cliArgs.filter.map(filterStr => { // replace everything after the last forward slash - return pattern.replace(/[^/]*$/, fileGlob => { + return pattern.map(p => p.replace(/[^/]*$/, fileGlob => { // create a new glob for each wildcard const fileGlobs = Array.from(fileGlob.matchAll(/(? { const arr = fileGlob.split(''); - arr.splice(index, 1, `*${filterStr}*`); + arr.splice(index, 1, filterStr); return arr.join(''); }); - return `+(${fileGlobs.join('|')})`; - }); - }); + return `+(${fileGlobs.join('|') || fileGlob})`; + })); + }).flat(); } return pattern; } create({ pattern = DEFAULT_PATTERN, - vdiff = DEFAULT_VDIFF, timeout, ...passthroughConfig } = {}) { - const { files, filter, golden, grep, group, manual, playwright, watch } = this.#cliArgs; - if (!group || group === 'default') { - if (playwright) { - console.warn('Warning: reducedMotion disabled. Use the unit group to enable reducedMotion.'); - } else { - console.warn('Warning: Running with puppeteer, reducedMotion disabled. Use the unit group to use playwright with reducedMotion enabled'); - } - } - delete passthroughConfig.browsers; if (typeof pattern !== 'function') throw new TypeError('pattern must be a function'); @@ -179,11 +181,10 @@ export class WTRConfig { config.groups ??= []; config.groups.push({ name: 'unit', - files: this.#getPattern('test'), - browsers: this.getBrowsers() + files: this.#getPattern('test') }); - if (vdiff) { + if (group === 'vdiff') { config.reporters ??= [ defaultReporter() ]; config.reporters.push(visualDiffReporter({ reportResults: !golden })); @@ -193,6 +194,9 @@ export class WTRConfig { config.groups.push(this.visualDiffGroup); } + // convert all browsers to playwright + config.groups.forEach(g => g.browsers = this.getBrowsers(g.browsers)); + if (watch || manual) { config.plugins ??= []; const currentPattern = files || config.groups.find(g => g.name === group)?.files || config.files; @@ -221,12 +225,26 @@ export class WTRConfig { } -export function createConfig(...args) { +/* +function createConfig(...args) { const wtrConfig = new WTRConfig(cliArgs); return wtrConfig.create(...args); } +*/ +/* export function getBrowsers(browsers) { const wtrConfig = new WTRConfig(cliArgs); return wtrConfig.getBrowsers(browsers); } +*/ + +const wtrConfig = new WTRConfig(cliArgs); +const config = wtrConfig.create(testConfig); + +await startTestRunner({ + readCliArgs: true, + argv: [ '--group', cliArgs.group, ...(cliArgs._unknown || []) ], + readFileConfig: false, + config +}); diff --git a/package-lock.json b/package-lock.json index 18c6294e..aa04e756 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,9 @@ "pixelmatch": "^5", "pngjs": "^7" }, + "bin": { + "d2l-test": "bin/test.js" + }, "devDependencies": { "@rollup/plugin-node-resolve": "^15", "@web/dev-server": "^0.2", diff --git a/package.json b/package.json index 00c3a649..098f32e8 100644 --- a/package.json +++ b/package.json @@ -10,9 +10,12 @@ "test": "npm run lint && npm run test:server && npm run test:browser", "test:browser": "web-test-runner --files \"./test/browser/**/*.test.js\" --node-resolve --playwright", "test:server": "mocha ./test/server/**/*.test.js", - "test:vdiff": "web-test-runner --config ./test/browser/wtr-vdiff.config.js --group vdiff", + "test:vdiff": "d2l-test --config ./test/browser/d2l-test.config.js --group vdiff", "test:vdiff:golden": "npm run test:vdiff -- --golden" }, + "bin": { + "d2l-test": "./bin/test.js" + }, "author": "D2L Corporation", "license": "Apache-2.0", "devDependencies": { diff --git a/src/server/headed-mode-plugin.js b/src/server/headed-mode-plugin.js index cc4c757d..ec6912b1 100644 --- a/src/server/headed-mode-plugin.js +++ b/src/server/headed-mode-plugin.js @@ -8,6 +8,7 @@ export function headedMode({ manual, watch, pattern }) { name: 'brightspace-headed-mode', async transform(context) { if ((watch || manual) && files.includes(context.path.slice(1))) { + // allow time to open devtools in firefox and webkit watch && await new Promise(r => setTimeout(r, 2000)); return `debugger;\n${context.body}`; } diff --git a/src/server/index.js b/src/server/index.js index 0c69be54..3462b1c5 100644 --- a/src/server/index.js +++ b/src/server/index.js @@ -1 +1 @@ -export { createConfig, getBrowsers } from './wtr-config.js'; +//export { getBrowsers } from './wtr-config.js'; diff --git a/src/server/visual-diff-plugin.js b/src/server/visual-diff-plugin.js index be23ff51..a1d1b4ca 100644 --- a/src/server/visual-diff-plugin.js +++ b/src/server/visual-diff-plugin.js @@ -89,6 +89,7 @@ async function tryMoveFile(srcFileName, destFileName) { await rename(srcFileName, destFileName); return true; } catch (e) { + console.log('HEY!'); console.warn(e); return false; } diff --git a/src/server/visual-diff-reporter.js b/src/server/visual-diff-reporter.js index 10f81bc3..6c552f44 100644 --- a/src/server/visual-diff-reporter.js +++ b/src/server/visual-diff-reporter.js @@ -112,7 +112,7 @@ export function visualDiffReporter({ reportResults = true } = {}) { cpSync(inputDir, tempDir, { force: true, recursive: true }); writeFileSync(join(tempDir, 'data.js'), `export default ${json};`); - execSync(`rollup -c ${join(__dirname, './rollup.config.js')}`); + execSync(`npx rollup -c ${join(__dirname, './rollup.config.js')}`, { stdio: 'pipe' }); rmSync(tempDir, { recursive: true }); diff --git a/test/browser/wtr-vdiff.config.js b/test/browser/d2l-test.config.js similarity index 60% rename from test/browser/wtr-vdiff.config.js rename to test/browser/d2l-test.config.js index 1ee0a3d7..62d4bd85 100644 --- a/test/browser/wtr-vdiff.config.js +++ b/test/browser/d2l-test.config.js @@ -1,7 +1,4 @@ import { argv } from 'node:process'; -import { createConfig } from '../../src/server/wtr-config.js'; - -const pattern = type => `test/browser/**/*.${type}.js`; function getGoldenFlag() { return { @@ -13,8 +10,7 @@ function getGoldenFlag() { }; } -export default createConfig({ - pattern, - vdiff: true, +export default { + pattern: type => `test/browser/**/*.${type}.js`, plugins: [getGoldenFlag()] -}); +}; diff --git a/test/server/wtr-config.test.js b/test/server/wtr-config.test.js index 3f573d9b..8e6977b1 100644 --- a/test/server/wtr-config.test.js +++ b/test/server/wtr-config.test.js @@ -169,14 +169,14 @@ describe('createWtrConfig', () => { }); it('should use browsers passed as arguments', () => { - const browsers = wtrConfig.getBrowsers(['webkit']); + const browsers = wtrConfig.getBrowsers(['safari']); expect(browsers).to.have.length(1); expect(browsers[0].name).to.equal('Webkit'); }); it('should only use CLI browsers when provided', () => { - const wtrConfig = new WTRConfig({ firefox: true, webkit: true }); - const browsers = wtrConfig.getBrowsers(['chromium']); + const wtrConfig = new WTRConfig({ firefox: true, safari: true }); + const browsers = wtrConfig.getBrowsers(['chrome']); expect(browsers).to.have.length(2); expect(browsers.map(b => b.name)).to.have.members(['Webkit', 'Firefox']); }); From 7f73356dac1274e122d7583dc543192061122c96 Mon Sep 17 00:00:00 2001 From: Danny Gleckler Date: Tue, 11 Jul 2023 13:54:29 -0400 Subject: [PATCH 02/43] Map browser names in getBrowsers --- bin/test.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bin/test.js b/bin/test.js index 17b2d1c9..44bfb556 100755 --- a/bin/test.js +++ b/bin/test.js @@ -38,7 +38,7 @@ const testConfig = await readConfig('d2l-test.config', cliArgs.config).catch(err //const DISALLOWED_ARGS = ['browsers', 'playwright', '_unknown']; const DEFAULT_PATTERN = type => `./test/**/*.${type}.js`; const ALLOWED_BROWSERS = ['chrome', 'firefox', 'safari']; -const BROWSER_MAP = { chrome: 'chromium', safari: 'webkit' }; +const BROWSER_MAP = { chrome: 'chromium', firefox: 'firefox', safari: 'webkit' }; export class WTRConfig { @@ -48,7 +48,7 @@ export class WTRConfig { constructor(cliArgs) { this.#cliArgs = cliArgs || {}; this.#cliArgs.group ??= 'unit'; - const requestedBrowsers = ALLOWED_BROWSERS.filter(b => cliArgs?.[b]).map(b => BROWSER_MAP[b] || b); + const requestedBrowsers = ALLOWED_BROWSERS.filter(b => cliArgs?.[b]); this.#requestedBrowsers = requestedBrowsers.length && requestedBrowsers; } @@ -218,7 +218,7 @@ export class WTRConfig { return browsers.map((b) => playwrightLauncher({ concurrency: b === 'firefox' ? 1 : undefined, // focus in Firefox unreliable if concurrency > 1 (https://github.com/modernweb-dev/web/issues/238) - product: b, + product: BROWSER_MAP[b], createBrowserContext: ({ browser }) => browser.newContext({ deviceScaleFactor: 2, reducedMotion: 'reduce' }) })); } From 2f59a8a7c9e9399800cf2ba79aa2e8f7c1349fcb Mon Sep 17 00:00:00 2001 From: Danny Gleckler Date: Tue, 11 Jul 2023 13:55:19 -0400 Subject: [PATCH 03/43] Cleanup --- bin/test.js | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/bin/test.js b/bin/test.js index 44bfb556..5c60021e 100755 --- a/bin/test.js +++ b/bin/test.js @@ -165,7 +165,7 @@ export class WTRConfig { timeout, ...passthroughConfig } = {}) { - const { files, filter, golden, grep, group, manual, playwright, watch } = this.#cliArgs; + const { files, filter, golden, grep, group, manual, watch } = this.#cliArgs; delete passthroughConfig.browsers; @@ -225,20 +225,6 @@ export class WTRConfig { } -/* -function createConfig(...args) { - const wtrConfig = new WTRConfig(cliArgs); - return wtrConfig.create(...args); -} -*/ - -/* -export function getBrowsers(browsers) { - const wtrConfig = new WTRConfig(cliArgs); - return wtrConfig.getBrowsers(browsers); -} -*/ - const wtrConfig = new WTRConfig(cliArgs); const config = wtrConfig.create(testConfig); From 30d8541cfcaa9a8721c5533d6bf853444f088ae3 Mon Sep 17 00:00:00 2001 From: Danny Gleckler Date: Tue, 11 Jul 2023 14:17:26 -0400 Subject: [PATCH 04/43] Fix option handling --- bin/test.js | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/bin/test.js b/bin/test.js index 5c60021e..78309b65 100755 --- a/bin/test.js +++ b/bin/test.js @@ -10,10 +10,10 @@ import { visualDiffReporter } from '../src/server/visual-diff-reporter.js'; const optionDefinitions = [ // @web/test-runner options + { name: 'config', type: String }, { name: 'files', type: String, multiple: true }, { name: 'group', type: String, defaultOption: true }, { name: 'manual', type: Boolean }, - { name: 'config', type: String }, { name: 'watch', type: Boolean }, // custom options { name: 'chrome', type: Boolean }, @@ -21,8 +21,8 @@ const optionDefinitions = [ { name: 'firefox', type: Boolean }, { name: 'golden', type: Boolean }, { name: 'grep', alias: 'g', type: String }, - { name: 'timeout', type: Number }, { name: 'safari', type: Boolean }, + { name: 'timeout', type: Number }, ]; const cliArgs = commandLineArgs(optionDefinitions, { partial: true }); @@ -228,9 +228,16 @@ export class WTRConfig { const wtrConfig = new WTRConfig(cliArgs); const config = wtrConfig.create(testConfig); +const argv = [ + '--group', cliArgs.group, + ...(cliArgs._unknown || []) +]; +// copy cli-only wtr options back to argv to be processed +cliArgs.watch && argv.push('--watch'); +cliArgs.manual && argv.push('--manual'); + await startTestRunner({ - readCliArgs: true, - argv: [ '--group', cliArgs.group, ...(cliArgs._unknown || []) ], - readFileConfig: false, - config + argv, + config, + readFileConfig: false }); From abefd406cc772d66133cae49c865b7c56e4d5a65 Mon Sep 17 00:00:00 2001 From: Danny Gleckler Date: Tue, 11 Jul 2023 16:18:45 -0400 Subject: [PATCH 05/43] Move WTRConfig class back out of test binary --- bin/test.js | 198 +-------------------------------------- src/server/wtr-config.js | 195 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 197 insertions(+), 196 deletions(-) create mode 100644 src/server/wtr-config.js diff --git a/bin/test.js b/bin/test.js index 78309b65..1e6ad5fa 100755 --- a/bin/test.js +++ b/bin/test.js @@ -1,12 +1,8 @@ #!/usr/bin/env node - import { ConfigLoaderError, readConfig } from '@web/config-loader'; -import { defaultReporter, startTestRunner } from '@web/test-runner'; import commandLineArgs from 'command-line-args'; -import { headedMode } from '../src/server/headed-mode-plugin.js'; -import { playwrightLauncher } from '@web/test-runner-playwright'; -import { visualDiff } from '../src/server/visual-diff-plugin.js'; -import { visualDiffReporter } from '../src/server/visual-diff-reporter.js'; +import { startTestRunner } from '@web/test-runner'; +import { WTRConfig } from '../src/server/wtr-config.js'; const optionDefinitions = [ // @web/test-runner options @@ -35,196 +31,6 @@ const testConfig = await readConfig('d2l-test.config', cliArgs.config).catch(err } }) || {}; -//const DISALLOWED_ARGS = ['browsers', 'playwright', '_unknown']; -const DEFAULT_PATTERN = type => `./test/**/*.${type}.js`; -const ALLOWED_BROWSERS = ['chrome', 'firefox', 'safari']; -const BROWSER_MAP = { chrome: 'chromium', firefox: 'firefox', safari: 'webkit' }; - -export class WTRConfig { - - #cliArgs; - #requestedBrowsers; - - constructor(cliArgs) { - this.#cliArgs = cliArgs || {}; - this.#cliArgs.group ??= 'unit'; - const requestedBrowsers = ALLOWED_BROWSERS.filter(b => cliArgs?.[b]); - this.#requestedBrowsers = requestedBrowsers.length && requestedBrowsers; - } - - get #defaultConfig() { - return { - nodeResolve: true, - testRunnerHtml: testFramework => - ` - - - - - - `, - }; - } - - get visualDiffGroup() { - return { - name: 'vdiff', - files: this.#getPattern('vdiff'), - browsers: ['chrome'], - testRunnerHtml: testFramework => - ` - - - - - - - - - - ` - }; - } - - #getMochaConfig(timeoutConfig) { - const { - timeout = timeoutConfig, - grep, - watch, - manual - } = this.#cliArgs; - - if (typeof timeout !== 'undefined' && typeof timeout !== 'number') throw new TypeError('timeout must be a number'); - - const config = {}; - - if (timeout) config.timeout = String(timeout); - if (watch || manual) config.timeout = '0'; - if (grep) config.grep = grep; - - return Object.keys(config).length && { testFramework: { config } }; - } - - #getPattern(type) { - const pattern = [].concat(this.#cliArgs.files || this.pattern(type)); - - // replace filename wildcards with all filter strings - // e.g. If filter is ['button', 'list'], pattern './test/*.test.*' becomes: - // [ './test/+(*button*.test.*|*.test.*button*)', './test/+(*list*.test.*|*.test.*list*)' ] - if (this.#cliArgs.filter) { - return this.#cliArgs.filter.map(filterStr => { - // replace everything after the last forward slash - return pattern.map(p => p.replace(/[^/]*$/, fileGlob => { - // create a new glob for each wildcard - const fileGlobs = Array.from(fileGlob.matchAll(/(? { - const arr = fileGlob.split(''); - arr.splice(index, 1, filterStr); - return arr.join(''); - }); - return `+(${fileGlobs.join('|') || fileGlob})`; - })); - }).flat(); - } - return pattern; - } - - create({ - pattern = DEFAULT_PATTERN, - timeout, - ...passthroughConfig - } = {}) { - const { files, filter, golden, grep, group, manual, watch } = this.#cliArgs; - - delete passthroughConfig.browsers; - - if (typeof pattern !== 'function') throw new TypeError('pattern must be a function'); - this.pattern = pattern; - - const config = { - ...this.#defaultConfig, - ...this.#getMochaConfig(timeout), - ...passthroughConfig - }; - - config.groups ??= []; - config.groups.push({ - name: 'unit', - files: this.#getPattern('test') - }); - - if (group === 'vdiff') { - config.reporters ??= [ defaultReporter() ]; - config.reporters.push(visualDiffReporter({ reportResults: !golden })); - - config.plugins ??= []; - config.plugins.push(visualDiff({ updateGoldens: golden, runSubset: !!(filter || grep) })); - - config.groups.push(this.visualDiffGroup); - } - - // convert all browsers to playwright - config.groups.forEach(g => g.browsers = this.getBrowsers(g.browsers)); - - if (watch || manual) { - config.plugins ??= []; - const currentPattern = files || config.groups.find(g => g.name === group)?.files || config.files; - - config.plugins.push(headedMode({ - pattern: currentPattern, - manual, - watch - })); - } - - return config; - } - - getBrowsers(browsers) { - browsers = this.#requestedBrowsers || browsers || ALLOWED_BROWSERS; - - if (!Array.isArray(browsers)) throw new TypeError('browsers must be an array'); - - return browsers.map((b) => playwrightLauncher({ - concurrency: b === 'firefox' ? 1 : undefined, // focus in Firefox unreliable if concurrency > 1 (https://github.com/modernweb-dev/web/issues/238) - product: BROWSER_MAP[b], - createBrowserContext: ({ browser }) => browser.newContext({ deviceScaleFactor: 2, reducedMotion: 'reduce' }) - })); - } - -} - const wtrConfig = new WTRConfig(cliArgs); const config = wtrConfig.create(testConfig); diff --git a/src/server/wtr-config.js b/src/server/wtr-config.js new file mode 100644 index 00000000..44849382 --- /dev/null +++ b/src/server/wtr-config.js @@ -0,0 +1,195 @@ +import { defaultReporter } from '@web/test-runner'; +import { headedMode } from '../src/server/headed-mode-plugin.js'; +import { playwrightLauncher } from '@web/test-runner-playwright'; +import { visualDiff } from '../src/server/visual-diff-plugin.js'; +import { visualDiffReporter } from '../src/server/visual-diff-reporter.js'; + +//const DISALLOWED_ARGS = ['browsers', 'playwright', '_unknown']; +const DEFAULT_PATTERN = type => `./test/**/*.${type}.js`; +const ALLOWED_BROWSERS = ['chrome', 'firefox', 'safari']; +const BROWSER_MAP = { chrome: 'chromium', firefox: 'firefox', safari: 'webkit' }; + +export class WTRConfig { + + #cliArgs; + #requestedBrowsers; + + constructor(cliArgs) { + this.#cliArgs = cliArgs || {}; + this.#cliArgs.group ??= 'unit'; + const requestedBrowsers = ALLOWED_BROWSERS.filter(b => cliArgs?.[b]); + this.#requestedBrowsers = requestedBrowsers.length && requestedBrowsers; + } + + get #defaultConfig() { + return { + nodeResolve: true, + testRunnerHtml: testFramework => + ` + + + + + + `, + }; + } + + get visualDiffGroup() { + return { + name: 'vdiff', + files: this.#getPattern('vdiff'), + browsers: ['chrome'], + testRunnerHtml: testFramework => + ` + + + + + + + + + + ` + }; + } + + #getMochaConfig(timeoutConfig) { + const { + timeout = timeoutConfig, + grep, + watch, + manual + } = this.#cliArgs; + + if (typeof timeout !== 'undefined' && typeof timeout !== 'number') throw new TypeError('timeout must be a number'); + + const config = {}; + + if (timeout) config.timeout = String(timeout); + if (watch || manual) config.timeout = '0'; + if (grep) config.grep = grep; + + return Object.keys(config).length && { testFramework: { config } }; + } + + #getPattern(type) { + const pattern = [].concat(this.#cliArgs.files || this.pattern(type)); + + // replace filename wildcards with all filter strings + // e.g. If filter is ['button', 'list'], pattern './test/*.test.*' becomes: + // [ './test/+(*button*.test.*|*.test.*button*)', './test/+(*list*.test.*|*.test.*list*)' ] + if (this.#cliArgs.filter) { + return this.#cliArgs.filter.map(filterStr => { + // replace everything after the last forward slash + return pattern.map(p => p.replace(/[^/]*$/, fileGlob => { + // create a new glob for each wildcard + const fileGlobs = Array.from(fileGlob.matchAll(/(? { + const arr = fileGlob.split(''); + arr.splice(index, 1, filterStr); + return arr.join(''); + }); + return `+(${fileGlobs.join('|') || fileGlob})`; + })); + }).flat(); + } + return pattern; + } + + create({ + pattern = DEFAULT_PATTERN, + timeout, + ...passthroughConfig + } = {}) { + const { files, filter, golden, grep, group, manual, watch } = this.#cliArgs; + + delete passthroughConfig.browsers; + + if (typeof pattern !== 'function') throw new TypeError('pattern must be a function'); + this.pattern = pattern; + + const config = { + ...this.#defaultConfig, + ...this.#getMochaConfig(timeout), + ...passthroughConfig + }; + + config.groups ??= []; + config.groups.push({ + name: 'unit', + files: this.#getPattern('test') + }); + + if (group === 'vdiff') { + config.reporters ??= [ defaultReporter() ]; + config.reporters.push(visualDiffReporter({ reportResults: !golden })); + + config.plugins ??= []; + config.plugins.push(visualDiff({ updateGoldens: golden, runSubset: !!(filter || grep) })); + + config.groups.push(this.visualDiffGroup); + } + + // convert all browsers to playwright + config.groups.forEach(g => g.browsers = this.getBrowsers(g.browsers)); + + if (watch || manual) { + config.plugins ??= []; + const currentPattern = files || config.groups.find(g => g.name === group)?.files || config.files; + + config.plugins.push(headedMode({ + pattern: currentPattern, + manual, + watch + })); + } + + return config; + } + + getBrowsers(browsers) { + browsers = this.#requestedBrowsers || browsers || ALLOWED_BROWSERS; + + if (!Array.isArray(browsers)) throw new TypeError('browsers must be an array'); + + return browsers.map((b) => playwrightLauncher({ + concurrency: b === 'firefox' ? 1 : undefined, // focus in Firefox unreliable if concurrency > 1 (https://github.com/modernweb-dev/web/issues/238) + product: BROWSER_MAP[b], + createBrowserContext: ({ browser }) => browser.newContext({ deviceScaleFactor: 2, reducedMotion: 'reduce' }) + })); + } + +} From 952140d313c065d4da07a69da8d961a605c3383e Mon Sep 17 00:00:00 2001 From: Danny Gleckler Date: Tue, 11 Jul 2023 16:19:20 -0400 Subject: [PATCH 06/43] Add option aliases --- bin/test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/test.js b/bin/test.js index 1e6ad5fa..df4244fc 100755 --- a/bin/test.js +++ b/bin/test.js @@ -6,19 +6,19 @@ import { WTRConfig } from '../src/server/wtr-config.js'; const optionDefinitions = [ // @web/test-runner options - { name: 'config', type: String }, { name: 'files', type: String, multiple: true }, { name: 'group', type: String, defaultOption: true }, { name: 'manual', type: Boolean }, { name: 'watch', type: Boolean }, // custom options { name: 'chrome', type: Boolean }, + { name: 'config', alias: 'c', type: String }, // disabled for wtr { name: 'filter', alias: 'f', type: String, multiple: true }, { name: 'firefox', type: Boolean }, { name: 'golden', type: Boolean }, { name: 'grep', alias: 'g', type: String }, { name: 'safari', type: Boolean }, - { name: 'timeout', type: Number }, + { name: 'timeout', alias: 't', type: Number }, ]; const cliArgs = commandLineArgs(optionDefinitions, { partial: true }); From 9261e5a8c62b329a7dbac97f37e3ba1c23437421 Mon Sep 17 00:00:00 2001 From: Danny Gleckler Date: Tue, 11 Jul 2023 16:28:24 -0400 Subject: [PATCH 07/43] Fix imports --- src/server/wtr-config.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/server/wtr-config.js b/src/server/wtr-config.js index 44849382..a3f796ca 100644 --- a/src/server/wtr-config.js +++ b/src/server/wtr-config.js @@ -1,8 +1,8 @@ import { defaultReporter } from '@web/test-runner'; -import { headedMode } from '../src/server/headed-mode-plugin.js'; +import { headedMode } from './headed-mode-plugin.js'; import { playwrightLauncher } from '@web/test-runner-playwright'; -import { visualDiff } from '../src/server/visual-diff-plugin.js'; -import { visualDiffReporter } from '../src/server/visual-diff-reporter.js'; +import { visualDiff } from './visual-diff-plugin.js'; +import { visualDiffReporter } from './visual-diff-reporter.js'; //const DISALLOWED_ARGS = ['browsers', 'playwright', '_unknown']; const DEFAULT_PATTERN = type => `./test/**/*.${type}.js`; From 8d3ed900c965eac0d840193e31499049d47331b9 Mon Sep 17 00:00:00 2001 From: Danny Gleckler Date: Tue, 11 Jul 2023 16:28:49 -0400 Subject: [PATCH 08/43] Delete server/index.js --- package.json | 3 +-- src/server/index.js | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) delete mode 100644 src/server/index.js diff --git a/package.json b/package.json index 098f32e8..fbd56f46 100644 --- a/package.json +++ b/package.json @@ -33,8 +33,7 @@ "sinon": "^15" }, "exports": { - ".": "./src/browser/index.js", - "./wtr-config.js": "./src/server/index.js" + ".": "./src/browser/index.js" }, "files": [ "/src" diff --git a/src/server/index.js b/src/server/index.js deleted file mode 100644 index 3462b1c5..00000000 --- a/src/server/index.js +++ /dev/null @@ -1 +0,0 @@ -//export { getBrowsers } from './wtr-config.js'; From b1f6fafb63ce7052a68c0f0e8c61a962029638e1 Mon Sep 17 00:00:00 2001 From: Danny Gleckler Date: Tue, 11 Jul 2023 16:36:13 -0400 Subject: [PATCH 09/43] Remove HEY! --- src/server/visual-diff-plugin.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/server/visual-diff-plugin.js b/src/server/visual-diff-plugin.js index a1d1b4ca..be23ff51 100644 --- a/src/server/visual-diff-plugin.js +++ b/src/server/visual-diff-plugin.js @@ -89,7 +89,6 @@ async function tryMoveFile(srcFileName, destFileName) { await rename(srcFileName, destFileName); return true; } catch (e) { - console.log('HEY!'); console.warn(e); return false; } From 46404439687063ee62f6c59806b0c7b760a48f68 Mon Sep 17 00:00:00 2001 From: Danny Gleckler Date: Tue, 11 Jul 2023 22:28:37 -0400 Subject: [PATCH 10/43] Install config-loader --- package-lock.json | 1 + package.json | 1 + 2 files changed, 2 insertions(+) diff --git a/package-lock.json b/package-lock.json index aa04e756..ad24d993 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "license": "Apache-2.0", "dependencies": { "@open-wc/testing": "^3", + "@web/config-loader": "^0.2", "@web/test-runner": "^0.16", "@web/test-runner-commands": "^0.7", "@web/test-runner-playwright": "^0.10", diff --git a/package.json b/package.json index fbd56f46..f25ff0a1 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ }, "dependencies": { "@open-wc/testing": "^3", + "@web/config-loader": "^0.2", "@web/test-runner": "^0.16", "@web/test-runner-commands": "^0.7", "@web/test-runner-playwright": "^0.10", From aafbc4eb541d1fe1e856a56384f2e4a73a2c1efe Mon Sep 17 00:00:00 2001 From: Danny Gleckler Date: Tue, 11 Jul 2023 22:33:00 -0400 Subject: [PATCH 11/43] Remove options that wtr shouldn't use --- bin/test.js | 4 ++++ src/server/wtr-config.js | 1 - 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/bin/test.js b/bin/test.js index df4244fc..07b105c8 100755 --- a/bin/test.js +++ b/bin/test.js @@ -4,6 +4,8 @@ import commandLineArgs from 'command-line-args'; import { startTestRunner } from '@web/test-runner'; import { WTRConfig } from '../src/server/wtr-config.js'; +const DISALLOWED_OPTIONS = ['--browsers', '--playwright', '--puppeteer', '--groups']; + const optionDefinitions = [ // @web/test-runner options { name: 'files', type: String, multiple: true }, @@ -23,6 +25,8 @@ const optionDefinitions = [ const cliArgs = commandLineArgs(optionDefinitions, { partial: true }); +cliArgs._unknown = cliArgs._unknown?.filter(o => !DISALLOWED_OPTIONS.includes(o)); + const testConfig = await readConfig('d2l-test.config', cliArgs.config).catch(err => { if (err instanceof ConfigLoaderError) { throw new Error(err.message); diff --git a/src/server/wtr-config.js b/src/server/wtr-config.js index a3f796ca..214c2f7c 100644 --- a/src/server/wtr-config.js +++ b/src/server/wtr-config.js @@ -4,7 +4,6 @@ import { playwrightLauncher } from '@web/test-runner-playwright'; import { visualDiff } from './visual-diff-plugin.js'; import { visualDiffReporter } from './visual-diff-reporter.js'; -//const DISALLOWED_ARGS = ['browsers', 'playwright', '_unknown']; const DEFAULT_PATTERN = type => `./test/**/*.${type}.js`; const ALLOWED_BROWSERS = ['chrome', 'firefox', 'safari']; const BROWSER_MAP = { chrome: 'chromium', firefox: 'firefox', safari: 'webkit' }; From ebf8ff479b316b4bdc63f9f065b8adeeb3dec134 Mon Sep 17 00:00:00 2001 From: Danny Gleckler Date: Tue, 11 Jul 2023 22:34:03 -0400 Subject: [PATCH 12/43] Add --help --- bin/test.js | 22 ++++++++++++++++++++++ package-lock.json | 1 + package.json | 1 + 3 files changed, 24 insertions(+) diff --git a/bin/test.js b/bin/test.js index 07b105c8..d2e12d32 100755 --- a/bin/test.js +++ b/bin/test.js @@ -1,6 +1,8 @@ #!/usr/bin/env node import { ConfigLoaderError, readConfig } from '@web/config-loader'; import commandLineArgs from 'command-line-args'; +import commandLineUsage from 'command-line-usage'; +import process from 'node:process'; import { startTestRunner } from '@web/test-runner'; import { WTRConfig } from '../src/server/wtr-config.js'; @@ -19,12 +21,32 @@ const optionDefinitions = [ { name: 'firefox', type: Boolean }, { name: 'golden', type: Boolean }, { name: 'grep', alias: 'g', type: String }, + { name: 'help', alias: 'h', type: Boolean }, { name: 'safari', type: Boolean }, { name: 'timeout', alias: 't', type: Number }, ]; const cliArgs = commandLineArgs(optionDefinitions, { partial: true }); +if (cliArgs.help) { + const help = commandLineUsage([ + { + header: 'D2L Test', + content: 'Test runner for D2L components and applications' + }, + { + header: 'Usage', + content: 'd2l-test [options]', + }, + { + header: 'Options', + optionList: optionDefinitions + } + ]); + process.stdout.write(help); + process.exit(); +} + cliArgs._unknown = cliArgs._unknown?.filter(o => !DISALLOWED_OPTIONS.includes(o)); const testConfig = await readConfig('d2l-test.config', cliArgs.config).catch(err => { diff --git a/package-lock.json b/package-lock.json index ad24d993..7cac246f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "@web/test-runner-commands": "^0.7", "@web/test-runner-playwright": "^0.10", "command-line-args": "^5", + "command-line-usage": "^7", "glob": "^10", "pixelmatch": "^5", "pngjs": "^7" diff --git a/package.json b/package.json index f25ff0a1..5a0c5900 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "@web/test-runner-commands": "^0.7", "@web/test-runner-playwright": "^0.10", "command-line-args": "^5", + "command-line-usage": "^7", "glob": "^10", "pixelmatch": "^5", "pngjs": "^7" From 923b2f1f427fb5edad9582ae9cccc40398c24ca5 Mon Sep 17 00:00:00 2001 From: Danny Gleckler Date: Tue, 11 Jul 2023 22:37:10 -0400 Subject: [PATCH 13/43] Add unit group when used --- src/server/wtr-config.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/server/wtr-config.js b/src/server/wtr-config.js index 214c2f7c..ad196be2 100644 --- a/src/server/wtr-config.js +++ b/src/server/wtr-config.js @@ -22,6 +22,7 @@ export class WTRConfig { get #defaultConfig() { return { + groups: [], nodeResolve: true, testRunnerHtml: testFramework => ` @@ -146,13 +147,12 @@ export class WTRConfig { ...passthroughConfig }; - config.groups ??= []; - config.groups.push({ - name: 'unit', - files: this.#getPattern('test') - }); - - if (group === 'vdiff') { + if (group === 'unit') { + config.groups.push({ + name: 'unit', + files: this.#getPattern('test') + }); + } else if (group === 'vdiff') { config.reporters ??= [ defaultReporter() ]; config.reporters.push(visualDiffReporter({ reportResults: !golden })); From b7bb29f5a987616df26ed775a1cfa674519c8856 Mon Sep 17 00:00:00 2001 From: Danny Gleckler Date: Tue, 11 Jul 2023 22:37:31 -0400 Subject: [PATCH 14/43] Update filter comment --- src/server/wtr-config.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/server/wtr-config.js b/src/server/wtr-config.js index ad196be2..1c15e55e 100644 --- a/src/server/wtr-config.js +++ b/src/server/wtr-config.js @@ -110,8 +110,8 @@ export class WTRConfig { const pattern = [].concat(this.#cliArgs.files || this.pattern(type)); // replace filename wildcards with all filter strings - // e.g. If filter is ['button', 'list'], pattern './test/*.test.*' becomes: - // [ './test/+(*button*.test.*|*.test.*button*)', './test/+(*list*.test.*|*.test.*list*)' ] + // e.g. If filter is ['button', 'list*'], pattern './test/*.test.*' becomes: + // [ './test/+(button.test.*|*.test.button)', './test/+(list*.test.*|*.test.list*)' ] if (this.#cliArgs.filter) { return this.#cliArgs.filter.map(filterStr => { // replace everything after the last forward slash From 72bd0fa06ded9f9a7b6db3d9bc6206ca21aa3446 Mon Sep 17 00:00:00 2001 From: Danny Gleckler Date: Wed, 12 Jul 2023 00:49:08 -0400 Subject: [PATCH 15/43] Add --help details --- bin/test.js | 106 ++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 91 insertions(+), 15 deletions(-) diff --git a/bin/test.js b/bin/test.js index d2e12d32..cf379f58 100755 --- a/bin/test.js +++ b/bin/test.js @@ -10,20 +10,94 @@ const DISALLOWED_OPTIONS = ['--browsers', '--playwright', '--puppeteer', '--grou const optionDefinitions = [ // @web/test-runner options - { name: 'files', type: String, multiple: true }, - { name: 'group', type: String, defaultOption: true }, - { name: 'manual', type: Boolean }, - { name: 'watch', type: Boolean }, - // custom options - { name: 'chrome', type: Boolean }, - { name: 'config', alias: 'c', type: String }, // disabled for wtr - { name: 'filter', alias: 'f', type: String, multiple: true }, - { name: 'firefox', type: Boolean }, - { name: 'golden', type: Boolean }, - { name: 'grep', alias: 'g', type: String }, - { name: 'help', alias: 'h', type: Boolean }, - { name: 'safari', type: Boolean }, - { name: 'timeout', alias: 't', type: Number }, + { + name: 'files', + type: String, + multiple: true, + description: 'Test files to run. Path or glob.\n[Default: ./test/**/*..js]', + order: 8 + }, + { + name: 'group', + type: String, + required: true, + defaultOption: true, + description: 'Name of the group to run tests for\n[Default: unit]', + order: 1 + }, + { + name: 'manual', + type: Boolean, + description: 'Starts test runner in manual testing mode. Ignores browser options and prints manual testing URL.\n{underline Not compatible with automated browser interactions}\nConsider using --watch to debug in the browser instead', + order: 11 + }, + { + name: 'watch', + type: Boolean, + description: 'Reload tests on file changes. Allows debugging in all browsers.', + order: 9 + }, + + // d2l-test options + { + name: 'chrome', + type: Boolean, + description: 'Run tests in Chromium', + order: 2 + }, + { + name: 'config', + alias: 'c', + type: String, + description: 'Location to read config file from\n[Default: ./d2l-test.config.js]', + order: 9 + }, + { + name: 'filter', + alias: 'f', + type: String, + multiple: true, + description: 'Filter test files by replacing wildcards with this glob', + order: 6 + }, + { + name: 'firefox', + type: Boolean, + description: 'Run tests in Firefox', + order: 3 + }, + { + name: 'golden', + type: Boolean, + description: 'Generate new golden screenshots', + order: 10 + }, + { + name: 'grep', + alias: 'g', + type: String, + description: 'Only run tests matching this string or regexp', + order: 7 + }, + { + name: 'help', + type: Boolean, + description: 'Print usage information and exit', + order: 12 + }, + { + name: 'safari', + type: Boolean, + description: 'Run tests in Webkit', + order: 4 + }, + { + name: 'timeout', + alias: 't', + type: Number, + description: 'Test timeout threshold in ms\n[Default: 2000]', + order: 5 + }, ]; const cliArgs = commandLineArgs(optionDefinitions, { partial: true }); @@ -36,11 +110,13 @@ if (cliArgs.help) { }, { header: 'Usage', - content: 'd2l-test [options]', + content: 'd2l-test [options]', }, { header: 'Options', optionList: optionDefinitions + .map(o => (o.description += '\n') && o) + .sort((a, b) => (a.order > b.order ? 1 : -1)) } ]); process.stdout.write(help); From 29dcded3249207772e9b72c550a815bcf7e8f87d Mon Sep 17 00:00:00 2001 From: Danny Gleckler Date: Wed, 12 Jul 2023 00:49:39 -0400 Subject: [PATCH 16/43] Use proper node imports --- src/server/visual-diff-reporter.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/server/visual-diff-reporter.js b/src/server/visual-diff-reporter.js index 6c552f44..9f397199 100644 --- a/src/server/visual-diff-reporter.js +++ b/src/server/visual-diff-reporter.js @@ -1,8 +1,8 @@ -import { cpSync, mkdirSync, rmSync, writeFileSync } from 'fs'; -import { dirname, join } from 'path'; +import { cpSync, mkdirSync, rmSync, writeFileSync } from 'node:fs'; +import { dirname, join } from 'node:path'; import { getTestInfo, PATHS } from './visual-diff-plugin.js'; -import { execSync } from 'child_process'; -import { fileURLToPath } from 'url'; +import { execSync } from 'node:child_process'; +import { fileURLToPath } from 'node:url'; const __dirname = dirname(fileURLToPath(import.meta.url)); From b58e57f6a4b18143c90374a0662e0fa7db41ddf6 Mon Sep 17 00:00:00 2001 From: Danny Gleckler Date: Wed, 12 Jul 2023 10:26:35 -0400 Subject: [PATCH 17/43] Add support for filtering custom group files --- src/server/wtr-config.js | 42 ++++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/src/server/wtr-config.js b/src/server/wtr-config.js index 1c15e55e..c8513dff 100644 --- a/src/server/wtr-config.js +++ b/src/server/wtr-config.js @@ -87,6 +87,21 @@ export class WTRConfig { }; } + #filterFiles(files) { + return this.#cliArgs.filter.map(filterStr => { + // replace everything after the last forward slash + return files.map(f => f.replace(/[^/]*$/, fileGlob => { + // create a new glob for each wildcard + const fileGlobs = Array.from(fileGlob.matchAll(/(? { + const arr = fileGlob.split(''); + arr.splice(index, 1, filterStr); + return arr.join(''); + }); + return `+(${fileGlobs.join('|') || fileGlob})`; + })); + }).flat(); + } + #getMochaConfig(timeoutConfig) { const { timeout = timeoutConfig, @@ -107,26 +122,13 @@ export class WTRConfig { } #getPattern(type) { - const pattern = [].concat(this.#cliArgs.files || this.pattern(type)); + const files = this.#cliArgs.files || [ this.pattern(type) ]; - // replace filename wildcards with all filter strings - // e.g. If filter is ['button', 'list*'], pattern './test/*.test.*' becomes: - // [ './test/+(button.test.*|*.test.button)', './test/+(list*.test.*|*.test.list*)' ] if (this.#cliArgs.filter) { - return this.#cliArgs.filter.map(filterStr => { - // replace everything after the last forward slash - return pattern.map(p => p.replace(/[^/]*$/, fileGlob => { - // create a new glob for each wildcard - const fileGlobs = Array.from(fileGlob.matchAll(/(? { - const arr = fileGlob.split(''); - arr.splice(index, 1, filterStr); - return arr.join(''); - }); - return `+(${fileGlobs.join('|') || fileGlob})`; - })); - }).flat(); + return this.#filterFiles(files); } - return pattern; + + return files; } create({ @@ -147,6 +149,12 @@ export class WTRConfig { ...passthroughConfig }; + if (filter) { + config.groups.forEach(group => { + group.files = this.#filterFiles([ group.files ].flat()); + }); + } + if (group === 'unit') { config.groups.push({ name: 'unit', From 8bd61b29d6bd369787c4fed7bc8d056f638c368b Mon Sep 17 00:00:00 2001 From: Danny Gleckler Date: Wed, 12 Jul 2023 21:15:16 -0400 Subject: [PATCH 18/43] unit -> test --- bin/test.js | 4 ++-- src/server/wtr-config.js | 32 ++++++++++++++++---------------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/bin/test.js b/bin/test.js index cf379f58..041b4d51 100755 --- a/bin/test.js +++ b/bin/test.js @@ -14,7 +14,7 @@ const optionDefinitions = [ name: 'files', type: String, multiple: true, - description: 'Test files to run. Path or glob.\n[Default: ./test/**/*..js]', + description: 'Test files to run. Path or glob.\n[Default: ./test/**/*..js]', order: 8 }, { @@ -22,7 +22,7 @@ const optionDefinitions = [ type: String, required: true, defaultOption: true, - description: 'Name of the group to run tests for\n[Default: unit]', + description: 'Name of the group to run tests for\n[Default: test]', order: 1 }, { diff --git a/src/server/wtr-config.js b/src/server/wtr-config.js index c8513dff..c93d6c97 100644 --- a/src/server/wtr-config.js +++ b/src/server/wtr-config.js @@ -15,7 +15,7 @@ export class WTRConfig { constructor(cliArgs) { this.#cliArgs = cliArgs || {}; - this.#cliArgs.group ??= 'unit'; + this.#cliArgs.group ??= 'test'; const requestedBrowsers = ALLOWED_BROWSERS.filter(b => cliArgs?.[b]); this.#requestedBrowsers = requestedBrowsers.length && requestedBrowsers; } @@ -41,10 +41,20 @@ export class WTRConfig { }; } + get #pattern() { + const files = this.#cliArgs.files || [ this.pattern(this.#cliArgs.group) ]; + + if (this.#cliArgs.filter) { + return this.#filterFiles(files); + } + + return files; + } + get visualDiffGroup() { return { name: 'vdiff', - files: this.#getPattern('vdiff'), + files: this.#pattern, browsers: ['chrome'], testRunnerHtml: testFramework => ` @@ -121,16 +131,6 @@ export class WTRConfig { return Object.keys(config).length && { testFramework: { config } }; } - #getPattern(type) { - const files = this.#cliArgs.files || [ this.pattern(type) ]; - - if (this.#cliArgs.filter) { - return this.#filterFiles(files); - } - - return files; - } - create({ pattern = DEFAULT_PATTERN, timeout, @@ -155,10 +155,10 @@ export class WTRConfig { }); } - if (group === 'unit') { + if (group === 'test') { config.groups.push({ - name: 'unit', - files: this.#getPattern('test') + name: 'test', + files: this.#pattern }); } else if (group === 'vdiff') { config.reporters ??= [ defaultReporter() ]; @@ -175,7 +175,7 @@ export class WTRConfig { if (watch || manual) { config.plugins ??= []; - const currentPattern = files || config.groups.find(g => g.name === group)?.files || config.files; + const currentPattern = files || config.groups.find(g => g.name === group)?.files; config.plugins.push(headedMode({ pattern: currentPattern, From 732837630c3324581f1c99c843fe890b64e485b9 Mon Sep 17 00:00:00 2001 From: Danny Gleckler Date: Wed, 12 Jul 2023 21:26:27 -0400 Subject: [PATCH 19/43] Remove --manual; Add --open and --slowmo, fixture pausing --- bin/test.js | 19 +++++++++++-------- src/browser/fixture.js | 5 +++++ src/server/headed-mode-plugin.js | 26 +++++++++++++++++++++----- src/server/wtr-config.js | 19 ++++++++++++------- 4 files changed, 49 insertions(+), 20 deletions(-) diff --git a/bin/test.js b/bin/test.js index 041b4d51..0ebac4be 100755 --- a/bin/test.js +++ b/bin/test.js @@ -6,7 +6,7 @@ import process from 'node:process'; import { startTestRunner } from '@web/test-runner'; import { WTRConfig } from '../src/server/wtr-config.js'; -const DISALLOWED_OPTIONS = ['--browsers', '--playwright', '--puppeteer', '--groups']; +const DISALLOWED_OPTIONS = ['--browsers', '--playwright', '--puppeteer', '--groups', '--manual']; const optionDefinitions = [ // @web/test-runner options @@ -17,6 +17,16 @@ const optionDefinitions = [ description: 'Test files to run. Path or glob.\n[Default: ./test/**/*..js]', order: 8 }, + { + name: 'open', + type: Boolean, + description: 'Open the browser in headed mode' + }, + { + name: 'slowmo', + type: Number, + description: 'Slows down test operations by the specified number of milliseconds. Useful so that you can see what is going on.' + }, { name: 'group', type: String, @@ -25,12 +35,6 @@ const optionDefinitions = [ description: 'Name of the group to run tests for\n[Default: test]', order: 1 }, - { - name: 'manual', - type: Boolean, - description: 'Starts test runner in manual testing mode. Ignores browser options and prints manual testing URL.\n{underline Not compatible with automated browser interactions}\nConsider using --watch to debug in the browser instead', - order: 11 - }, { name: 'watch', type: Boolean, @@ -142,7 +146,6 @@ const argv = [ ]; // copy cli-only wtr options back to argv to be processed cliArgs.watch && argv.push('--watch'); -cliArgs.manual && argv.push('--manual'); await startTestRunner({ argv, diff --git a/src/browser/fixture.js b/src/browser/fixture.js index 4818e34f..dd971b65 100644 --- a/src/browser/fixture.js +++ b/src/browser/fixture.js @@ -48,8 +48,13 @@ async function waitForElem(elem, awaitLoadingComplete = true) { } export async function fixture(element, opts = {}) { + const pause = window.d2lTestPause; await Promise.all([reset(opts), document.fonts.ready]); const elem = await wcFixture(element); await waitForElem(elem, opts.awaitLoadingComplete); + await pause; + if (pause) { + window.d2lTestPause = new Promise(r => window.d2lTestPauseResolve = r); + } return elem; } diff --git a/src/server/headed-mode-plugin.js b/src/server/headed-mode-plugin.js index ec6912b1..de2c3cb6 100644 --- a/src/server/headed-mode-plugin.js +++ b/src/server/headed-mode-plugin.js @@ -1,16 +1,32 @@ import { globSync } from 'glob'; -export function headedMode({ manual, watch, pattern }) { +export function headedMode({ open, watch, pattern }) { const files = globSync(pattern, { ignore: 'node_modules/**', posix: true }); return { name: 'brightspace-headed-mode', async transform(context) { - if ((watch || manual) && files.includes(context.path.slice(1))) { - // allow time to open devtools in firefox and webkit - watch && await new Promise(r => setTimeout(r, 2000)); - return `debugger;\n${context.body}`; + if ((watch || open) && files.includes(context.path.slice(1))) { + return ` +pause(); +${context.body} +function play() { + document.querySelector('#play').disabled = true; + window.d2lTestPauseResolve(); +} +function pause() { + window.d2lTestPause = new Promise(r => window.d2lTestPauseResolve = r); + const controls = '
'; + document.documentElement.insertAdjacentHTML('afterBegin', controls); + const playBtn = document.querySelector('#play'); + playBtn.addEventListener('click', play); + beforeEach(function() { + document.querySelector('#test-name').innerText = this.currentTest.fullTitle(); + playBtn.disabled = false; + }); +} +`; } } }; diff --git a/src/server/wtr-config.js b/src/server/wtr-config.js index c93d6c97..896f003a 100644 --- a/src/server/wtr-config.js +++ b/src/server/wtr-config.js @@ -117,7 +117,7 @@ export class WTRConfig { timeout = timeoutConfig, grep, watch, - manual + open } = this.#cliArgs; if (typeof timeout !== 'undefined' && typeof timeout !== 'number') throw new TypeError('timeout must be a number'); @@ -125,7 +125,7 @@ export class WTRConfig { const config = {}; if (timeout) config.timeout = String(timeout); - if (watch || manual) config.timeout = '0'; + if (watch || open) config.timeout = '0'; if (grep) config.grep = grep; return Object.keys(config).length && { testFramework: { config } }; @@ -136,7 +136,7 @@ export class WTRConfig { timeout, ...passthroughConfig } = {}) { - const { files, filter, golden, grep, group, manual, watch } = this.#cliArgs; + const { files, filter, golden, grep, group, open, watch } = this.#cliArgs; delete passthroughConfig.browsers; @@ -173,13 +173,13 @@ export class WTRConfig { // convert all browsers to playwright config.groups.forEach(g => g.browsers = this.getBrowsers(g.browsers)); - if (watch || manual) { + if (watch || open) { config.plugins ??= []; const currentPattern = files || config.groups.find(g => g.name === group)?.files; config.plugins.push(headedMode({ pattern: currentPattern, - manual, + open, watch })); } @@ -193,9 +193,14 @@ export class WTRConfig { if (!Array.isArray(browsers)) throw new TypeError('browsers must be an array'); return browsers.map((b) => playwrightLauncher({ - concurrency: b === 'firefox' ? 1 : undefined, // focus in Firefox unreliable if concurrency > 1 (https://github.com/modernweb-dev/web/issues/238) + concurrency: b === 'firefox' || this.#cliArgs.open ? 1 : undefined, // focus in Firefox unreliable if concurrency > 1 (https://github.com/modernweb-dev/web/issues/238) product: BROWSER_MAP[b], - createBrowserContext: ({ browser }) => browser.newContext({ deviceScaleFactor: 2, reducedMotion: 'reduce' }) + createBrowserContext: ({ browser }) => browser.newContext({ deviceScaleFactor: 2, reducedMotion: 'reduce' }), + launchOptions: { + headless: !this.#cliArgs.open, + devtools: false, + slowMo: this.#cliArgs.slowmo || 0 + } })); } From 7ed8f75143d33a449f9949e3d07f2e88b55fd757 Mon Sep 17 00:00:00 2001 From: Danny Gleckler Date: Thu, 13 Jul 2023 00:21:23 -0400 Subject: [PATCH 20/43] Import pause script; Add start button; --- package.json | 3 ++- src/browser/fixture.js | 2 +- src/server/headed-mode-plugin.js | 23 +++-------------- src/server/pause.js | 42 ++++++++++++++++++++++++++++++++ src/server/wtr-config.js | 1 + 5 files changed, 50 insertions(+), 21 deletions(-) create mode 100644 src/server/pause.js diff --git a/package.json b/package.json index 5a0c5900..6fdcb03c 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,8 @@ "sinon": "^15" }, "exports": { - ".": "./src/browser/index.js" + ".": "./src/browser/index.js", + "./pause.js": "./src/server/pause.js" }, "files": [ "/src" diff --git a/src/browser/fixture.js b/src/browser/fixture.js index dd971b65..902e2a58 100644 --- a/src/browser/fixture.js +++ b/src/browser/fixture.js @@ -54,7 +54,7 @@ export async function fixture(element, opts = {}) { await waitForElem(elem, opts.awaitLoadingComplete); await pause; if (pause) { - window.d2lTestPause = new Promise(r => window.d2lTestPauseResolve = r); + window.d2lTestPause = new Promise(r => window.d2lTestRun = r); } return elem; } diff --git a/src/server/headed-mode-plugin.js b/src/server/headed-mode-plugin.js index de2c3cb6..684c5f8b 100644 --- a/src/server/headed-mode-plugin.js +++ b/src/server/headed-mode-plugin.js @@ -4,29 +4,14 @@ export function headedMode({ open, watch, pattern }) { const files = globSync(pattern, { ignore: 'node_modules/**', posix: true }); + const delay = ms => ms && new Promise(r => setTimeout(r, ms)); + return { name: 'brightspace-headed-mode', async transform(context) { if ((watch || open) && files.includes(context.path.slice(1))) { - return ` -pause(); -${context.body} -function play() { - document.querySelector('#play').disabled = true; - window.d2lTestPauseResolve(); -} -function pause() { - window.d2lTestPause = new Promise(r => window.d2lTestPauseResolve = r); - const controls = '
'; - document.documentElement.insertAdjacentHTML('afterBegin', controls); - const playBtn = document.querySelector('#play'); - playBtn.addEventListener('click', play); - beforeEach(function() { - document.querySelector('#test-name').innerText = this.currentTest.fullTitle(); - playBtn.disabled = false; - }); -} -`; + await delay(0); + return `import '@brightspace-ui/testing/pause.js';\ndebugger;\n${context.body}`; } } }; diff --git a/src/server/pause.js b/src/server/pause.js new file mode 100644 index 00000000..0293b784 --- /dev/null +++ b/src/server/pause.js @@ -0,0 +1,42 @@ +window.d2lTestPause = new Promise(r => window.d2lTestStart = r); + +const controls = ` +
+
+ + .${window.__WTR_CONFIG__.testFile.split('?')[0]} +
+ +
+`; + +document.documentElement.insertAdjacentHTML('afterBegin', controls); + +document.querySelector('#start-button').addEventListener('click', start); + +const runBtn = document.querySelector('#run-button'); +runBtn.addEventListener('click', run); + +const testName = document.querySelector('#test-name'); + +beforeEach(function() { // eslint-disable-line no-undef + testName.innerText = this.currentTest.fullTitle(); // eslint-disable-line no-invalid-this + runBtn.disabled = false; +}); + +function run() { + runBtn.disabled = true; + window.d2lTestRun(); +} + +function start() { + document.querySelector('#start').hidden = true; + document.querySelector('#run').hidden = false; + window.d2lTestStart(); +} + +await window.d2lTestPause; +window.d2lTestPause = new Promise(r => window.d2lTestRun = r); diff --git a/src/server/wtr-config.js b/src/server/wtr-config.js index 896f003a..25e42d05 100644 --- a/src/server/wtr-config.js +++ b/src/server/wtr-config.js @@ -174,6 +174,7 @@ export class WTRConfig { config.groups.forEach(g => g.browsers = this.getBrowsers(g.browsers)); if (watch || open) { + config.testsFinishTimeout = 600000; config.plugins ??= []; const currentPattern = files || config.groups.find(g => g.name === group)?.files; From 3b910f44dd7b8bd8ef771393d17b98d85afc7563 Mon Sep 17 00:00:00 2001 From: Danny Gleckler Date: Thu, 13 Jul 2023 08:52:54 -0400 Subject: [PATCH 21/43] Cleanup --- src/server/headed-mode-plugin.js | 2 +- src/server/pause.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/server/headed-mode-plugin.js b/src/server/headed-mode-plugin.js index 684c5f8b..f1fe755f 100644 --- a/src/server/headed-mode-plugin.js +++ b/src/server/headed-mode-plugin.js @@ -11,7 +11,7 @@ export function headedMode({ open, watch, pattern }) { async transform(context) { if ((watch || open) && files.includes(context.path.slice(1))) { await delay(0); - return `import '@brightspace-ui/testing/pause.js';\ndebugger;\n${context.body}`; + return `debugger;\nimport '@brightspace-ui/testing/pause.js';\n${context.body}`; } } }; diff --git a/src/server/pause.js b/src/server/pause.js index 0293b784..569144cd 100644 --- a/src/server/pause.js +++ b/src/server/pause.js @@ -3,11 +3,11 @@ window.d2lTestPause = new Promise(r => window.d2lTestStart = r); const controls = `
- + .${window.__WTR_CONFIG__.testFile.split('?')[0]}
From 734d22681f39a6b5ad4ab89386b6866694d243e9 Mon Sep 17 00:00:00 2001 From: Danny Gleckler Date: Thu, 13 Jul 2023 12:05:22 -0400 Subject: [PATCH 22/43] Cleanup --- src/browser/fixture.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/browser/fixture.js b/src/browser/fixture.js index 902e2a58..f7cbd7bd 100644 --- a/src/browser/fixture.js +++ b/src/browser/fixture.js @@ -48,13 +48,14 @@ async function waitForElem(elem, awaitLoadingComplete = true) { } export async function fixture(element, opts = {}) { - const pause = window.d2lTestPause; await Promise.all([reset(opts), document.fonts.ready]); const elem = await wcFixture(element); await waitForElem(elem, opts.awaitLoadingComplete); - await pause; - if (pause) { + + await window.d2lTestPause; + if (window.d2lTestPause) { window.d2lTestPause = new Promise(r => window.d2lTestRun = r); } + return elem; } From 54b09129625edc4b3b11170c7cfd140f6dc04254 Mon Sep 17 00:00:00 2001 From: Danny Gleckler Date: Thu, 13 Jul 2023 13:32:42 -0400 Subject: [PATCH 23/43] Import pause.js directly --- package.json | 3 +-- src/server/headed-mode-plugin.js | 5 ++++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 6fdcb03c..5a0c5900 100644 --- a/package.json +++ b/package.json @@ -33,8 +33,7 @@ "sinon": "^15" }, "exports": { - ".": "./src/browser/index.js", - "./pause.js": "./src/server/pause.js" + ".": "./src/browser/index.js" }, "files": [ "/src" diff --git a/src/server/headed-mode-plugin.js b/src/server/headed-mode-plugin.js index f1fe755f..c5dfdef3 100644 --- a/src/server/headed-mode-plugin.js +++ b/src/server/headed-mode-plugin.js @@ -1,3 +1,4 @@ +import { dirname, join } from 'node:path'; import { globSync } from 'glob'; export function headedMode({ open, watch, pattern }) { @@ -9,9 +10,11 @@ export function headedMode({ open, watch, pattern }) { return { name: 'brightspace-headed-mode', async transform(context) { + if ((watch || open) && files.includes(context.path.slice(1))) { await delay(0); - return `debugger;\nimport '@brightspace-ui/testing/pause.js';\n${context.body}`; + const pausePath = join(dirname(import.meta.url), 'pause.js').replace('file:', ''); + return `debugger;\nimport '${pausePath}'\n${context.body}`; } } }; From 07d2d49a116f03df128aae68ca2f7162622b3a88 Mon Sep 17 00:00:00 2001 From: Danny Gleckler Date: Thu, 13 Jul 2023 21:18:10 -0400 Subject: [PATCH 24/43] Rewrite --- src/browser/fixture.js | 17 ++++-- src/server/pause.js | 121 +++++++++++++++++++++++++++++++++++------ 2 files changed, 117 insertions(+), 21 deletions(-) diff --git a/src/browser/fixture.js b/src/browser/fixture.js index f7cbd7bd..b57f55c5 100644 --- a/src/browser/fixture.js +++ b/src/browser/fixture.js @@ -52,10 +52,17 @@ export async function fixture(element, opts = {}) { const elem = await wcFixture(element); await waitForElem(elem, opts.awaitLoadingComplete); - await window.d2lTestPause; - if (window.d2lTestPause) { - window.d2lTestPause = new Promise(r => window.d2lTestRun = r); - } - + await pause(); return elem; } + +async function pause() { + const test = window.d2lTest || {}; + + test.update?.(); + + if (test.pause) { + await test.pause; + if (test.pause) test.pause = new Promise(r => test.run = r); + } +} diff --git a/src/server/pause.js b/src/server/pause.js index 569144cd..803ac422 100644 --- a/src/server/pause.js +++ b/src/server/pause.js @@ -1,42 +1,131 @@ -window.d2lTestPause = new Promise(r => window.d2lTestStart = r); +const test = window.d2lTest = {}; +test.pause = new Promise(r => test.start = r); const controls = ` -
+ +
- .${window.__WTR_CONFIG__.testFile.split('?')[0]} +
`; -document.documentElement.insertAdjacentHTML('afterBegin', controls); +document.body.insertAdjacentHTML('afterBegin', controls); -document.querySelector('#start-button').addEventListener('click', start); +const startBtn = document.querySelector('#start-button'); +startBtn.addEventListener('click', start); + +const runAllBtn = document.querySelector('#run-all-button'); +runAllBtn.addEventListener('click', runAll); const runBtn = document.querySelector('#run-button'); runBtn.addEventListener('click', run); const testName = document.querySelector('#test-name'); -beforeEach(function() { // eslint-disable-line no-undef - testName.innerText = this.currentTest.fullTitle(); // eslint-disable-line no-invalid-this - runBtn.disabled = false; +beforeEach(async function() { // eslint-disable-line no-undef + const fixture = new Promise(r => test.update = r); + const currentTest = this.currentTest; // eslint-disable-line no-invalid-this + setTimeout(async() => { + await fixture; + testName.innerText = currentTest.fullTitle(); + if (test.pause) { + runBtn.disabled = false; + runBtn.focus(); + } + }); }); +function start() { + document.querySelector('#start').hidden = true; + document.querySelector('#run').hidden = false; + test.start(); +} + function run() { runBtn.disabled = true; - window.d2lTestRun(); + test.run(); } -function start() { - document.querySelector('#start').hidden = true; - document.querySelector('#run').hidden = false; - window.d2lTestStart(); +function runAll() { + runAllBtn.disabled = true; + test.pause = null; + run(); } -await window.d2lTestPause; -window.d2lTestPause = new Promise(r => window.d2lTestRun = r); +await test.pause; +test.pause = new Promise(r => test.run = r); From 64645e073c0fa49a9be472aae869e248e44c854c Mon Sep 17 00:00:00 2001 From: Danny Gleckler Date: Fri, 14 Jul 2023 00:31:41 -0400 Subject: [PATCH 25/43] Add skip buttons --- src/server/pause.js | 57 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 44 insertions(+), 13 deletions(-) diff --git a/src/server/pause.js b/src/server/pause.js index 803ac422..ae61ad99 100644 --- a/src/server/pause.js +++ b/src/server/pause.js @@ -3,11 +3,12 @@ test.pause = new Promise(r => test.start = r); const controls = ` @@ -75,16 +87,21 @@ const controls = `
.${window.__WTR_CONFIG__.testFile.split('?')[0]} +
`; -document.body.insertAdjacentHTML('afterBegin', controls); +document.body.insertAdjacentHTML('beforeBegin', controls); + +document.querySelector('#skip-button').addEventListener('click', skip); +document.querySelector('#skip-all-button').addEventListener('click', skipAll); const startBtn = document.querySelector('#start-button'); startBtn.addEventListener('click', start); @@ -97,9 +114,11 @@ runBtn.addEventListener('click', run); const testName = document.querySelector('#test-name'); +let currentTest; beforeEach(async function() { // eslint-disable-line no-undef const fixture = new Promise(r => test.update = r); - const currentTest = this.currentTest; // eslint-disable-line no-invalid-this + currentTest = this.currentTest; // eslint-disable-line no-invalid-this + if (test.skipAll) this.test.parent.ctx.skip(); // eslint-disable-line no-invalid-this setTimeout(async() => { await fixture; testName.innerText = currentTest.fullTitle(); @@ -127,5 +146,17 @@ function runAll() { run(); } +function skip() { + run(); + try { + currentTest.skip(); + } catch (e) { null; } +} + +function skipAll() { + test.skipAll = true; + test.start(); +} + await test.pause; test.pause = new Promise(r => test.run = r); From e84c10424fd9343c1685fc3fd14a130b39e22d85 Mon Sep 17 00:00:00 2001 From: Danny Gleckler Date: Fri, 14 Jul 2023 00:46:53 -0400 Subject: [PATCH 26/43] Remember focus element --- src/server/pause.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/server/pause.js b/src/server/pause.js index ae61ad99..44c1762f 100644 --- a/src/server/pause.js +++ b/src/server/pause.js @@ -100,9 +100,11 @@ const controls = ` document.body.insertAdjacentHTML('beforeBegin', controls); -document.querySelector('#skip-button').addEventListener('click', skip); document.querySelector('#skip-all-button').addEventListener('click', skipAll); +const skipBtn = document.querySelector('#skip-button'); +skipBtn.addEventListener('click', skip); + const startBtn = document.querySelector('#start-button'); startBtn.addEventListener('click', start); @@ -114,7 +116,7 @@ runBtn.addEventListener('click', run); const testName = document.querySelector('#test-name'); -let currentTest; +let currentTest, focusEl; beforeEach(async function() { // eslint-disable-line no-undef const fixture = new Promise(r => test.update = r); currentTest = this.currentTest; // eslint-disable-line no-invalid-this @@ -124,7 +126,7 @@ beforeEach(async function() { // eslint-disable-line no-undef testName.innerText = currentTest.fullTitle(); if (test.pause) { runBtn.disabled = false; - runBtn.focus(); + focusEl.focus(); } }); }); @@ -136,6 +138,7 @@ function start() { } function run() { + focusEl = runBtn; runBtn.disabled = true; test.run(); } @@ -148,6 +151,7 @@ function runAll() { function skip() { run(); + focusEl = skipBtn; try { currentTest.skip(); } catch (e) { null; } From b068b10d41afeaf3fcc74cc7192eaabecb96b232 Mon Sep 17 00:00:00 2001 From: Danny Gleckler Date: Fri, 14 Jul 2023 08:54:05 -0400 Subject: [PATCH 27/43] Tweaks --- src/server/pause.js | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/server/pause.js b/src/server/pause.js index 44c1762f..7f243e98 100644 --- a/src/server/pause.js +++ b/src/server/pause.js @@ -10,10 +10,10 @@ const controls = ` #d2l-test-controls #start, #d2l-test-controls #run { display: flex; - gap: 1em; - font-size: 18px; + gap: .65em; + font-size: 20px; font-family: 'Lato', sans-serif; - padding: 1em; + padding: .85em; background: #fff; color: #222; flex-wrap: wrap; @@ -22,7 +22,7 @@ const controls = ` } #d2l-test-controls #start { - font-size: 24px; + font-size: 22px; height: 600px; flex-direction: column; justify-content: center; @@ -31,6 +31,7 @@ const controls = ` } #d2l-test-controls #test-name { + font-size: .9em; flex: 1; } @@ -41,11 +42,11 @@ const controls = ` border-radius: .3em; border-style: none; font-weight: 700; - font-size: .9em; + font-size: .7em; margin: 0; min-height: calc(2em + 2px); outline: none; - padding: .55em 1.5em; + padding: calc(.55em * 1.43) calc(1.5em * 1.43); text-align: center; line-height: 1em; cursor: pointer; @@ -67,7 +68,8 @@ const controls = ` background-color: #e3e9f1; } - #d2l-test-controls button:focus { + #d2l-test-controls button:focus, + #d2l-test-controls button:hover { background-color: #cdd5dc; } @@ -75,7 +77,8 @@ const controls = ` box-shadow: 0 0 0 2px #fff, 0 0 0 4px #006fbf; } - #d2l-test-controls button.primary:focus { + #d2l-test-controls button.primary:focus, + #d2l-test-controls button.primary:hover { background-color: #004489; } @@ -86,8 +89,8 @@ const controls = `
.${window.__WTR_CONFIG__.testFile.split('?')[0]} - - + +
From 2be13d5046c965594df98ecb30c6a26e74f12e76 Mon Sep 17 00:00:00 2001 From: Danny Gleckler Date: Fri, 14 Jul 2023 20:03:58 -0400 Subject: [PATCH 30/43] Fix chrome headed screenshots --- src/browser/commands.js | 1 + src/browser/vdiff.js | 44 +++++++++++++++++++++++++++++++++++++++++ src/server/pause.js | 43 +++++++++++++++++++++++++++++++++------- 3 files changed, 81 insertions(+), 7 deletions(-) diff --git a/src/browser/commands.js b/src/browser/commands.js index 9ba41a45..900e1ae7 100644 --- a/src/browser/commands.js +++ b/src/browser/commands.js @@ -30,6 +30,7 @@ export async function focusElem(elem) { export async function hoverAt(x, y) { await sendMouse({ type: 'move', position: [x, y] }); + if (window.d2lTest) window.d2lTest.hovering = true; } export async function hoverElem(elem) { diff --git a/src/browser/vdiff.js b/src/browser/vdiff.js index ca704a55..3f8fd03e 100644 --- a/src/browser/vdiff.js +++ b/src/browser/vdiff.js @@ -15,6 +15,11 @@ mocha.setup({ // eslint-disable-line no-undef }); async function ScreenshotAndCompare(opts) { + + if (window.d2lTest) { + inlineStyles(this.elem); + } + const name = this.test.fullTitle(); const rect = this.elem.getBoundingClientRect(); let result = await executeServerCommand('brightspace-visual-diff-compare', { name, rect, opts }); @@ -22,7 +27,46 @@ async function ScreenshotAndCompare(opts) { this.test.timeout(0); result = await executeServerCommand('brightspace-visual-diff-compare-resize', { name }); } + + if (window.d2lTest) document.documentElement.classList.remove('screenshot'); + if (!result.pass) { expect.fail(result.message); } } + +const disallowedProps = ['width', 'inline-size']; +let count = 0; + +function inlineStyles(elem) { + // headed chrome takes screenshots by first moving the element, which + // breaks the hover state. So, copy currently styles inline before screenshot + if (window.d2lTest.hovering && window.chrome) { + count += 1; + document.documentElement.classList.add('screenshot'); + elem.classList.add(`__d2lTestHovering-${count}`); + + [...elem.children, ...elem.shadowRoot?.children ?? []].forEach(child => inlineStyles(child)); + + const computedStyle = getComputedStyle(elem); + [...computedStyle].forEach(prop => { + if (!disallowedProps.includes(prop)) { + elem.style[prop] = computedStyle.getPropertyValue(prop); + } + }); + + ['before', 'after'].forEach(pseudoEl => { + const computedStyle = getComputedStyle(elem, `::${pseudoEl}`); + if (computedStyle.content !== 'none') { + const sheet = new CSSStyleSheet(); + [...computedStyle].forEach(prop => { + if (!disallowedProps.includes(prop)) { + const value = computedStyle.getPropertyValue(prop); + sheet.insertRule(`.__d2lTestHovering-${count}::${pseudoEl} { ${prop}: ${value} !important; }`); + } + }); + elem.getRootNode().adoptedStyleSheets.push(sheet); + } + }); + } +} diff --git a/src/server/pause.js b/src/server/pause.js index 9511f44c..d363173f 100644 --- a/src/server/pause.js +++ b/src/server/pause.js @@ -3,6 +3,37 @@ test.pause = new Promise(r => test.start = r); const controls = `