Skip to content

Commit 8e91deb

Browse files
authored
NTP: Basic omnibar integration tests (#1798)
* Add basic integration tests for omnibar widget * Add tests for omnibar suggestions * Add input selection assertions for omnibar suggestions * Replace regex role selectors with exact string matches
1 parent f1dee2a commit 8e91deb

File tree

4 files changed

+490
-0
lines changed

4 files changed

+490
-0
lines changed
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
import { expect } from '@playwright/test';
2+
3+
export class OmnibarPage {
4+
/**
5+
* @param {import("../../../integration-tests/new-tab.page.js").NewtabPage} ntp
6+
*/
7+
constructor(ntp) {
8+
this.ntp = ntp;
9+
this.page = this.ntp.page;
10+
}
11+
12+
context() {
13+
return this.page.locator('[data-entry-point="omnibar"]');
14+
}
15+
16+
async ready() {
17+
await this.ntp.mocks.waitForCallCount({ method: 'omnibar_getConfig', count: 1 });
18+
}
19+
20+
searchInput() {
21+
return this.context().getByRole('combobox');
22+
}
23+
24+
chatInput() {
25+
return this.context().getByRole('textbox', { name: 'Chat privately with Duck.ai' });
26+
}
27+
28+
tabList() {
29+
return this.context().getByRole('tablist');
30+
}
31+
32+
searchTab() {
33+
return this.context().getByRole('tab', { name: 'Search' });
34+
}
35+
36+
aiTab() {
37+
return this.context().getByRole('tab', { name: 'Duck.ai' });
38+
}
39+
40+
aiChatButton() {
41+
return this.context().getByRole('button', { name: 'Duck.ai' });
42+
}
43+
44+
suggestionsList() {
45+
return this.context().getByRole('listbox');
46+
}
47+
48+
suggestions() {
49+
return this.suggestionsList().getByRole('option');
50+
}
51+
52+
/**
53+
* @param {number} count
54+
*/
55+
async expectSuggestionsCount(count) {
56+
await expect(this.suggestions()).toHaveCount(count);
57+
}
58+
59+
selectedSuggestion() {
60+
return this.suggestionsList().getByRole('option', { selected: true });
61+
}
62+
63+
/**
64+
* @param {string} text
65+
*/
66+
async expectSelectedSuggestion(text) {
67+
await expect(this.selectedSuggestion()).toHaveText(text);
68+
}
69+
70+
async expectNoSelection() {
71+
await expect(this.selectedSuggestion()).toHaveCount(0);
72+
}
73+
74+
async waitForSuggestions() {
75+
await expect(this.suggestions().first()).toBeVisible();
76+
}
77+
78+
/**
79+
* @param {string} value
80+
*/
81+
async expectInputValue(value) {
82+
await expect(this.searchInput()).toHaveValue(value);
83+
}
84+
85+
/**
86+
* @param {number} startIndex
87+
* @param {number} endIndex
88+
*/
89+
async expectInputSelection(startIndex, endIndex) {
90+
const input = this.searchInput();
91+
const selectionStart = await input.evaluate((el) => {
92+
if (!(el instanceof HTMLInputElement)) {
93+
throw new Error('Element is not an HTMLInputElement');
94+
}
95+
return el.selectionStart;
96+
});
97+
const selectionEnd = await input.evaluate((el) => {
98+
if (!(el instanceof HTMLInputElement)) {
99+
throw new Error('Element is not an HTMLInputElement');
100+
}
101+
return el.selectionEnd;
102+
});
103+
expect(selectionStart).toBe(startIndex);
104+
expect(selectionEnd).toBe(endIndex);
105+
}
106+
107+
/**
108+
* @param {string} selectedText
109+
*/
110+
async expectInputSelectionText(selectedText) {
111+
const input = this.searchInput();
112+
const selection = await input.evaluate((el) => {
113+
if (!(el instanceof HTMLInputElement)) {
114+
throw new Error('Element is not an HTMLInputElement');
115+
}
116+
return el.value.slice(el.selectionStart ?? 0, el.selectionEnd ?? 0);
117+
});
118+
expect(selection).toBe(selectedText);
119+
}
120+
121+
/**
122+
* @param {'search' | 'ai'} mode
123+
*/
124+
async expectMode(mode) {
125+
if (mode === 'search') {
126+
await expect(this.searchTab()).toHaveAttribute('aria-selected', 'true');
127+
} else {
128+
await expect(this.aiTab()).toHaveAttribute('aria-selected', 'true');
129+
}
130+
}
131+
132+
/**
133+
* @param {string} method
134+
* @param {number} count
135+
*/
136+
async expectMethodCallCount(method, count) {
137+
await this.ntp.mocks.waitForCallCount({ method, count });
138+
}
139+
140+
/**
141+
* @param {string} method
142+
* @param {object} expectedParams
143+
*/
144+
async expectMethodCalledWith(method, expectedParams) {
145+
const calls = await this.ntp.mocks.waitForCallCount({ method, count: 1 });
146+
expect(calls[0].payload.params).toEqual(expectedParams);
147+
}
148+
}

0 commit comments

Comments
 (0)