Skip to content

Commit 1516f76

Browse files
committed
test: implement e2e tests on hotkeys
1 parent 2c45df3 commit 1516f76

File tree

6 files changed

+447
-4
lines changed

6 files changed

+447
-4
lines changed

lib/static/new-ui/components/AsidePanel/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ interface AsidePanelProps {
1212

1313
export function AsidePanel(props: AsidePanelProps): ReactNode {
1414
return <div className={classNames(styles.container, props.className)}>
15-
<h2 className={classNames('text-display-1')}>{props.title}</h2>
15+
<h2 className={classNames('text-display-1')} data-qa="aside-panel-title">{props.title}</h2>
1616
<Divider className={styles.divider} orientation={'horizontal'} />
1717
{props.children}
1818
</div>;

lib/static/new-ui/components/AttemptPickerItem/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,5 +90,5 @@ export const AttemptPickerItem = (props: AttemptPickerItemProps): ReactNode => {
9090
{[styles['attempt-picker-item--non-matched']]: isGroupingEnabled && !matchedSelectedGroup}
9191
);
9292

93-
return <Button {...buttonStyle} title={title} className={className} onClick={onClick} qa={'retry-switcher'}>{result.attempt + 1}</Button>;
93+
return <Button {...buttonStyle} title={title} className={className} onClick={onClick} qa={'retry-switcher'} data-qa-active={isActive}>{result.attempt + 1}</Button>;
9494
};

lib/static/new-ui/components/SuiteTitle/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export function SuiteTitle(props: SuiteTitleProps): ReactNode {
4545
</div>
4646
</div>
4747
<div className={styles.paginationContainer}>
48-
<span className={styles.counter}>{props.index === -1 ? '–' : props.index + 1}/{props.totalItems}</span>
48+
<span className={styles.counter} data-qa="suite-title-counter">{props.index === -1 ? '–' : props.index + 1}/{props.totalItems}</span>
4949
<Button qa='suite-prev' view={'flat'} disabled={props.index <= 0} onClick={props.onPrevious}><Icon
5050
data={ChevronUp}/></Button>
5151
<Button qa='suite-next' view={'flat'} disabled={props.index < 0 || props.index === props.totalItems - 1} onClick={props.onNext}><Icon

lib/static/new-ui/features/suites/components/TestStatusBar/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export const TestStatusBar = (): ReactNode => {
3636
<div className={styles['test-status-bar__main']}>
3737
{getIconByStatus(suite.status)}
3838

39-
<div className={styles['test-status-bar__status']}>
39+
<div className={styles['test-status-bar__status']} data-qa="suite-status-bar-status">
4040
{_.startCase(suite.status)}
4141
</div>
4242
<div className={styles['test-status-bar__duration']}>
Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
const childProcess = require('child_process');
2+
const {existsSync} = require('fs');
3+
const fs = require('fs/promises');
4+
const path = require('path');
5+
const {promisify} = require('util');
6+
7+
const treeKill = promisify(require('tree-kill'));
8+
9+
const {PORTS} = require('../../utils/constants');
10+
const {runGui, waitForFsChanges} = require('../utils');
11+
12+
const serverHost = process.env.SERVER_HOST ?? 'host.docker.internal';
13+
14+
const projectName = process.env.PROJECT_UNDER_TEST;
15+
const projectDir = path.resolve(__dirname, '../../fixtures', projectName);
16+
const guiUrl = `http://${serverHost}:${PORTS[projectName].gui}`;
17+
18+
const reportDir = path.join(projectDir, 'report');
19+
const reportBackupDir = path.join(projectDir, 'report-backup');
20+
const screensDir = path.join(projectDir, 'screens');
21+
22+
describe('GUI mode', () => {
23+
describe('Hotkeys', () => {
24+
let guiProcess;
25+
26+
beforeEach(async ({browser}) => {
27+
if (existsSync(reportBackupDir)) {
28+
await fs.rm(reportDir, {recursive: true, force: true, maxRetries: 3});
29+
await fs.cp(reportBackupDir, reportDir, {recursive: true, force: true});
30+
} else {
31+
await fs.cp(reportDir, reportBackupDir, {recursive: true});
32+
}
33+
34+
guiProcess = await runGui(projectDir);
35+
36+
await browser.url(guiUrl + '/new-ui');
37+
38+
await browser.waitUntil(async () => {
39+
const title = await browser.getTitle();
40+
return title.includes('GUI report');
41+
}, {timeout: 10000});
42+
});
43+
44+
afterEach(async () => {
45+
await treeKill(guiProcess.pid);
46+
47+
childProcess.execSync('git restore .', {cwd: screensDir});
48+
childProcess.execSync('git clean -dfx .', {cwd: screensDir});
49+
});
50+
51+
describe('Run test hotkeys', () => {
52+
it('should run current test with "r" key', async ({browser}) => {
53+
const testElement = await browser.$('[data-qa="tree-view-item"]*=chrome');
54+
await testElement.click();
55+
56+
await browser.keys('r');
57+
58+
await browser.waitUntil(async () => {
59+
const isLoading = await browser.$('.g-spin').isExisting();
60+
return isLoading;
61+
}, {timeout: 5000, timeoutMsg: 'Test did not start running'});
62+
63+
await expect(browser.$('[data-qa="suite-status-bar-status"]')).toHaveText('Success');
64+
});
65+
66+
it('should run all visible tests with "shift+r" key', async ({browser}) => {
67+
await browser.keys(['Shift', 'r']);
68+
69+
await browser.waitUntil(async () => {
70+
const spinners = await browser.$$('.g-spin');
71+
return spinners.length > 0;
72+
}, {timeout: 5000, timeoutMsg: 'Tests did not start running'});
73+
74+
await browser.waitUntil(async () => {
75+
const spinners = await browser.$$('.g-spin');
76+
return spinners.length === 0;
77+
}, {timeout: 30000, timeoutMsg: 'Tests did not complete'});
78+
79+
const treeItems = await browser.$$('[data-qa="tree-view-item"]*=chrome');
80+
81+
await treeItems[0].click();
82+
await browser.$('[data-qa="suite-title-counter"]').waitForDisplayed();
83+
let attempts = await browser.$$('[data-qa="retry-switcher"]');
84+
expect(attempts.length).toBe(1);
85+
86+
await treeItems[1].click();
87+
await browser.$('[data-qa="suite-title-counter"]').waitForDisplayed();
88+
attempts = await browser.$$('[data-qa="retry-switcher"]');
89+
expect(attempts.length).toBe(2);
90+
91+
await treeItems[2].click();
92+
await browser.$('[data-qa="suite-title-counter"]').waitForDisplayed();
93+
attempts = await browser.$$('[data-qa="retry-switcher"]');
94+
expect(attempts.length).toBe(2);
95+
});
96+
});
97+
98+
describe('Screenshot accept/undo hotkeys', () => {
99+
beforeEach(async ({browser}) => {
100+
await browser.keys('v');
101+
102+
const visualChecksTitle = await browser.$('[data-qa="sidebar-title"]');
103+
await expect(visualChecksTitle).toHaveText('Visual Checks');
104+
});
105+
106+
it('should accept screenshot with "a" key and auto-advance to next', async ({browser}) => {
107+
const failedTab = await browser.$('[title="Failed"]');
108+
await failedTab.click();
109+
110+
await browser.waitUntil(async () => {
111+
const items = await browser.$$('[data-qa="tree-view-item"]');
112+
return items.length > 0;
113+
});
114+
115+
const initialTitle = await browser.$('h2');
116+
const initialText = await initialTitle.getText();
117+
118+
const acceptButton = await browser.$('[data-qa="accept-button"]');
119+
const isAcceptAvailable = await acceptButton.isExisting();
120+
121+
if (isAcceptAvailable) {
122+
await browser.keys('a');
123+
124+
await waitForFsChanges(screensDir);
125+
126+
await browser.waitUntil(async () => {
127+
const title = await browser.$('h2');
128+
const text = await title.getText();
129+
return text !== initialText;
130+
}, {timeout: 5000, timeoutMsg: 'Did not auto-advance to next screenshot'});
131+
}
132+
});
133+
134+
it('should undo accept with "u" key', async ({browser}) => {
135+
const failedTab = await browser.$('[title="Failed"]');
136+
await failedTab.click();
137+
138+
await browser.waitUntil(async () => {
139+
const items = await browser.$$('[data-qa="tree-view-item"]');
140+
return items.length > 0;
141+
});
142+
143+
const acceptButton = await browser.$('[data-qa="accept-button"]');
144+
const isAcceptAvailable = await acceptButton.isExisting();
145+
146+
if (isAcceptAvailable) {
147+
await acceptButton.click();
148+
await waitForFsChanges(screensDir);
149+
150+
await browser.keys('ArrowUp');
151+
152+
const undoButton = await browser.$('[data-qa="undo-button"]');
153+
await browser.waitUntil(async () => await undoButton.isExisting());
154+
155+
await browser.keys('u');
156+
157+
await waitForFsChanges(screensDir, (output) => output.length === 0);
158+
159+
const acceptButtonAfterUndo = await browser.$('[data-qa="accept-button"]');
160+
await expect(acceptButtonAfterUndo).toBeDisplayed();
161+
}
162+
});
163+
164+
it('should accept all visible with "shift+a" key', async ({browser}) => {
165+
const failedTab = await browser.$('[title="Failed"]');
166+
await failedTab.click();
167+
168+
await browser.waitUntil(async () => {
169+
const items = await browser.$$('[data-qa="tree-view-item"]');
170+
return items.length > 0;
171+
});
172+
173+
const failedItems = await browser.$$('[data-qa="tree-view-item"]');
174+
const failedCount = failedItems.length;
175+
176+
if (failedCount > 0) {
177+
await browser.keys(['Shift', 'a']);
178+
179+
await waitForFsChanges(screensDir);
180+
181+
const passedTab = await browser.$('[title="Passed"]');
182+
await passedTab.click();
183+
184+
await browser.waitUntil(async () => {
185+
const items = await browser.$$('[data-qa="tree-view-item"]');
186+
return items.length > 0;
187+
});
188+
189+
const passedItems = await browser.$$('[data-qa="tree-view-item"]');
190+
expect(passedItems.length).toBeGreaterThan(0);
191+
}
192+
});
193+
});
194+
195+
describe('Navigation hotkeys in Suites page', () => {
196+
it('should navigate to Visual Checks with "g" key from screenshot', async ({browser}) => {
197+
const testWithImage = await browser.$('[data-list-item*="image comparison diff"]');
198+
if (await testWithImage.isExisting()) {
199+
await testWithImage.click();
200+
201+
const screenshotContainer = await browser.$('[data-qa="go-visual-button"]');
202+
if (await screenshotContainer.isExisting()) {
203+
await screenshotContainer.scrollIntoView();
204+
await screenshotContainer.moveTo();
205+
206+
await browser.keys('g');
207+
208+
const visualChecksTitle = await browser.$('[data-qa="sidebar-title"]');
209+
await expect(visualChecksTitle).toHaveText('Visual Checks');
210+
}
211+
}
212+
});
213+
});
214+
});
215+
});

0 commit comments

Comments
 (0)