Skip to content

Commit 7be880a

Browse files
committed
chore: generate simple dom descriptions in codegen
1 parent 177576a commit 7be880a

File tree

13 files changed

+217
-108
lines changed

13 files changed

+217
-108
lines changed

packages/playwright-core/src/server/codegen/codeGenerator.ts

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export type FrameDescription = {
2727

2828
export type ActionInContext = {
2929
frame: FrameDescription;
30+
description?: string;
3031
action: Action;
3132
committed?: boolean;
3233
};

packages/playwright-core/src/server/codegen/javascript.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ export class JavaScriptLanguageGenerator implements LanguageGenerator {
6565
if (signals.download)
6666
formatter.add(`const download${signals.download.downloadAlias}Promise = ${pageAlias}.waitForEvent('download');`);
6767

68-
formatter.add(this._generateActionCall(subject, actionInContext));
68+
formatter.add(wrapWithStep(actionInContext.description, this._generateActionCall(subject, actionInContext)));
6969

7070
if (signals.popup)
7171
formatter.add(`const ${signals.popup.popupAlias} = await ${signals.popup.popupAlias}Promise;`);
@@ -259,3 +259,9 @@ export class JavaScriptFormatter {
259259
function quote(text: string) {
260260
return escapeWithQuotes(text, '\'');
261261
}
262+
263+
function wrapWithStep(description: string | undefined, body: string) {
264+
return description ? `await test.step(\`${description}\`, async () => {
265+
${body}
266+
});` : body;
267+
}

packages/playwright-core/src/server/frames.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -800,7 +800,7 @@ export class Frame extends SdkObject {
800800
const result = await resolved.injected.evaluateHandle((injected, { info, root }) => {
801801
const elements = injected.querySelectorAll(info.parsed, root || document);
802802
const element: Element | undefined = elements[0];
803-
const visible = element ? injected.isVisible(element) : false;
803+
const visible = element ? injected.utils.isElementVisible(element) : false;
804804
let log = '';
805805
if (elements.length > 1) {
806806
if (info.strict)
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,21 @@
1+
const path = require('path');
2+
13
module.exports = {
2-
rules: {
3-
"no-restricted-globals": [
4-
"error",
5-
{ "name": "window" },
6-
{ "name": "document" },
7-
{ "name": "globalThis" },
8-
]
9-
}
4+
parser: "@typescript-eslint/parser",
5+
plugins: ["@typescript-eslint", "notice"],
6+
parserOptions: {
7+
ecmaVersion: 9,
8+
sourceType: "module",
9+
project: path.join(__dirname, '../../../../../tsconfig.json'),
10+
},
11+
rules: {
12+
"no-restricted-globals": [
13+
"error",
14+
{ "name": "window" },
15+
{ "name": "document" },
16+
{ "name": "globalThis" },
17+
],
18+
'@typescript-eslint/no-floating-promises': 'error',
19+
"@typescript-eslint/no-unnecessary-boolean-literal-compare": 2,
20+
},
1021
};

packages/playwright-core/src/server/injected/clock.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ export class ClockController {
216216
const sinceLastSync = now - this._realTime!.lastSyncTicks;
217217
this._realTime!.lastSyncTicks = now;
218218
// eslint-disable-next-line no-console
219-
this._runTo(shiftTicks(this._now.ticks, sinceLastSync)).catch(e => console.error(e)).then(() => this._updateRealTimeTimer());
219+
void this._runTo(shiftTicks(this._now.ticks, sinceLastSync)).catch(e => console.error(e)).then(() => this._updateRealTimeTimer());
220220
}, callAt - this._now.ticks),
221221
};
222222
}

packages/playwright-core/src/server/injected/injectedScript.ts

+27-21
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,12 @@ import type { CSSComplexSelectorList } from '../../utils/isomorphic/cssParser';
2929
import { generateSelector, type GenerateSelectorOptions } from './selectorGenerator';
3030
import type * as channels from '@protocol/channels';
3131
import { Highlight } from './highlight';
32-
import { getChecked, getAriaDisabled, getAriaRole, getElementAccessibleName, getElementAccessibleDescription } from './roleUtils';
32+
import { getChecked, getAriaDisabled, getAriaRole, getElementAccessibleName, getElementAccessibleDescription, beginAriaCaches, endAriaCaches } from './roleUtils';
3333
import { kLayoutSelectorNames, type LayoutSelectorName, layoutSelectorScore } from './layoutSelectorUtils';
3434
import { asLocator } from '../../utils/isomorphic/locatorGenerators';
3535
import type { Language } from '../../utils/isomorphic/locatorGenerators';
36-
import { cacheNormalizedWhitespaces, normalizeWhiteSpace, trimStringWithEllipsis } from '../../utils/isomorphic/stringUtils';
36+
import { cacheNormalizedWhitespaces, escapeHTML, escapeHTMLAttribute, normalizeWhiteSpace, trimStringWithEllipsis } from '../../utils/isomorphic/stringUtils';
37+
import { generateSimpleDom, generateSimpleDomNode, selectorForSimpleDomNodeId } from './simpleDom';
3738

3839
export type FrameExpectParams = Omit<channels.FrameExpectParams, 'expectedValue'> & { expectedValue?: any };
3940

@@ -66,7 +67,28 @@ export class InjectedScript {
6667
// eslint-disable-next-line no-restricted-globals
6768
readonly window: Window & typeof globalThis;
6869
readonly document: Document;
69-
readonly utils = { isInsideScope, elementText, asLocator, normalizeWhiteSpace, cacheNormalizedWhitespaces };
70+
71+
// Recorder must use any external dependencies through InjectedScript.
72+
// Otherwise it will end up with a copy of all modules it uses, and any
73+
// module-level globals will be duplicated, which leads to subtle bugs.
74+
readonly utils = {
75+
asLocator,
76+
beginAriaCaches,
77+
cacheNormalizedWhitespaces,
78+
elementText,
79+
endAriaCaches,
80+
escapeHTML,
81+
escapeHTMLAttribute,
82+
generateSimpleDom: generateSimpleDom.bind(undefined, this),
83+
generateSimpleDomNode: generateSimpleDomNode.bind(undefined, this),
84+
getAriaRole,
85+
getElementAccessibleDescription,
86+
getElementAccessibleName,
87+
isElementVisible,
88+
isInsideScope,
89+
normalizeWhiteSpace,
90+
selectorForSimpleDomNodeId: selectorForSimpleDomNodeId.bind(undefined, this),
91+
};
7092

7193
// eslint-disable-next-line no-restricted-globals
7294
constructor(window: Window & typeof globalThis, isUnderTest: boolean, sdkLanguage: Language, testIdAttributeNameForStrictErrorAndConsoleCodegen: string, stableRafCount: number, browserName: string, customEngines: { name: string, engine: SelectorEngine }[]) {
@@ -426,10 +448,6 @@ export class InjectedScript {
426448
return new constrFunction(this, params);
427449
}
428450

429-
isVisible(element: Element): boolean {
430-
return isElementVisible(element);
431-
}
432-
433451
async viewportRatio(element: Element): Promise<number> {
434452
return await new Promise(resolve => {
435453
const observer = new IntersectionObserver(entries => {
@@ -567,9 +585,9 @@ export class InjectedScript {
567585
}
568586

569587
if (state === 'visible')
570-
return this.isVisible(element);
588+
return isElementVisible(element);
571589
if (state === 'hidden')
572-
return !this.isVisible(element);
590+
return !isElementVisible(element);
573591

574592
const disabled = getAriaDisabled(element);
575593
if (state === 'disabled')
@@ -1296,18 +1314,6 @@ export class InjectedScript {
12961314
}
12971315
throw this.createStacklessError('Unknown expect matcher: ' + expression);
12981316
}
1299-
1300-
getElementAccessibleName(element: Element, includeHidden?: boolean): string {
1301-
return getElementAccessibleName(element, !!includeHidden);
1302-
}
1303-
1304-
getElementAccessibleDescription(element: Element, includeHidden?: boolean): string {
1305-
return getElementAccessibleDescription(element, !!includeHidden);
1306-
}
1307-
1308-
getAriaRole(element: Element) {
1309-
return getAriaRole(element);
1310-
}
13111317
}
13121318

13131319
const autoClosingTags = new Set(['AREA', 'BASE', 'BR', 'COL', 'COMMAND', 'EMBED', 'HR', 'IMG', 'INPUT', 'KEYGEN', 'LINK', 'MENUITEM', 'META', 'PARAM', 'SOURCE', 'TRACK', 'WBR']);
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Recorder must use any external dependencies through InjectedScript.
1+
# Recorder must use any external dependencies through injectedScript.utils.
22
# Otherwise it will end up with a copy of all modules it uses, and any
33
# module-level globals will be duplicated, which leads to subtle bugs.
44
[*]

0 commit comments

Comments
 (0)