From 83512caa9e7370b01fc7e24bb74a847252b38aa8 Mon Sep 17 00:00:00 2001 From: Maurycy Zarzycki Date: Tue, 17 Oct 2023 20:36:48 +0200 Subject: [PATCH] Huge JSDoc rewrite --- jsconfig.json | 8 ++ scripts/test.js | 47 +++++++++- tests-pw/NOTES.md | 11 ++- tests-pw/common/core/{Test.js => BaseTest.js} | 22 ++++- tests-pw/common/core/EditionSelector.js | 92 +++++++++++++++---- tests-pw/common/core/TiddlerStore.js | 51 +++++++++- tests-pw/common/core/TiddlyWikiConfig.js | 26 +++++- tests-pw/common/utils/BoundingBoxUtils.js | 8 +- tests-pw/common/utils/DialogUtils.js | 22 ++++- tests-pw/common/utils/FlowUtils.js | 15 --- tests-pw/common/utils/HtmlUtils.js | 17 ++++ tests-pw/common/utils/LocatorUtils.js | 23 ----- tests-pw/common/utils/OsUtils.js | 5 + tests-pw/common/utils/PageUtils.js | 14 ++- tests-pw/common/utils/PlaywrightTextUtils.js | 22 ----- tests-pw/global.d.ts | 26 ++++++ .../specs/AutoComplete/CodeMirror.spec.js | 14 +-- .../specs/AutoComplete/EditTiddler.spec.js | 4 +- .../specs/AutoComplete/FramedEditor.spec.js | 26 +++--- .../specs/AutoComplete/SidebarSearch.spec.js | 3 +- .../specs/AutoComplete/SimpleEditor.spec.js | 23 ++--- tests-pw/specs/AutoComplete/TextInput.spec.js | 23 ++--- ...eteFixtures.ts => AutoCompleteFixtures.js} | 11 ++- .../AutoComplete/_helpers/AutoCompleteTest.js | 25 ++++- .../_helpers/AutoCompleteUtils.js | 49 +++++----- .../specs/AutoComplete/_ui/AutoCompleteUi.js | 6 +- .../AutoComplete/_ui/AutoCompleteWindowUi.js | 5 +- tests-pw/specs/SanityTests.js | 2 +- 28 files changed, 422 insertions(+), 178 deletions(-) create mode 100644 jsconfig.json rename tests-pw/common/core/{Test.js => BaseTest.js} (50%) delete mode 100644 tests-pw/common/utils/FlowUtils.js create mode 100644 tests-pw/common/utils/HtmlUtils.js delete mode 100644 tests-pw/common/utils/LocatorUtils.js delete mode 100644 tests-pw/common/utils/PlaywrightTextUtils.js create mode 100644 tests-pw/global.d.ts rename tests-pw/specs/AutoComplete/_fixtures/{AutoCompleteFixtures.ts => AutoCompleteFixtures.js} (72%) diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 0000000..915bf68 --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "module": "ES2022", + "moduleResolution": "node", + "target": "ES2022" + }, + "exclude": ["node_modules", "**/node_modules/*"], +} diff --git a/scripts/test.js b/scripts/test.js index 5cfe596..1ce92e4 100644 --- a/scripts/test.js +++ b/scripts/test.js @@ -98,10 +98,51 @@ async function getTestsList() { } async function selectTest(tests) { + const inputToMatches = input => { + return input.split(' ').filter(empty).map(word => new RegExp(escapeRegexp(word), 'gi')); + }; + const prompt = new enquirer.AutoComplete({ message: 'Pick test to run', limit: 10, - choices: tests.map(test => ({ name: test.fullName, value: test.id })) + choices: tests.map(test => ({ name: test.fullName, value: test.id })), + suggest(typed, choices) { + if (!typed) { + return choices; + } + + const matches = inputToMatches(typed); + + return choices.filter(choice => { + const missingMatch = matches.findIndex(match => !match.test(choice.message)); + + return missingMatch === -1; + }); + }, + async render() { + if (this.state.status !== 'pending') { + return await enquirer.Select.prototype.render.call(this); + } + const hl = this.options.highlight || this.styles.complement; + + if (!this.input) { + return await enquirer.Select.prototype.render.call(this); + } else { + const matches = inputToMatches(this.input); + const style = message => { + for (const match of matches) { + message = message.replace(match, str => chalk.underline.red(str)); + } + + return message; + } + + const choices = this.choices; + this.choices = choices.map(ch => ({ ...ch, message: style(ch.message) })); + await enquirer.Select.prototype.render.call(this); + this.choices = choices; + } + } }); const result = await prompt.run(); @@ -111,7 +152,7 @@ async function selectTest(tests) { async function runTest(test, debugEnabled) { return new Promise((resolve, reject) => { - const safeTitle = test.titlePath.map(title => escapeRegex(title)).join(".+"); + const safeTitle = test.titlePath.map(title => escapeRegexp(title)).join(".+"); const pwArgs = [ 'playwright', 'test', @@ -147,6 +188,6 @@ async function runTest(test, debugEnabled) { }); } -function escapeRegex(string) { +function escapeRegexp(string) { return string.replace(/[/\-\\^$*+?.()|[\]{}]/g, '\\$&'); } \ No newline at end of file diff --git a/tests-pw/NOTES.md b/tests-pw/NOTES.md index 1c064d2..5cdc7ba 100644 --- a/tests-pw/NOTES.md +++ b/tests-pw/NOTES.md @@ -1,6 +1,13 @@ -# Note 001: +# Note#001: TiddlyWiki will not update $edit widget if it has focus even when the edited field changed. That's expected behavior, to avoid the widget from updating while typing in it. This is a bit of a bummer for our tests which involve things being open in another window, -so as a solution we force blur on the inputs. \ No newline at end of file +so as a solution we force blur on the inputs. + +# Note#002: + +Playwright fixture support works great when used with TypeScript but is a bit of a hell if +you try to use pure JS. The solution used to have proper code completion in VSCode +was taken from [this comment](https://github.com/microsoft/playwright/issues/7890#issuecomment-1369828521) +by **Andrew Hobson** on Playwright's GitHub issues. \ No newline at end of file diff --git a/tests-pw/common/core/Test.js b/tests-pw/common/core/BaseTest.js similarity index 50% rename from tests-pw/common/core/Test.js rename to tests-pw/common/core/BaseTest.js index 10160f3..e10fd56 100644 --- a/tests-pw/common/core/Test.js +++ b/tests-pw/common/core/BaseTest.js @@ -1,10 +1,24 @@ -import { test as baseTest } from '@playwright/test'; +// @ts-check +import * as base from '@playwright/test'; import { EditionSelector } from './EditionSelector'; import { TiddlyWikiUi } from '../ui/TiddlyWikiUi'; import { TiddlerStore } from './TiddlerStore'; import { TiddlyWikiConfig } from './TiddlyWikiConfig'; -export const test = baseTest.extend({ +// -------------------- +// NOTES.md#002 +// -------------------- + +/** + * @typedef {object} TiddlyWikiTestFixtures + * @property {TiddlerStore} store + * @property {TiddlyWikiConfig} twConfig + * @property {EditionSelector} selectEdition + * @property {TiddlyWikiUi} ui + */ + +/** @type {base.Fixtures} */ +export const baseTestFixtures = { store: async({page}, use) => { await use(new TiddlerStore(page)); }, @@ -17,4 +31,6 @@ export const test = baseTest.extend({ ui: async ({page}, use) => { await use(new TiddlyWikiUi(page)); } -}); +}; + +export const baseTest = base.test.extend(baseTestFixtures); \ No newline at end of file diff --git a/tests-pw/common/core/EditionSelector.js b/tests-pw/common/core/EditionSelector.js index 017ac91..81afdef 100644 --- a/tests-pw/common/core/EditionSelector.js +++ b/tests-pw/common/core/EditionSelector.js @@ -1,3 +1,4 @@ +// @ts-check import { sep, resolve } from 'path'; import { expect } from 'playwright/test'; @@ -5,27 +6,59 @@ const docsPath = resolve(`${process.cwd()}${sep}docs${sep}`); // Windows requires adding a slash at the start, while Linux already has it baked in const crossPlatformDocsPath = docsPath.replace(/^\/+/, ''); +/** + * @typedef {object} EditionEntry + * @property {string} suffix Suffix to add to the `index.html` file + * @property {string} version Version used for asserting the correct file was loaded + * @property {boolean} hasCodeMirror Whether this version includes code mirror + */ + +/** + * @type {Object.} + */ +const SUPPORTED_EDITIONS = { + tw522: getEdition('', '5.2.2', false), + tw522CodeMirror: getEdition('-cm', '5.2.2-CodeMirror', true), + tw530: getEdition('-530', '5.3.0', false), + tw530CodeMirror: getEdition('-530-cm', '5.3.0-CodeMirror', true), + tw531: getEdition('-531', '5.3.1', false), + tw531CodeMirror: getEdition('-531-cm', '5.3.1-CodeMirror', true), +}; + +/** + * Selects and initializes a specific version of Tiddly Wiki for testing + */ export class EditionSelector { + /** + * @param {import('playwright-core').Page} page + */ constructor(page) { this.page = page; } - tw522 = async page => this.#goto(page, '', '5.2.2'); - tw522CodeMirror = async page => this.#goto(page, '-cm', '5.2.2-CodeMirror'); - tw530 = async page => this.#goto(page, '-530', '5.3.0'); - tw530CodeMirror = async page => this.#goto(page, '-530-cm', '5.3.0-CodeMirror'); - tw531 = async page => this.#goto(page, '-531', '5.3.1'); - tw531CodeMirror = async page => this.#goto(page, '-531-cm', '5.3.1-CodeMirror'); + /** + * Initializes the given TiddlyWiki edition + * + * @param {string} editionName + * @param {import('playwright-core').Page} [page] + * @return {Promise} + */ + initByName = async (editionName, page = undefined) => { + const edition = SUPPORTED_EDITIONS[editionName]; - initByName = async (name, page) => { - if (typeof this[name] !== 'function') { - throw new Error(`Unsupported edition '${name}'`); + if (!edition) { + throw new Error(`Unsupported edition '${editionName}'`); } - await this[name](page); + await this.#init(page, edition.suffix, edition.version); } - #goto = async (page, suffix, expectedVersion) => { + /** + * @param {import('playwright-core').Page|undefined} page + * @param {string} suffix + * @param {string} expectedVersion + */ + #init = async (page, suffix, expectedVersion) => { page = page ?? this.page; await page.goto(`file:///${crossPlatformDocsPath}/index${suffix}.html`); @@ -33,14 +66,33 @@ export class EditionSelector { await expect(page.locator('[data-test-id="tw-edition"]')).toHaveText(expectedVersion, {timeout: 300}); }; - static getEditions(codeMirrorFilter) { - return [ - codeMirrorFilter !== true ? 'tw522' : null, - codeMirrorFilter !== false ? 'tw522CodeMirror' : null, - codeMirrorFilter !== true ? 'tw530' : null, - codeMirrorFilter !== false ? 'tw530CodeMirror' : null, - codeMirrorFilter !== true ? 'tw531' : null, - codeMirrorFilter !== false ? 'tw531CodeMirror' : null, - ].filter(x => x); + /** + * Returns a list of TW editions that can be passed to `initByName`. + * + * @param {boolean|undefined} codeMirrorFilter If set to true or false will respectively return editions that + * include Code Mirror or ones that don't. When undefined/left empty it returns all editions. + * + * @returns {string[]} + */ + static getEditions(codeMirrorFilter = undefined) { + return Object.keys(SUPPORTED_EDITIONS) + .filter(id => { + const edition = SUPPORTED_EDITIONS[id]; + + return codeMirrorFilter === undefined || edition.hasCodeMirror === codeMirrorFilter + }); } +} + +/** + * Utility to build a list of supported editions. + * + * @param {string} suffix + * @param {string} version + * @param {boolean} hasCodeMirror + * + * @returns {EditionEntry} + */ +function getEdition(suffix, version, hasCodeMirror) { + return {suffix, version, hasCodeMirror}; } \ No newline at end of file diff --git a/tests-pw/common/core/TiddlerStore.js b/tests-pw/common/core/TiddlerStore.js index c208716..6b44187 100644 --- a/tests-pw/common/core/TiddlerStore.js +++ b/tests-pw/common/core/TiddlerStore.js @@ -1,6 +1,13 @@ -import fs from 'fs'; -import {sep, resolve} from 'path'; +// @ts-check +/** + * @typedef {object} TiddlerStoreFixture + * @property {string} title + */ + +/** + * Class for interacting with browser's Tiddler storage, much faster than the UI. + */ export class TiddlerStore { /** * @param {import("@playwright/test").Page} page @@ -9,6 +16,11 @@ export class TiddlerStore { this.page = page; } + /** + * Create a store for a different page object + * @param {import("playwright-core").Page} page + * @returns {TiddlerStore} + */ forPage(page) { return new TiddlerStore(page); } @@ -23,18 +35,41 @@ export class TiddlerStore { }, tag); } + /** + * Adds a given fixture as a tiddler. It must be a JS object with at least one field called `title`. + *n + * @param {TiddlerStoreFixture} fixture + * @returns {Promise} Title of the loaded fixture + */ async loadFixture(fixture) { + if (!fixture) { + throw new Error("Attempted to load fixture but none was given"); + + } else if (typeof fixture !== 'object') { + throw new Error(`Attempted to load fixture but it was of type '${typeof fixture}' rather than 'object'`); + + } else if (!fixture.title) { + throw new Error("Attempted to load fixture but it did not have a `title` field"); + } + await this.loadFixtures([fixture]); return fixture.title; } + /** + * Adds multiple fixtures as a tiddler. It will graciously handle all errors. + * + * @see loadFixture + * @param {TiddlerStoreFixture[]} fixtures + * @returns {Promise} Titles of the loaded fixtures. + */ async loadFixtures(fixtures) { fixtures = Array.isArray(fixtures) ? fixtures : [fixtures]; this.page.evaluate(fixtures => { for (const fixture of fixtures) { - if (!fixture) { + if (!fixture || typeof fixture !== 'object' || !fixture.title) { continue; } @@ -45,8 +80,16 @@ export class TiddlerStore { return fixtures.map(fixture => fixture.title); } + /** + * Updates fields of an existing tiddler, optionally creating it if it doesn't exist. + * + * @param {string} title + * @param {Object.} fields + * @param {boolean} allowCreate + * @returns {Promise} + */ async updateTiddler(title, fields, allowCreate = false) { - return this.page.evaluate(({title, fields, allowCreate}) => { + await this.page.evaluate(({title, fields, allowCreate}) => { const tiddler = $tw.wiki.getTiddler(title) ?? (allowCreate ? new $tw.Tiddler({title}, fields) : false); diff --git a/tests-pw/common/core/TiddlyWikiConfig.js b/tests-pw/common/core/TiddlyWikiConfig.js index 06ffa25..2acffcb 100644 --- a/tests-pw/common/core/TiddlyWikiConfig.js +++ b/tests-pw/common/core/TiddlyWikiConfig.js @@ -1,10 +1,13 @@ -import fs from 'fs'; -import {sep, resolve} from 'path'; +// @ts-check import { TiddlerStore } from './TiddlerStore'; +/** + * Handy function to change certain configuration options in TiddlyWiki without having to pass + * tiddler names. + */ export class TiddlyWikiConfig { /** - * @param {import("@playwright/test").Page} page + * @param {import('playwright/test').Page} page * @param {TiddlerStore} store */ constructor(page, store) { @@ -12,15 +15,28 @@ export class TiddlyWikiConfig { this.store = store; } + /** + * Created a configurator for another page object. + * @param {import('playwright/test').Page} page + * @returns TiddlyWikiConfig + */ forPage(page) { return new TiddlyWikiConfig(page, this.store.forPage(page)); } + /** + * Controls whether framed editor is used or not. + * @param {boolean} bool + */ async useFramedEditor(bool) { - this.store.updateTiddler('$:/config/TextEditor/EnableToolbar', {text: bool ? 'yes' : 'no'}, true); + return this.store.updateTiddler('$:/config/TextEditor/EnableToolbar', {text: bool ? 'yes' : 'no'}, true); } + /** + * Controls whether Code Mirror's Auto Close Tags plugin is active + * @param {boolean} bool + */ async codeMirrorAutoCloseTags(bool) { - this.store.updateTiddler('$:/config/codemirror/autoCloseTags', {text: bool ? 'true' : 'false'}, true); + return this.store.updateTiddler('$:/config/codemirror/autoCloseTags', {text: bool ? 'true' : 'false'}, true); } } \ No newline at end of file diff --git a/tests-pw/common/utils/BoundingBoxUtils.js b/tests-pw/common/utils/BoundingBoxUtils.js index 53a4d89..dec0c1e 100644 --- a/tests-pw/common/utils/BoundingBoxUtils.js +++ b/tests-pw/common/utils/BoundingBoxUtils.js @@ -1,5 +1,11 @@ +// @ts-check - +/** + * Returns the distances between top-left corners of two bounding boxes. + * @param {BoundingBox} leftBB + * @param {BoundingBox} rightBB + * @returns {{x: number, y: number, distance: number}} + */ export function getBoundingBoxDistance(leftBB, rightBB) { return { x: Math.abs(rightBB.x - leftBB.x), diff --git a/tests-pw/common/utils/DialogUtils.js b/tests-pw/common/utils/DialogUtils.js index d72f9cc..cb20220 100644 --- a/tests-pw/common/utils/DialogUtils.js +++ b/tests-pw/common/utils/DialogUtils.js @@ -1,9 +1,23 @@ +// @ts-check - -export async function getDialogs(page, callback, onDialog = true) { +/** + * Runs page interactions (or whatever you put into callback), handles all dialogs that execute during that time + * and then returns the handled dialogs. + * + * @param {import("playwright/test").Page} page Page which should handle the dialogs + * @param {function():Promise} pageInteractionsCallback Function to call to invoke page interactions + * @param {boolean|function(import("playwright/test").Dialog): void} onDialog Default option to chose in the dialog + * (if it's bool) or a callback that will handle the dialog. + * + * @returns {Promise} + */ +export async function getDialogs(page, pageInteractionsCallback, onDialog = true) { let dialogs = []; + /** + * @param {import("playwright/test").Dialog} dialog + */ const internalDialogHandler = dialog => { - dialogs.push(dialog.message()); + dialogs.push(dialog); if (typeof onDialog === 'function') { onDialog(dialog); @@ -15,7 +29,7 @@ export async function getDialogs(page, callback, onDialog = true) { }; page.on('dialog', internalDialogHandler); - await callback(); + await pageInteractionsCallback(); page.off('dialog', internalDialogHandler); return dialogs; diff --git a/tests-pw/common/utils/FlowUtils.js b/tests-pw/common/utils/FlowUtils.js deleted file mode 100644 index b9a971e..0000000 --- a/tests-pw/common/utils/FlowUtils.js +++ /dev/null @@ -1,15 +0,0 @@ -export async function repeatUntilNotNull(attempts, callback, message) { - while (attempts-- > 0) { - const result = await callback(); - - if (result) { - return result; - } - } - - throw new Error( - message - ? `Repeat-Until has failed: ${message}` - : `Repeat-Until has failed.` - ); -} \ No newline at end of file diff --git a/tests-pw/common/utils/HtmlUtils.js b/tests-pw/common/utils/HtmlUtils.js new file mode 100644 index 0000000..90d1bd5 --- /dev/null +++ b/tests-pw/common/utils/HtmlUtils.js @@ -0,0 +1,17 @@ +// @ts-check + +/** + * Return input caret selection start/end values as a 2 element array. + * If the element does not have the relevant properties it returns 0 + * + * @param {import("playwright-core").Locator} locator + * @returns {Promise<{selectionStart: number, selectionEnd: number}>} + */ +export async function getInputSelection(locator) { + return locator.evaluate(element => { + return { + selectionStart: /** @type {any} */ (element).selectionStart ?? 0, + selectionEnd: /** @type {any} */ (element).selectionEnd ?? 0 + } + }); +} \ No newline at end of file diff --git a/tests-pw/common/utils/LocatorUtils.js b/tests-pw/common/utils/LocatorUtils.js deleted file mode 100644 index 62fdc94..0000000 --- a/tests-pw/common/utils/LocatorUtils.js +++ /dev/null @@ -1,23 +0,0 @@ -import { expect } from "playwright/test"; - - -export async function getDialogs(page, callback, onDialog = true) { - let dialogs = []; - const internalDialogHandler = dialog => { - dialogs.push(dialog.message()); - - if (typeof onDialog === 'function') { - onDialog(dialog); - } else if (dialog) { - dialog.accept(); - } else { - dialog.dismiss(); - } - }; - - page.on('dialog', internalDialogHandler); - await callback(); - page.off('dialog', internalDialogHandler); - - return dialogs; -} \ No newline at end of file diff --git a/tests-pw/common/utils/OsUtils.js b/tests-pw/common/utils/OsUtils.js index e4ded1f..2caa914 100644 --- a/tests-pw/common/utils/OsUtils.js +++ b/tests-pw/common/utils/OsUtils.js @@ -1,5 +1,10 @@ +// @ts-check + import Os from 'os' +/** + * @returns {boolean} True if the current OS is Windows + */ export function isWindows() { return Os.platform() === 'win32' } \ No newline at end of file diff --git a/tests-pw/common/utils/PageUtils.js b/tests-pw/common/utils/PageUtils.js index ce92e7a..fdcd7db 100644 --- a/tests-pw/common/utils/PageUtils.js +++ b/tests-pw/common/utils/PageUtils.js @@ -1,10 +1,20 @@ +// @ts-check + import test from "playwright/test"; -export async function getNewPage(page, callback) { +/** + * Calls a function that contains page interactions that should lead to a new page being open. + * It then returns the newly opened page. + * + * @param {import("playwright/test").Page} page + * @param {function():Promise} interactionCallback + * @returns {Promise} + */ +export async function getNewPage(page, interactionCallback) { return await test.step('Attempting to extract new page that is supposed to open', async () => { const waitForPagePromise = page.context().waitForEvent('page'); - await callback(); + await interactionCallback(); const newPage = await waitForPagePromise; await newPage.waitForLoadState(); diff --git a/tests-pw/common/utils/PlaywrightTextUtils.js b/tests-pw/common/utils/PlaywrightTextUtils.js deleted file mode 100644 index 114a40e..0000000 --- a/tests-pw/common/utils/PlaywrightTextUtils.js +++ /dev/null @@ -1,22 +0,0 @@ - - -/** - * Extract Text Content from a locator or return an empty string if it doesn't exist. - * - * The returned text content will be trimmed, unless the second argument is set to false. - * - * @param {import("playwright/test").Locator} locator - * @param {boolean} returnRaw - */ -export async function getTextContent(locator, returnRaw = false) { - try { - const text = (await locator.textContent()) ?? ""; - - return returnRaw - ? text - : text.trim(); - - } catch (e) { - return ""; - } -} \ No newline at end of file diff --git a/tests-pw/global.d.ts b/tests-pw/global.d.ts new file mode 100644 index 0000000..a6b4faa --- /dev/null +++ b/tests-pw/global.d.ts @@ -0,0 +1,26 @@ +class TW_Tiddler { + constructor(...fields: Record[]); +} + +interface TW_Wiki { + addTiddler(tiddler: TW_Tiddler): void; + getTiddler(title: string): TW_Tiddler + getTiddlersWithTag(tag: string): string[]; + deleteTiddler(title: string): void; +} + + +interface TW { + Tiddler: typeof TW_Tiddler; + wiki: TW_Wiki; +} + +declare interface BoundingBox { + x: number; + y: number; + width: number; + height: number; +} + + +declare const $tw: TW; \ No newline at end of file diff --git a/tests-pw/specs/AutoComplete/CodeMirror.spec.js b/tests-pw/specs/AutoComplete/CodeMirror.spec.js index a64d7cd..6f2ab0c 100644 --- a/tests-pw/specs/AutoComplete/CodeMirror.spec.js +++ b/tests-pw/specs/AutoComplete/CodeMirror.spec.js @@ -1,13 +1,12 @@ // @ts-check -import { test } from './_helpers/AutoCompleteTest'; +import { autoCompleteTest as test } from './_helpers/AutoCompleteTest'; import { EditionSelector } from '../../common/core/EditionSelector'; import { expect } from 'playwright/test'; -import { getNewPage } from '../../common/utils/PageUtils'; import { getBoundingBoxDistance } from '../../common/utils/BoundingBoxUtils'; EditionSelector.getEditions(true).forEach(edition => { - test(`${edition} -> Auto Complete -> Code Mirror -> Broad test`, async ({ page, selectEdition, store, ui, pluginUi, pluginUtils, fixtures, twConfig }) => { + test(`${edition} -> Auto Complete -> Code Mirror -> Broad test`, async ({ page, selectEdition, ui, pluginUi, pluginUtils, fixtures, twConfig }) => { await selectEdition.initByName(edition); await pluginUtils.initTriggers(fixtures.triggerSearchInTitle); await twConfig.useFramedEditor(true); @@ -156,10 +155,11 @@ EditionSelector.getEditions(true).forEach(edition => { }); }); - test(`${edition} -> Auto Complete -> Code Mirror -> Disable Auto Trigger`, async ({ page, selectEdition, store, ui, pluginUi, pluginUtils, fixtures, twConfig }) => { + test(`${edition} -> Auto Complete -> Code Mirror -> Disable Auto Trigger`, async ({ page, selectEdition, ui, pluginUi, pluginUtils, fixtures, twConfig }) => { await selectEdition.initByName(edition); await pluginUtils.initTriggers(fixtures.triggerSearchInTitle); - await pluginUtils.updateTrigger(1, {autoTriggerTextArea: 0}); + await page.pause(); + await pluginUtils.updateTrigger(1, {autoTriggerTextArea: false}); await twConfig.useFramedEditor(true); const { autoCompleteWindow } = pluginUi; @@ -177,7 +177,7 @@ EditionSelector.getEditions(true).forEach(edition => { }); }); - test(`${edition} -> Auto Complete -> Code Mirror -> Dialog position`, async ({ page, selectEdition, store, ui, pluginUi, pluginUtils, fixtures, twConfig }) => { + test(`${edition} -> Auto Complete -> Code Mirror -> Dialog position`, async ({ page, selectEdition, ui, pluginUi, pluginUtils, fixtures, twConfig }) => { await selectEdition.initByName(edition); await pluginUtils.initTriggers(fixtures.triggerSearchInTitle); await twConfig.useFramedEditor(true); @@ -197,7 +197,7 @@ EditionSelector.getEditions(true).forEach(edition => { await pluginUtils.assertDialogPosition("[[1", codeMirrorInputDiv, autoCompleteWindow.self); }); - test(`${edition} -> Auto Complete -> Code Mirror -> Not losing focus`, async ({ page, selectEdition, store, ui, pluginUi, pluginUtils, fixtures, twConfig }) => { + test(`${edition} -> Auto Complete -> Code Mirror -> Not losing focus`, async ({ selectEdition, ui, pluginUi, pluginUtils, fixtures, twConfig }) => { await selectEdition.initByName(edition); await pluginUtils.initTriggers(fixtures.triggerSearchInTitle); await twConfig.useFramedEditor(true); diff --git a/tests-pw/specs/AutoComplete/EditTiddler.spec.js b/tests-pw/specs/AutoComplete/EditTiddler.spec.js index e724310..03bfd4e 100644 --- a/tests-pw/specs/AutoComplete/EditTiddler.spec.js +++ b/tests-pw/specs/AutoComplete/EditTiddler.spec.js @@ -1,12 +1,12 @@ // @ts-check -import { test } from './_helpers/AutoCompleteTest'; +import { autoCompleteTest as test} from './_helpers/AutoCompleteTest'; import { EditionSelector } from '../../common/core/EditionSelector'; import { expect } from 'playwright/test'; import { getDialogs } from '../../common/utils/DialogUtils'; EditionSelector.getEditions(false).forEach(edition => { - test(`${edition} -> Auto Complete -> Edit Tiddler -> Cancel draft popup`, async ({ page, selectEdition, store, ui, twConfig, pluginUi, pluginUtils, fixtures }) => { + test(`${edition} -> Auto Complete -> Edit Tiddler -> Cancel draft popup`, async ({ page, selectEdition, ui, twConfig, pluginUtils, fixtures }) => { await selectEdition.initByName(edition); await pluginUtils.initTriggers(fixtures.triggerSearchInTitle); await twConfig.useFramedEditor(false); diff --git a/tests-pw/specs/AutoComplete/FramedEditor.spec.js b/tests-pw/specs/AutoComplete/FramedEditor.spec.js index 8150ba8..a35a0a9 100644 --- a/tests-pw/specs/AutoComplete/FramedEditor.spec.js +++ b/tests-pw/specs/AutoComplete/FramedEditor.spec.js @@ -1,11 +1,12 @@ // @ts-check -import { test } from './_helpers/AutoCompleteTest'; +import { autoCompleteTest as test } from './_helpers/AutoCompleteTest'; import { EditionSelector } from '../../common/core/EditionSelector'; import { expect } from 'playwright/test'; +import { getInputSelection } from '../../common/utils/HtmlUtils'; EditionSelector.getEditions(false).forEach(edition => { - test(`${edition} -> Auto Complete -> Framed Editor -> Broad test`, async ({ page, selectEdition, store, ui, pluginUi, pluginUtils, fixtures, twConfig }) => { + test(`${edition} -> Auto Complete -> Framed Editor -> Broad test`, async ({ page, selectEdition, ui, pluginUi, pluginUtils, fixtures, twConfig }) => { await selectEdition.initByName(edition); await pluginUtils.initTriggers(fixtures.triggerSearchInTitle); await twConfig.useFramedEditor(true); @@ -58,9 +59,10 @@ EditionSelector.getEditions(false).forEach(edition => { await expect(framedBodyTextArea, "Expected focus to not be lost on completion").toBeFocused(); await test.step("Validate caret position", async () => { - const [caretStart, caretEnd] = await framedBodyTextArea.evaluate(input => [input.selectionStart, input.selectionEnd]); - expect(caretStart, "Expected caret position to not be a selection").toEqual(caretEnd); - expect(caretStart, "Expected caret to be placed after the closing bracket").toEqual(4 + selectedText.length); + // @ts-ignore + const { selectionStart, selectionEnd } = await getInputSelection(framedBodyTextArea); + expect(selectionStart, "Expected caret position to not be a selection").toEqual(selectionEnd); + expect(selectionStart, "Expected caret to be placed after the closing bracket").toEqual(4 + selectedText.length); }); }); @@ -75,9 +77,9 @@ EditionSelector.getEditions(false).forEach(edition => { await expect(framedBodyTextArea, "Expected focus to not be lost on completion").toBeFocused(); await test.step("Validate caret position", async () => { - const [caretStart, caretEnd] = await framedBodyTextArea.evaluate(input => [input.selectionStart, input.selectionEnd]); - expect(caretStart, "Expected caret position to not be a selection").toEqual(caretEnd); - expect(caretStart, "Expected caret to be placed after the closing bracket").toEqual(4 + selectedText.length); + const { selectionStart, selectionEnd } = await getInputSelection(framedBodyTextArea); + expect(selectionStart, "Expected caret position to not be a selection").toEqual(selectionEnd); + expect(selectionStart, "Expected caret to be placed after the closing bracket").toEqual(4 + selectedText.length); }); }); @@ -94,9 +96,9 @@ EditionSelector.getEditions(false).forEach(edition => { await expect(framedBodyTextArea, "Expected focus to not be lost on completion").toBeFocused(); await test.step("Validate caret position", async () => { - const [caretStart, caretEnd] = await framedBodyTextArea.evaluate(input => [input.selectionStart, input.selectionEnd]); - expect(caretStart, "Expected caret position to not be a selection").toEqual(caretEnd); - expect(caretStart, "Expected caret to be placed after the closing bracket").toEqual(4 + selectedText.length); + const { selectionStart, selectionEnd } = await getInputSelection(framedBodyTextArea); + expect(selectionStart, "Expected caret position to not be a selection").toEqual(selectionEnd); + expect(selectionStart, "Expected caret to be placed after the closing bracket").toEqual(4 + selectedText.length); }); }); @@ -149,7 +151,7 @@ EditionSelector.getEditions(false).forEach(edition => { test(`${edition} -> Auto Complete -> Simple Editor -> Disable Auto Trigger`, async ({ page, selectEdition, store, ui, pluginUi, pluginUtils, fixtures, twConfig }) => { await selectEdition.initByName(edition); await pluginUtils.initTriggers(fixtures.triggerSearchInTitle); - await pluginUtils.updateTrigger(1, {autoTriggerTextArea: 0}); + await pluginUtils.updateTrigger(1, { autoTriggerTextArea: false }); await twConfig.useFramedEditor(true); const { autoCompleteWindow } = pluginUi; diff --git a/tests-pw/specs/AutoComplete/SidebarSearch.spec.js b/tests-pw/specs/AutoComplete/SidebarSearch.spec.js index ecf941a..468f044 100644 --- a/tests-pw/specs/AutoComplete/SidebarSearch.spec.js +++ b/tests-pw/specs/AutoComplete/SidebarSearch.spec.js @@ -1,7 +1,6 @@ // @ts-check -import { test } from './_helpers/AutoCompleteTest'; +import { autoCompleteTest as test } from './_helpers/AutoCompleteTest'; import { EditionSelector } from '../../common/core/EditionSelector'; -import { getTextContent } from '../../common/utils/PlaywrightTextUtils'; import { expect } from 'playwright/test'; diff --git a/tests-pw/specs/AutoComplete/SimpleEditor.spec.js b/tests-pw/specs/AutoComplete/SimpleEditor.spec.js index 104da9b..7c56b70 100644 --- a/tests-pw/specs/AutoComplete/SimpleEditor.spec.js +++ b/tests-pw/specs/AutoComplete/SimpleEditor.spec.js @@ -1,9 +1,10 @@ // @ts-check -import { test } from './_helpers/AutoCompleteTest'; +import { autoCompleteTest as test } from './_helpers/AutoCompleteTest'; import { EditionSelector } from '../../common/core/EditionSelector'; import { expect } from 'playwright/test'; import { getNewPage } from '../../common/utils/PageUtils'; +import { getInputSelection } from '../../common/utils/HtmlUtils'; EditionSelector.getEditions(false).forEach(edition => { test(`${edition} -> Auto Complete -> Simple Editor -> Broad test`, async ({ page, selectEdition, store, ui, pluginUi, pluginUtils, fixtures, twConfig }) => { @@ -59,9 +60,9 @@ EditionSelector.getEditions(false).forEach(edition => { await expect(unframedBodyTextArea, "Expected focus to not be lost on completion").toBeFocused(); await test.step("Validate caret position", async () => { - const [caretStart, caretEnd] = await unframedBodyTextArea.evaluate(input => [input.selectionStart, input.selectionEnd]); - expect(caretStart, "Expected caret position to not be a selection").toEqual(caretEnd); - expect(caretStart, "Expected caret to be placed after the closing bracket").toEqual(4 + selectedText.length); + const {selectionStart, selectionEnd} = await getInputSelection(unframedBodyTextArea); + expect(selectionStart, "Expected caret position to not be a selection").toEqual(selectionEnd); + expect(selectionStart, "Expected caret to be placed after the closing bracket").toEqual(4 + selectedText.length); }); }); @@ -76,9 +77,9 @@ EditionSelector.getEditions(false).forEach(edition => { await expect(unframedBodyTextArea, "Expected focus to not be lost on completion").toBeFocused(); await test.step("Validate caret position", async () => { - const [caretStart, caretEnd] = await unframedBodyTextArea.evaluate(input => [input.selectionStart, input.selectionEnd]); - expect(caretStart, "Expected caret position to not be a selection").toEqual(caretEnd); - expect(caretStart, "Expected caret to be placed after the closing bracket").toEqual(4 + selectedText.length); + const {selectionStart, selectionEnd} = await getInputSelection(unframedBodyTextArea); + expect(selectionStart, "Expected caret position to not be a selection").toEqual(selectionEnd); + expect(selectionStart, "Expected caret to be placed after the closing bracket").toEqual(4 + selectedText.length); }); }); @@ -95,9 +96,9 @@ EditionSelector.getEditions(false).forEach(edition => { await expect(unframedBodyTextArea, "Expected focus to not be lost on completion").toBeFocused(); await test.step("Validate caret position", async () => { - const [caretStart, caretEnd] = await unframedBodyTextArea.evaluate(input => [input.selectionStart, input.selectionEnd]); - expect(caretStart, "Expected caret position to not be a selection").toEqual(caretEnd); - expect(caretStart, "Expected caret to be placed after the closing bracket").toEqual(4 + selectedText.length); + const {selectionStart, selectionEnd} = await getInputSelection(unframedBodyTextArea); + expect(selectionStart, "Expected caret position to not be a selection").toEqual(selectionEnd); + expect(selectionStart, "Expected caret to be placed after the closing bracket").toEqual(4 + selectedText.length); }); }); @@ -150,7 +151,7 @@ EditionSelector.getEditions(false).forEach(edition => { test(`${edition} -> Auto Complete -> Simple Editor -> Disable Auto Trigger`, async ({ page, selectEdition, store, ui, pluginUi, pluginUtils, fixtures, twConfig }) => { await selectEdition.initByName(edition); await pluginUtils.initTriggers(fixtures.triggerSearchInTitle); - await pluginUtils.updateTrigger(1, { autoTriggerTextArea: 0 }); + await pluginUtils.updateTrigger(1, { autoTriggerTextArea: false }); await twConfig.useFramedEditor(false); const { autoCompleteWindow } = pluginUi; diff --git a/tests-pw/specs/AutoComplete/TextInput.spec.js b/tests-pw/specs/AutoComplete/TextInput.spec.js index f53a10a..0a4d28c 100644 --- a/tests-pw/specs/AutoComplete/TextInput.spec.js +++ b/tests-pw/specs/AutoComplete/TextInput.spec.js @@ -1,9 +1,10 @@ // @ts-check -import { test } from './_helpers/AutoCompleteTest'; +import { autoCompleteTest as test } from './_helpers/AutoCompleteTest'; import { EditionSelector } from '../../common/core/EditionSelector'; import { expect } from 'playwright/test'; import { getNewPage } from '../../common/utils/PageUtils'; +import { getInputSelection } from '../../common/utils/HtmlUtils'; EditionSelector.getEditions(false).forEach(edition => { test(`${edition} -> Auto Complete -> Text Input -> Broad test`, async ({ page, selectEdition, store, ui, pluginUi, pluginUtils, fixtures }) => { @@ -58,9 +59,9 @@ EditionSelector.getEditions(false).forEach(edition => { await expect(searchInput, "Expected focus to not be lost on completion").toBeFocused(); await test.step("Validate caret position", async () => { - const [caretStart, caretEnd] = await searchInput.evaluate(input => [input.selectionStart, input.selectionEnd]); - expect(caretStart, "Expected caret position to not be a selection").toEqual(caretEnd); - expect(caretStart, "Expected caret to be placed after the closing bracket").toEqual(4 + selectedText.length); + const {selectionStart, selectionEnd} = await getInputSelection(searchInput); + expect(selectionStart, "Expected caret position to not be a selection").toEqual(selectionEnd); + expect(selectionStart, "Expected caret to be placed after the closing bracket").toEqual(4 + selectedText.length); }); }); @@ -75,9 +76,9 @@ EditionSelector.getEditions(false).forEach(edition => { await expect(searchInput, "Expected focus to not be lost on completion").toBeFocused(); await test.step("Validate caret position", async () => { - const [caretStart, caretEnd] = await searchInput.evaluate(input => [input.selectionStart, input.selectionEnd]); - expect(caretStart, "Expected caret position to not be a selection").toEqual(caretEnd); - expect(caretStart, "Expected caret to be placed after the closing bracket").toEqual(4 + selectedText.length); + const {selectionStart, selectionEnd} = await getInputSelection(searchInput); + expect(selectionStart, "Expected caret position to not be a selection").toEqual(selectionEnd); + expect(selectionStart, "Expected caret to be placed after the closing bracket").toEqual(4 + selectedText.length); }); }); @@ -94,9 +95,9 @@ EditionSelector.getEditions(false).forEach(edition => { await expect(searchInput, "Expected focus to not be lost on completion").toBeFocused(); await test.step("Validate caret position", async () => { - const [caretStart, caretEnd] = await searchInput.evaluate(input => [input.selectionStart, input.selectionEnd]); - expect(caretStart, "Expected caret position to not be a selection").toEqual(caretEnd); - expect(caretStart, "Expected caret to be placed after the closing bracket").toEqual(4 + selectedText.length); + const {selectionStart, selectionEnd} = await getInputSelection(searchInput); + expect(selectionStart, "Expected caret position to not be a selection").toEqual(selectionEnd); + expect(selectionStart, "Expected caret to be placed after the closing bracket").toEqual(4 + selectedText.length); }); }); @@ -149,7 +150,7 @@ EditionSelector.getEditions(false).forEach(edition => { test(`${edition} -> Auto Complete -> Text Input -> Disable Auto Trigger`, async ({ page, selectEdition, store, ui, pluginUi, pluginUtils, fixtures }) => { await selectEdition.initByName(edition); await pluginUtils.initTriggers(fixtures.triggerSearchInTitle); - await pluginUtils.updateTrigger(1, {autoTriggerInput: 0}); + await pluginUtils.updateTrigger(1, {autoTriggerInput: false}); const { autoCompleteWindow } = pluginUi; const { searchInput } = ui.sidebar; diff --git a/tests-pw/specs/AutoComplete/_fixtures/AutoCompleteFixtures.ts b/tests-pw/specs/AutoComplete/_fixtures/AutoCompleteFixtures.js similarity index 72% rename from tests-pw/specs/AutoComplete/_fixtures/AutoCompleteFixtures.ts rename to tests-pw/specs/AutoComplete/_fixtures/AutoCompleteFixtures.js index 965e4c4..d679270 100644 --- a/tests-pw/specs/AutoComplete/_fixtures/AutoCompleteFixtures.ts +++ b/tests-pw/specs/AutoComplete/_fixtures/AutoCompleteFixtures.js @@ -1,4 +1,4 @@ - +// @ts-check import { resolve, dirname } from 'path'; import { readFileSync } from 'fs'; import { isWindows } from '../../../common/utils/OsUtils'; @@ -7,18 +7,27 @@ const filename = import.meta.url.replace(/^file:\/+/, isWindows() ? '' : '/'); const fixturesDir = resolve(dirname(filename)); export class AutoCompleteFixtures { + /** + * @return {import('../../../common/core/TiddlerStore').TiddlerStoreFixture} + */ get triggerSearchInTitle() { const json = readFileSync(resolve(fixturesDir, 'trigger-searchInTitle.json'), 'utf-8'); return JSON.parse(json); } + /** + * @return {import('../../../common/core/TiddlerStore').TiddlerStoreFixture} + */ get tiddlerEditInput() { const json = readFileSync(resolve(fixturesDir, 'tiddler-edit-input.json'), 'utf-8'); return JSON.parse(json); } + /** + * @return {import('../../../common/core/TiddlerStore').TiddlerStoreFixture} + */ get tiddlerEditTextArea() { const json = readFileSync(resolve(fixturesDir, 'tiddler-edit-textarea.json'), 'utf-8'); diff --git a/tests-pw/specs/AutoComplete/_helpers/AutoCompleteTest.js b/tests-pw/specs/AutoComplete/_helpers/AutoCompleteTest.js index 5276650..605daa5 100644 --- a/tests-pw/specs/AutoComplete/_helpers/AutoCompleteTest.js +++ b/tests-pw/specs/AutoComplete/_helpers/AutoCompleteTest.js @@ -1,9 +1,26 @@ -import { test as baseTest } from '../../../common/core/Test'; +// @ts-check +import * as base from '@playwright/test'; +import { baseTestFixtures } from '../../../common/core/BaseTest'; import { AutoCompleteFixtures } from '../_fixtures/AutoCompleteFixtures'; import { AutoCompleteUi } from '../_ui/AutoCompleteUi'; import { AutoCompleteUtils } from './AutoCompleteUtils'; -export const test = baseTest.extend({ +// -------------------- +// NOTES.md#002 +// -------------------- + +/** + * @typedef {import('../../../common/core/BaseTest').TiddlyWikiTestFixtures} TiddlyWikiTestFixtures + * @typedef {object} AutoCompleteTest + * @property {AutoCompleteFixtures} fixtures + * @property {AutoCompleteUi} pluginUi + * @property {AutoCompleteUtils} pluginUtils + */ + +/** @type {base.Fixtures} */ +const autoCompleteTestFixtures = { + ...baseTestFixtures, + fixtures: async ({}, use) => { await use(new AutoCompleteFixtures()); }, @@ -15,4 +32,6 @@ export const test = baseTest.extend({ pluginUtils: async ({store}, use) => { await use(new AutoCompleteUtils(store.page, store)); } -}); +}; + +export const autoCompleteTest = base.test.extend(autoCompleteTestFixtures); diff --git a/tests-pw/specs/AutoComplete/_helpers/AutoCompleteUtils.js b/tests-pw/specs/AutoComplete/_helpers/AutoCompleteUtils.js index ebf9814..efc15b1 100644 --- a/tests-pw/specs/AutoComplete/_helpers/AutoCompleteUtils.js +++ b/tests-pw/specs/AutoComplete/_helpers/AutoCompleteUtils.js @@ -1,8 +1,15 @@ +// @ts-check import { expect } from "playwright/test"; import { TiddlerStore } from "../../../common/core/TiddlerStore"; -import { test } from "./AutoCompleteTest"; +import { autoCompleteTest } from "./AutoCompleteTest"; import { getBoundingBoxDistance } from "../../../common/utils/BoundingBoxUtils"; +/** + * @typedef {object} AutoCompleteTriggerChanges + * @property {boolean} [autoTriggerInput] + * @property {boolean} [autoTriggerTextArea] + * @property {string} [trigger] + */ export class AutoCompleteUtils { /** @@ -34,22 +41,20 @@ export class AutoCompleteUtils { await this.store.loadFixtures(triggerTiddlers); } - async updateTrigger(triggerId, change) { + /** + * @param {string|number} triggerId + * @param {AutoCompleteTriggerChanges} changes + */ + async updateTrigger(triggerId, changes) { const fields = {}; - for (const [key, value] of Object.entries(change)) { - switch (key) { - case 'autoTriggerInput': - fields['auto-trigger-input'] = value ? '1' : '0'; - break; - case 'autoTriggerTextArea': - fields['auto-trigger-textarea'] = value ? '1' : '0'; - break; - case 'trigger': - fields['trigger'] = value; - break; - default: - throw new Error(`Failed to update trigger because it contains a change '${key}' that is not recognized.`); - } + if (changes.autoTriggerInput !== undefined) { + fields['auto-trigger-input'] = changes.autoTriggerInput ? '1' : '0'; + } + if (changes.autoTriggerTextArea !== undefined) { + fields['auto-trigger-textarea'] = changes.autoTriggerTextArea ? '1' : '0'; + } + if (changes.trigger !== undefined) { + fields['trigger'] = changes.trigger; } await this.store.updateTiddler(`$:/EvidentlyCube/Trigger/${triggerId}`, fields); @@ -57,8 +62,8 @@ export class AutoCompleteUtils { /** * Hacky way to determine that the Auto Complete dialog appears more or less where it should. - * Because we don't have a guaranteed way to get caret's position on the screen, nor want to hardcode numbers to protect - * the assertion from design changes we get creative. + * There is no guaranteed way to get caret's position on the screen, nor we want to hardcode numbers to protect + * the assertion from design changes. So we have to get creative. * * The assumption is that with the caret near the start of the input, the AC Dialog will appear near the input's * top-left corner. @@ -82,15 +87,15 @@ export class AutoCompleteUtils { inputLocator = inputLocator || dialogSourceLocator; - await test.step("Assert dialog position", async () => { - const textAreaBounds = await test.step('Retrieve source BBox', async () => dialogSourceLocator.boundingBox()); + await autoCompleteTest.step("Assert dialog position", async () => { + const textAreaBounds = await autoCompleteTest.step('Retrieve source BBox', async () => dialogSourceLocator.boundingBox()); expect(textAreaBounds, "Expected dialog source bounding box to be retrieved").not.toBeFalsy(); let lastBoundingBox = textAreaBounds; for (let i = 0; i < textInputs.length; i++) { const input = textInputs[i]; - const boundingBox = await test.step(`Retrieve dialog BBox for input #${i} '${input}'`, async () => { + const boundingBox = await autoCompleteTest.step(`Retrieve dialog BBox for input #${i} '${input}'`, async () => { // Dismiss completion as in some cases it can obscure the input enough for PW to choke if (await autoCompleteDialogLocator.isVisible()) { await autoCompleteDialogLocator.page().keyboard.press('Escape'); @@ -107,7 +112,7 @@ export class AutoCompleteUtils { expect(boundingBox, "Expected auto complete bounding box to be retrieved").not.toBeFalsy(); - await test.step(`Assert distance of input #${i} '${input}' from previous input is less than ${ALLOWED_AXIS_DISTANCE} in each dimension`, async () => { + await autoCompleteTest.step(`Assert distance of input #${i} '${input}' from previous input is less than ${ALLOWED_AXIS_DISTANCE} in each dimension`, async () => { const distance = getBoundingBoxDistance(lastBoundingBox, boundingBox); expect(distance.x, "Expected X distance to be under the threshold").toBeLessThan(ALLOWED_AXIS_DISTANCE); expect(distance.y, "Expected Y distance to be under the threshold").toBeLessThan(ALLOWED_AXIS_DISTANCE); diff --git a/tests-pw/specs/AutoComplete/_ui/AutoCompleteUi.js b/tests-pw/specs/AutoComplete/_ui/AutoCompleteUi.js index 7346534..cb79e20 100644 --- a/tests-pw/specs/AutoComplete/_ui/AutoCompleteUi.js +++ b/tests-pw/specs/AutoComplete/_ui/AutoCompleteUi.js @@ -1,7 +1,11 @@ -import { AutoCompleteWindowUi } from "./AutoCompleteWindowUi"; +// @ts-check +import { AutoCompleteWindowUi } from "./AutoCompleteWindowUi"; export class AutoCompleteUi { + /** + * @param {import("playwright/test").Page} page + */ constructor(page) { this.page = page; } diff --git a/tests-pw/specs/AutoComplete/_ui/AutoCompleteWindowUi.js b/tests-pw/specs/AutoComplete/_ui/AutoCompleteWindowUi.js index 905908a..b7818e9 100644 --- a/tests-pw/specs/AutoComplete/_ui/AutoCompleteWindowUi.js +++ b/tests-pw/specs/AutoComplete/_ui/AutoCompleteWindowUi.js @@ -1,6 +1,9 @@ - +// @ts-check export class AutoCompleteWindowUi { + /** + * @param {import("playwright/test").Page} page + */ constructor(page) { this.page = page; } diff --git a/tests-pw/specs/SanityTests.js b/tests-pw/specs/SanityTests.js index 94d442f..67d41e2 100644 --- a/tests-pw/specs/SanityTests.js +++ b/tests-pw/specs/SanityTests.js @@ -1,6 +1,6 @@ // @ts-check -import { test } from '../common/core/Test'; import { EditionSelector } from '../common/core/EditionSelector'; +import { baseTest as test } from '../common/core/BaseTest'; EditionSelector.getEditions().forEach(edition => { test.describe(`Sanity (${edition})`, async () => {