Skip to content

Commit a27f50d

Browse files
authored
chore: put "auto expect" codegen feature under a checkbox (#37038)
1 parent e722aa9 commit a27f50d

File tree

13 files changed

+81
-9
lines changed

13 files changed

+81
-9
lines changed

packages/playwright-core/src/protocol/validator.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,7 @@ scheme.DebugControllerSetReportStateChangedResult = tOptional(tObject({}));
459459
scheme.DebugControllerSetRecorderModeParams = tObject({
460460
mode: tEnum(['inspecting', 'recording', 'none']),
461461
testIdAttributeName: tOptional(tString),
462+
generateAutoExpect: tOptional(tBoolean),
462463
});
463464
scheme.DebugControllerSetRecorderModeResult = tOptional(tObject({}));
464465
scheme.DebugControllerHighlightParams = tObject({

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,16 @@ import type * as actions from '@recorder/actions';
2222
export function generateCode(actions: actions.ActionInContext[], languageGenerator: LanguageGenerator, options: LanguageGeneratorOptions) {
2323
const header = languageGenerator.generateHeader(options);
2424
const footer = languageGenerator.generateFooter(options.saveStorage);
25-
const actionTexts = actions.map(a => generateActionText(languageGenerator, a)).filter(Boolean) as string[];
25+
const actionTexts = actions.map(a => generateActionText(languageGenerator, a, !!options.generateAutoExpect)).filter(Boolean) as string[];
2626
const text = [header, ...actionTexts, footer].join('\n');
2727
return { header, footer, actionTexts, text };
2828
}
2929

30-
function generateActionText(generator: LanguageGenerator, action: actions.ActionInContext): string | undefined {
30+
function generateActionText(generator: LanguageGenerator, action: actions.ActionInContext, generateAutoExpect: boolean): string | undefined {
3131
let text = generator.generateAction(action);
3232
if (!text)
3333
return;
34-
if (action.action.preconditionSelector) {
34+
if (generateAutoExpect && action.action.preconditionSelector) {
3535
const expectAction: actions.ActionInContext = {
3636
frame: action.frame,
3737
startTime: action.startTime,
@@ -44,7 +44,7 @@ function generateActionText(generator: LanguageGenerator, action: actions.Action
4444
};
4545
const expectText = generator.generateAction(expectAction);
4646
if (expectText)
47-
text = expectText + '\n' + text;
47+
text = expectText + '\n\n' + text;
4848
}
4949
return text;
5050
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export type LanguageGeneratorOptions = {
2525
contextOptions: BrowserContextOptions;
2626
deviceName?: string;
2727
saveStorage?: string;
28+
generateAutoExpect?: boolean;
2829
};
2930

3031
export interface LanguageGenerator {

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ export class DebugController extends SdkObject {
4545
private _trackHierarchyListener: InstrumentationListener | undefined;
4646
private _playwright: Playwright;
4747
_sdkLanguage: Language = 'javascript';
48+
_generateAutoExpect = false;
4849

4950
constructor(playwright: Playwright) {
5051
super({ attribution: { isInternalPlaywright: true }, instrumentation: createInstrumentation() } as any, undefined, 'DebugController');
@@ -73,8 +74,9 @@ export class DebugController extends SdkObject {
7374
}
7475
}
7576

76-
async setRecorderMode(progress: Progress, params: { mode: Mode, testIdAttributeName?: string }) {
77+
async setRecorderMode(progress: Progress, params: { mode: Mode, testIdAttributeName?: string, generateAutoExpect?: boolean }) {
7778
await progress.race(this._closeBrowsersWithoutPages());
79+
this._generateAutoExpect = !!params.generateAutoExpect;
7880

7981
if (params.mode === 'none') {
8082
for (const recorder of await progress.race(this._allRecorders())) {
@@ -181,6 +183,7 @@ function wireListeners(recorder: Recorder, debugController: DebugController) {
181183
browserName: 'chromium',
182184
launchOptions: {},
183185
contextOptions: {},
186+
generateAutoExpect: debugController._generateAutoExpect,
184187
});
185188
debugController.emit(DebugController.Events.SourceChanged, { text, header, footer, actions: actionTexts });
186189
};

packages/playwright-core/src/server/recorder/recorderApp.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,11 @@ export class RecorderApp {
138138
}
139139
return;
140140
}
141+
if (data.event === 'setAutoExpect') {
142+
this._languageGeneratorOptions.generateAutoExpect = data.params.autoExpect;
143+
this._updateActions();
144+
return;
145+
}
141146
if (data.event === 'setMode') {
142147
this._recorder.setMode(data.params.mode);
143148
return;

packages/protocol/src/channels.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -770,9 +770,11 @@ export type DebugControllerSetReportStateChangedResult = void;
770770
export type DebugControllerSetRecorderModeParams = {
771771
mode: 'inspecting' | 'recording' | 'none',
772772
testIdAttributeName?: string,
773+
generateAutoExpect?: boolean,
773774
};
774775
export type DebugControllerSetRecorderModeOptions = {
775776
testIdAttributeName?: string,
777+
generateAutoExpect?: boolean,
776778
};
777779
export type DebugControllerSetRecorderModeResult = void;
778780
export type DebugControllerHighlightParams = {

packages/protocol/src/protocol.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -895,6 +895,7 @@ DebugController:
895895
- recording
896896
- none
897897
testIdAttributeName: string?
898+
generateAutoExpect: boolean?
898899

899900
highlight:
900901
internal: true

packages/recorder/src/recorder.css

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,20 @@ body.dark-mode .recorder .toolbar-button.toggled.circle-large-filled {
5757
.recorder .selector-input {
5858
flex: auto;
5959
}
60+
61+
.setting {
62+
display: flex;
63+
align-items: center;
64+
}
65+
66+
.setting label {
67+
text-overflow: ellipsis;
68+
white-space: nowrap;
69+
overflow: hidden;
70+
cursor: pointer;
71+
}
72+
73+
.setting input {
74+
margin-right: 5px;
75+
flex-shrink: 0;
76+
}

packages/recorder/src/recorder.tsx

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,11 @@ import * as React from 'react';
2626
import { CallLogView } from './callLog';
2727
import './recorder.css';
2828
import { asLocator } from '@isomorphic/locatorGenerators';
29-
import { toggleTheme } from '@web/theme';
29+
import { useDarkModeSetting } from '@web/theme';
3030
import { copy, useSetting } from '@web/uiUtils';
3131
import yaml from 'yaml';
3232
import { parseAriaSnapshot } from '@isomorphic/ariaSnapshot';
33+
import { Dialog } from '@web/components/dialog';
3334

3435
export interface RecorderProps {
3536
sources: Source[],
@@ -48,8 +49,16 @@ export const Recorder: React.FC<RecorderProps> = ({
4849
const [selectedTab, setSelectedTab] = useSetting<string>('recorderPropertiesTab', 'log');
4950
const [ariaSnapshot, setAriaSnapshot] = React.useState<string | undefined>();
5051
const [ariaSnapshotErrors, setAriaSnapshotErrors] = React.useState<SourceHighlight[]>();
52+
const [settingsOpen, setSettingsOpen] = React.useState(false);
53+
const [darkMode, setDarkMode] = useDarkModeSetting();
54+
const [autoExpect, setAutoExpect] = useSetting<boolean>('autoExpect', false);
55+
const settingsButtonRef = React.useRef<HTMLButtonElement>(null);
5156
window.playwrightSelectSource = selectedSourceId => setSelectedFileId(selectedSourceId);
5257

58+
React.useEffect(() => {
59+
window.dispatch({ event: 'setAutoExpect', params: { autoExpect } });
60+
}, [autoExpect]);
61+
5362
const source = React.useMemo(() => {
5463
const source = sources.find(s => s.id === selectedFileId);
5564
return source ?? emptySource();
@@ -178,7 +187,33 @@ export const Recorder: React.FC<RecorderProps> = ({
178187
<ToolbarButton icon='clear-all' title='Clear' disabled={!source || !source.text} onClick={() => {
179188
window.dispatch({ event: 'clear' });
180189
}}></ToolbarButton>
181-
<ToolbarButton icon='color-mode' title='Toggle color mode' toggled={false} onClick={() => toggleTheme()}></ToolbarButton>
190+
<ToolbarButton
191+
ref={settingsButtonRef}
192+
icon='settings-gear'
193+
title='Settings'
194+
onClick={() => setSettingsOpen(current => !current)}
195+
/>
196+
<Dialog
197+
style={{ padding: '4px 8px' }}
198+
open={settingsOpen}
199+
width={200}
200+
verticalOffset={8}
201+
requestClose={() => setSettingsOpen(false)}
202+
anchor={settingsButtonRef}
203+
dataTestId='settings-dialog'
204+
>
205+
<div key='dark-mode-setting' className='setting'>
206+
<input type='checkbox' id='dark-mode-setting' checked={darkMode} onChange={() => setDarkMode(!darkMode)} />
207+
<label htmlFor='dark-mode-setting'>Dark mode</label>
208+
</div>
209+
<div key='auto-expect-setting' className='setting' title='Automatically generate assertions while recording'>
210+
<input type='checkbox' id='auto-expect-setting' checked={autoExpect} onChange={() => {
211+
window.dispatch({ event: 'setAutoExpect', params: { autoExpect: !autoExpect } });
212+
setAutoExpect(!autoExpect);
213+
}} />
214+
<label htmlFor='auto-expect-setting'>Generate assertions</label>
215+
</div>
216+
</Dialog>
182217
</Toolbar>
183218
<SplitView
184219
sidebarSize={200}

packages/trace-viewer/src/ui/settingsToolbarButton.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
*/
1616

1717
import * as React from 'react';
18-
import { Dialog } from './shared/dialog';
18+
import { Dialog } from '@web/components/dialog';
1919
import { ToolbarButton } from '@web/components/toolbarButton';
2020
import { DefaultSettingsView } from './defaultSettingsView';
2121

0 commit comments

Comments
 (0)