@salesforcedevs/sfdocs-e2e 1.0.12-e2e-topic-version-6 → 1.0.12-e2e-topic-version

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -89,10 +89,14 @@ const chrome = resolvedChromeDriverVersion ? { version: resolvedChromeDriverVers
89
89
  const drivers = {
90
90
  // ChromeDriver 115+ is distributed via Chrome for Testing:
91
91
  // https://googlechromelabs.github.io/chrome-for-testing/
92
- chrome,
93
- firefox: { version: '0.35.0' }, // https://github.com/mozilla/geckodriver/releases - 0.30.0+ supports ARM64 Mac
92
+ chrome
94
93
  };
95
94
 
95
+ // selenium-standalone's own default (4.9.0) doesn't support the `-I` driver flag it
96
+ // unconditionally passes on Selenium 4.x, so the server exits immediately on start.
97
+ // Pinning a version that does support it (4.18.1) avoids that crash.
98
+ const seleniumServerVersion = process.env.SELENIUM_VERSION || '4.18.1';
99
+
96
100
  exports.config = {
97
101
  //
98
102
  // ====================
@@ -188,8 +192,8 @@ exports.config = {
188
192
  [
189
193
  'selenium-standalone',
190
194
  {
191
- installArgs: { drivers }, // drivers to install
192
- args: { drivers }, // drivers to use
195
+ installArgs: { version: seleniumServerVersion, drivers }, // drivers to install
196
+ args: { version: seleniumServerVersion, drivers }, // drivers to use
193
197
  },
194
198
  ],
195
199
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforcedevs/sfdocs-e2e",
3
- "version": "1.0.12-e2e-topic-version-6",
3
+ "version": "1.0.12-e2e-topic-version",
4
4
  "description": "E2E test suite for SFDocs static portal pages (SSG)",
5
5
  "license": "ISC",
6
6
  "bin": {
@@ -149,17 +149,9 @@ describe('Component Specification', () => {
149
149
  'div.container div.sidebar-content dx-tree',
150
150
  );
151
151
 
152
- const getItemText = async (item: WebdriverIO.Element) => {
153
- const link = await item.shadow$('a');
154
- const tiles = await link.$$('dx-tree-tile');
155
- const tileLink = await tiles[0].shadow$('a.tile.sidebar-item');
156
- const label = await tileLink.$('span.tile-label');
157
- return (await label.getText()).trim();
158
- };
159
-
160
152
  const firstItem = (await sidebarTrees[0].shadow$$('dx-tree-item'))[0];
161
153
  const firstChildItem = (await firstItem.shadow$$('dx-tree-item'))[0];
162
- const firstChildText = await getItemText(firstChildItem);
154
+ const firstChildText = await page.getTreeItemLabel(firstChildItem);
163
155
 
164
156
  expect(firstChildText).toBe('Accordion');
165
157
  });
@@ -264,18 +256,10 @@ describe('Component Specification', () => {
264
256
  'div.container div.sidebar-content dx-tree',
265
257
  );
266
258
 
267
- const getItemText = async (item: WebdriverIO.Element) => {
268
- const link = await item.shadow$('a');
269
- const tiles = await link.$$('dx-tree-tile');
270
- const tileLink = await tiles[0].shadow$('a.tile.sidebar-item');
271
- const label = await tileLink.$('span.tile-label');
272
- return (await label.getText()).trim();
273
- };
274
-
275
259
  const auraItem = (await sidebarTrees[1].shadow$$('dx-tree-item'))[0];
276
260
  const lightningItem = (await auraItem.shadow$$('dx-tree-item'))[0];
277
261
  const componentsItem = (await lightningItem.shadow$$('dx-tree-item'))[0];
278
- const firstComponentText = await getItemText(
262
+ const firstComponentText = await page.getTreeItemLabel(
279
263
  (
280
264
  await componentsItem.shadow$$('dx-tree-item')
281
265
  )[0],
@@ -19,72 +19,23 @@ class DocsPage extends Page {
19
19
  return (await $('doc-unified-content-layout')).$('div.markdown-content h1');
20
20
  }
21
21
 
22
- private async getSidebar(): Promise<WebdriverIO.Element> {
23
- return (await $('>>>doc-content-layout')).shadow$('div > dx-sidebar-old');
24
- }
25
-
26
- async waitForSidebar(): Promise<void> {
27
- await (await this.getSidebar()).waitForDisplayed();
28
- }
29
-
30
- async getSidebarTrees(): Promise<WebdriverIO.ElementArray> {
31
- const sidebar = await this.getSidebar();
32
- return sidebar.shadow$$('div.container div.sidebar-content dx-tree');
33
- }
34
-
35
- private async getTreeTile(treeItem: WebdriverIO.Element): Promise<WebdriverIO.Element> {
36
- return this.getTreeItemTile(treeItem);
37
- }
38
-
39
- private async getTreeItemLabel(treeItem: WebdriverIO.Element): Promise<string> {
40
- const treeTile = await this.getTreeTile(treeItem);
41
- const label = await treeTile.shadow$('span.tile-label');
42
- return (await label.getText()).trim();
43
- }
44
-
45
- private async findTreeItemByLabel(
46
- treeItem: WebdriverIO.Element,
47
- label: string,
48
- ): Promise<WebdriverIO.Element | null> {
49
- if ((await this.getTreeItemLabel(treeItem)) === label) {
50
- return treeItem;
51
- }
52
-
53
- const children = await treeItem.shadow$$('dx-tree-item');
54
- for (const child of children) {
55
- const match = await this.findTreeItemByLabel(child, label);
56
- if (match) {
57
- return match;
58
- }
59
- }
60
- return null;
61
- }
62
-
63
22
  async getSidebarTile(label: string): Promise<WebdriverIO.Element> {
64
- const trees = await this.getSidebarTrees();
65
- for (const tree of trees) {
66
- const rootItem = await tree.shadow$('dx-tree-item');
67
- const match = await this.findTreeItemByLabel(rootItem, label);
68
- if (match) {
69
- return match;
70
- }
71
- }
72
- throw new Error(`No sidebar tile found with label "${label}"`);
23
+ return this.getSidebarTreeItemByLabel(label);
73
24
  }
74
25
 
75
26
  async getTileForwardArrow(label: string): Promise<WebdriverIO.Element> {
76
27
  const treeItem = await this.getSidebarTile(label);
77
- return (await this.getTreeTile(treeItem)).shadow$('.tile-forward-arrow');
28
+ return (await this.getTreeItemTile(treeItem)).shadow$('.tile-forward-arrow');
78
29
  }
79
30
 
80
31
  async getTileExternalLinkIcon(label: string): Promise<WebdriverIO.Element> {
81
32
  const treeItem = await this.getSidebarTile(label);
82
- return (await this.getTreeTile(treeItem)).shadow$('.tile-external-link-icon');
33
+ return (await this.getTreeItemTile(treeItem)).shadow$('.tile-external-link-icon');
83
34
  }
84
35
 
85
36
  async getTileAnchor(label: string): Promise<WebdriverIO.Element> {
86
37
  const treeItem = await this.getSidebarTile(label);
87
- return (await this.getTreeTile(treeItem)).shadow$('a');
38
+ return (await this.getTreeItemTile(treeItem)).shadow$('a');
88
39
  }
89
40
 
90
41
  async getRedocBackLink(): Promise<WebdriverIO.Element> {
@@ -118,8 +69,16 @@ class DocsPage extends Page {
118
69
  }
119
70
 
120
71
  async selectLanguage(displayText: string): Promise<void> {
72
+ const picker = await this.getLanguagePicker();
73
+ // A hidden mobile/desktop duplicate of the footer nav can overlap this button at the
74
+ // same screen coordinates, so a native pointer click risks getting intercepted by it.
75
+ await browser.execute((el: { click: () => void }) => el.click(), picker);
76
+
121
77
  const options = await $$('>>>dx-dropdown-option');
122
- for (const option of options) {
78
+ const isDisplayed = await Promise.all(options.map((option) => option.isDisplayed()));
79
+ const visibleOptions = options.filter((_, index) => isDisplayed[index]);
80
+
81
+ for (const option of visibleOptions) {
123
82
  const link = await option.shadow$('a.option');
124
83
  if ((await link.getText()).trim() === displayText) {
125
84
  await browser.execute((el: { click: () => void }) => el.click(), link);
@@ -88,19 +88,63 @@ export default class Page {
88
88
  return treeItem.shadow$('dx-tree-tile');
89
89
  }
90
90
 
91
+ async getSidebar(): Promise<WebdriverIO.Element> {
92
+ return (await $('>>>doc-content-layout')).shadow$('div > dx-sidebar-old');
93
+ }
94
+
95
+ async waitForSidebar(): Promise<void> {
96
+ await (await this.getSidebar()).waitForDisplayed();
97
+ }
98
+
99
+ async getSidebarTrees(): Promise<WebdriverIO.ElementArray> {
100
+ const sidebar = await this.getSidebar();
101
+ return sidebar.shadow$$('div.container div.sidebar-content dx-tree');
102
+ }
103
+
104
+ async getTreeItemLabel(treeItem: WebdriverIO.Element): Promise<string> {
105
+ const tile = await this.getTreeItemTile(treeItem);
106
+ const label = await tile.shadow$('span.tile-label');
107
+ return (await label.getText()).trim();
108
+ }
109
+
110
+ async findTreeItemByLabel(
111
+ treeItem: WebdriverIO.Element,
112
+ label: string,
113
+ ): Promise<WebdriverIO.Element | null> {
114
+ if ((await this.getTreeItemLabel(treeItem)) === label) {
115
+ return treeItem;
116
+ }
117
+
118
+ const children = await treeItem.shadow$$('dx-tree-item');
119
+ for (const child of children) {
120
+ const match = await this.findTreeItemByLabel(child, label);
121
+ if (match) {
122
+ return match;
123
+ }
124
+ }
125
+ return null;
126
+ }
127
+
128
+ async getSidebarTreeItemByLabel(label: string): Promise<WebdriverIO.Element> {
129
+ const trees = await this.getSidebarTrees();
130
+ for (const tree of trees) {
131
+ const rootItem = await tree.shadow$('dx-tree-item');
132
+ const match = await this.findTreeItemByLabel(rootItem, label);
133
+ if (match) {
134
+ return match;
135
+ }
136
+ }
137
+ throw new Error(`No sidebar tree item found with label "${label}"`);
138
+ }
139
+
91
140
  /**
92
141
  * Options render in a single shared overlay, one per picker on the page, hidden until their
93
142
  * own dropdown opens — filtering to visible ones scopes this to whichever just opened.
94
143
  */
95
144
  async getVersionOptions(): Promise<WebdriverIO.Element[]> {
96
145
  const allOptions = await $$('>>>doc-version-picker dx-dropdown-option');
97
- const visible: WebdriverIO.Element[] = [];
98
- for (const option of allOptions) {
99
- if (await option.isDisplayed()) {
100
- visible.push(option);
101
- }
102
- }
103
- return visible;
146
+ const isDisplayed = await Promise.all(allOptions.map((option) => option.isDisplayed()));
147
+ return allOptions.filter((_, index) => isDisplayed[index]);
104
148
  }
105
149
 
106
150
  async waitForSpinnerToDisappear(element: WebdriverIO.Element): Promise<void> {
@@ -28,7 +28,6 @@ export class ReferencePage extends Page {
28
28
  const versionPicker = await this.getVersionPickerBtn();
29
29
  await versionPicker.click();
30
30
  const versionOptions = await $$('>>>doc-version-picker dx-dropdown-option');
31
- await versionOptions[1].waitForClickable();
32
31
  await versionOptions[1].click();
33
32
  }
34
33
 
@@ -24,53 +24,8 @@ export default class TopicVersionPage extends Page {
24
24
  });
25
25
  }
26
26
 
27
- // --- Sidebar tree traversal (mirrors docs.page.ts's private helpers, exposed here so
28
- // individual rows and their pickers can be located directly) ---
29
-
30
- async getSidebar(): Promise<WebdriverIO.Element> {
31
- return (await $('>>>doc-content-layout')).shadow$('div > dx-sidebar-old');
32
- }
33
-
34
- async waitForSidebar(): Promise<void> {
35
- await (await this.getSidebar()).waitForDisplayed();
36
- }
37
-
38
- private async getTreeItemLabel(treeItem: WebdriverIO.Element): Promise<string> {
39
- const tile = await this.getTreeItemTile(treeItem);
40
- const label = await tile.shadow$('span.tile-label');
41
- return (await label.getText()).trim();
42
- }
43
-
44
- private async findTreeItemByLabel(
45
- treeItem: WebdriverIO.Element,
46
- label: string,
47
- ): Promise<WebdriverIO.Element | null> {
48
- if ((await this.getTreeItemLabel(treeItem)) === label) {
49
- return treeItem;
50
- }
51
-
52
- const children = await treeItem.shadow$$('dx-tree-item');
53
- for (const child of children) {
54
- const match = await this.findTreeItemByLabel(child, label);
55
- if (match) {
56
- return match;
57
- }
58
- }
59
- return null;
60
- }
61
-
62
- /** Locates a sidebar tree row (dx-tree-item) by its visible label. */
63
27
  async getSidebarTreeItem(label: string): Promise<WebdriverIO.Element> {
64
- const sidebar = await this.getSidebar();
65
- const trees = await sidebar.shadow$$('div.container div.sidebar-content dx-tree');
66
- for (const tree of trees) {
67
- const rootItem = await tree.shadow$('dx-tree-item');
68
- const match = await this.findTreeItemByLabel(rootItem, label);
69
- if (match) {
70
- return match;
71
- }
72
- }
73
- throw new Error(`No sidebar tree item found with label "${label}"`);
28
+ return this.getSidebarTreeItemByLabel(label);
74
29
  }
75
30
 
76
31
  /** The sidebar row's own doc-version-picker (small variant), or null if it has none. */
@@ -113,11 +68,10 @@ export default class TopicVersionPage extends Page {
113
68
  }
114
69
 
115
70
  /**
116
- * The markdown-topic analog of `getRedocVersionBanner`. TODO: unconfirmed whether markdown
117
- * topics render this banner at all yet — only the Redoc spec-page banner is confirmed shipped.
71
+ * The markdown-topic analog of `getRedocVersionBanner`.
118
72
  */
119
73
  async getMarkdownVersionBanner(): Promise<WebdriverIO.Element | null> {
120
- const banner = await $('>>>doc-content-layout .doc-phase-wrapper doc-phase');
74
+ const banner = await $('>>>doc-content-layout doc-phase[slot="version-banner"]');
121
75
  return (await banner.isExisting()) ? banner : null;
122
76
  }
123
77
 
@@ -171,7 +125,6 @@ export default class TopicVersionPage extends Page {
171
125
  return (await label.getText()).trim();
172
126
  }
173
127
 
174
- /** Opens the picker and clicks the option whose label matches exactly. */
175
128
  async selectVersionByLabel(picker: WebdriverIO.Element, label: string): Promise<void> {
176
129
  await this.openPicker(picker);
177
130
  const options = await this.getVersionOptions();
@@ -187,9 +187,11 @@ describe('Redoc API Reference page', () => {
187
187
 
188
188
  it('should show arrow button after OAS reference titles', async () => {
189
189
  const sideBarContent = await refPage.getSidebarItems();
190
- const treeItem = await sideBarContent[1].shadow$('dx-tree-item');
191
- const tile = await refPage.getTreeItemTile(treeItem);
192
- const arrowIcon = await tile.shadow$(
190
+
191
+ const secondItem = sideBarContent[1];
192
+ const treeItem = await secondItem.shadow$('dx-tree-item');
193
+ const treeTile = await refPage.getTreeItemTile(treeItem);
194
+ const arrowIcon = await treeTile.shadow$(
193
195
  'a > div[role="heading"] > div.tile-forward-arrow',
194
196
  );
195
197
 
@@ -106,11 +106,9 @@ describe('Reference Versioning', () => {
106
106
  const versionBtn = await $('>>>.version-picker-button');
107
107
  await versionBtn.waitForDisplayed();
108
108
  await versionBtn.click();
109
- const versionOptions = await $$('>>>doc-version-picker dx-dropdown-option');
109
+ const versionOptions = await referencePage.getVersionOptions();
110
110
 
111
111
  expect(versionOptions.length).toEqual(2);
112
- await versionOptions[0].waitForClickable();
113
- await versionOptions[1].waitForClickable();
114
112
  expect(await (await versionOptions[0].shadow$('div>div')).getText()).toEqual('0.1.3');
115
113
  expect(await (await versionOptions[1].shadow$('div>div')).getText()).toEqual('0.1.2');
116
114
  });
@@ -121,37 +119,25 @@ describe('Reference Versioning', () => {
121
119
 
122
120
  await searchBox.click();
123
121
  await browser.keys('coupon');
122
+ await browser.pause(1000);
124
123
 
125
- // Search filters asynchronously — wait for the tree to actually narrow down instead of
126
- // querying immediately, which would still catch the pre-filter (full) tree.
127
- await browser.waitUntil(
128
- async () => (await $$('>>>dx-tree-item')).length === 2,
129
- { timeoutMsg: 'expected search for "coupon" to narrow the tree to 2 results' },
130
- );
131
124
  const dxTreeItems = await $$('>>>dx-tree-item');
132
125
 
133
- await (await dxTreeItems[0]).click();
126
+ await dxTreeItems[2].click();
134
127
 
135
128
  const versionBtn = await $('>>>.version-picker-button');
136
129
 
137
130
  await versionBtn.waitForDisplayed();
138
131
  await versionBtn.click();
139
132
 
140
- const versionOptions = await $$('>>>doc-version-picker dx-dropdown-option');
133
+ const versionOptions = await referencePage.getVersionOptions();
141
134
 
142
135
  await versionOptions[1].waitForClickable();
143
136
  await versionOptions[1].click();
144
137
  await referencePage.waitForSpecBasedReferencePageLoad();
145
138
 
146
- // The outer container can display before Redoc finishes populating its content, so wait
147
- // for each element directly rather than reading them immediately.
148
- const apiTitleElement = await $('>>>div.api-title span');
149
- await apiTitleElement.waitForDisplayed();
150
- const apiVersionElement = await $('>>>p.version span');
151
- await apiVersionElement.waitForDisplayed();
152
-
153
- const apiTitle = await apiTitleElement.getText();
154
- const apiVersion = await apiVersionElement.getText();
139
+ const apiTitle = await (await $('>>>div.api-title span')).getText();
140
+ const apiVersion = await (await $('>>>p.version span')).getText();
155
141
 
156
142
  expect(apiTitle).toEqual('Salesforce Connect API');
157
143
  expect(apiVersion).toEqual('53.0');
@@ -163,9 +149,7 @@ describe('Reference Versioning', () => {
163
149
  await versionPicker.waitForDisplayed();
164
150
  await versionPicker.click();
165
151
 
166
- const versionOptions = await $$('>>>doc-version-picker dx-dropdown-option');
167
-
168
- await versionOptions[1].waitForClickable();
152
+ const versionOptions = await referencePage.getVersionOptions();
169
153
  const selectedVersionText = await (await versionOptions[1].shadow$('div>div')).getText();
170
154
 
171
155
  await versionOptions[1].click();
@@ -3,7 +3,7 @@ import TopicVersionPage from '../pageobjects/topic-version.page';
3
3
 
4
4
  // Fixtures live in the `docs-content-type-example` project, kept in sync between this monorepo's
5
5
  // local `packages/example-repository` and the remote `salesforcedocs/example-repository`'s
6
- // `mh/topic-level-version` branch.
6
+ // `nd/e2e-topic-version` branch (pinned in example-portal/docsportal.config.js).
7
7
  const BASE = '/docs/docs-content-type-example';
8
8
 
9
9
  let page: TopicVersionPage;
@@ -41,7 +41,7 @@ describe('Topic-level versioning > versioned markdown topic', () => {
41
41
  expect(await (await $('>>>h1')).getText()).toEqual('OAuth 1.0');
42
42
  await browser.waitUntil(
43
43
  async () => (await browser.getUrl()).includes(`${BASE}/v1/oauth.html`),
44
- { timeoutMsg: 'expected URL to contain /v1/oauth.html' },
44
+ { timeout: 15000, timeoutMsg: 'expected URL to contain /v1/oauth.html' },
45
45
  );
46
46
  });
47
47
 
@@ -54,15 +54,17 @@ describe('Topic-level versioning > versioned markdown topic', () => {
54
54
  await page.selectVersionByLabel(picker!, "WI'26");
55
55
  await page.waitForMarkdownTopicLoad();
56
56
 
57
+ await browser.waitUntil(
58
+ async () => (await browser.getUrl()).endsWith(`${BASE}/oauth.html`),
59
+ { timeout: 15000, timeoutMsg: 'expected URL to end with /oauth.html' },
60
+ );
57
61
  const url = await browser.getUrl();
58
62
  expect(url.endsWith(`${BASE}/oauth.html`)).toBeTruthy();
59
63
  expect(url).not.toContain('/v2/');
60
64
  expect(await (await $('>>>h1')).getText()).toEqual('OAuth 2.0');
61
65
  });
62
66
 
63
- // TODO: unconfirmed whether markdown topics render this banner at all yet (only Redoc spec
64
- // pages are confirmed to). Enable once verified.
65
- it.skip('should show the "Newer Version Available" banner when the version is not latest', async () => {
67
+ it('should show the "Newer Version Available" banner when the version is not latest', async () => {
66
68
  page = new TopicVersionPage(`${BASE}/v1/oauth.html`);
67
69
  page.open();
68
70
  await page.waitForMarkdownTopicLoad();
@@ -98,7 +100,7 @@ describe('Topic-level versioning > versioned parent with versioned child subtree
98
100
 
99
101
  await browser.waitUntil(
100
102
  async () => (await browser.getUrl()).includes(`${BASE}/v1/android.html`),
101
- { timeoutMsg: 'expected URL to contain /v1/android.html' },
103
+ { timeout: 15000, timeoutMsg: 'expected URL to contain /v1/android.html' },
102
104
  );
103
105
  expect(await (await $('>>>h1')).getText()).toEqual('Android SDK v1');
104
106
 
@@ -117,7 +119,7 @@ describe('Topic-level versioning > versioned parent with versioned child subtree
117
119
  page.open();
118
120
  await page.waitForMarkdownTopicLoad();
119
121
 
120
- page = new TopicVersionPage(`${BASE}/android.html`);
122
+ page = new TopicVersionPage(`${BASE}/v2/android.html`);
121
123
  page.open();
122
124
  await page.waitForMarkdownTopicLoad();
123
125
 
@@ -155,7 +157,10 @@ describe('Topic-level versioning > versioned parent with own content, subtopics
155
157
  expect(await (await $('>>>h1')).getText()).toEqual('Errors and Rate Limits (v1)');
156
158
  await browser.waitUntil(
157
159
  async () => (await browser.getUrl()).includes(`${BASE}/v1/errors-and-rate-limits.html`),
158
- { timeoutMsg: 'expected URL to contain /v1/errors-and-rate-limits.html' },
160
+ {
161
+ timeout: 15000,
162
+ timeoutMsg: 'expected URL to contain /v1/errors-and-rate-limits.html',
163
+ },
159
164
  );
160
165
  });
161
166
 
@@ -171,7 +176,7 @@ describe('Topic-level versioning > versioned parent with own content, subtopics
171
176
  expect(await (await $('>>>h1')).getText()).toEqual('Common Error Codes (v1)');
172
177
  await browser.waitUntil(
173
178
  async () => (await browser.getUrl()).includes(`${BASE}/v1/extra-child-a.html`),
174
- { timeoutMsg: 'expected URL to contain /v1/extra-child-a.html' },
179
+ { timeout: 15000, timeoutMsg: 'expected URL to contain /v1/extra-child-a.html' },
175
180
  );
176
181
  });
177
182
 
@@ -187,7 +192,7 @@ describe('Topic-level versioning > versioned parent with own content, subtopics
187
192
 
188
193
  await browser.waitUntil(
189
194
  async () => (await browser.getUrl()).includes(`${BASE}/v1/new-sample-api`),
190
- { timeoutMsg: 'expected URL to contain /v1/new-sample-api' },
195
+ { timeout: 15000, timeoutMsg: 'expected URL to contain /v1/new-sample-api' },
191
196
  );
192
197
  // v1's spec lacks the `GET /errors/{code}` operation that only v2 has — its absence
193
198
  // confirms the v1 spec asset loaded, since both versions share the same spec title.
@@ -237,7 +242,7 @@ describe('Topic-level versioning > standalone Redoc reference', () => {
237
242
 
238
243
  await browser.waitUntil(
239
244
  async () => (await browser.getUrl()).includes(`${BASE}/v1/widgets-api-reference`),
240
- { timeoutMsg: 'expected URL to contain /v1/widgets-api-reference' },
245
+ { timeout: 15000, timeoutMsg: 'expected URL to contain /v1/widgets-api-reference' },
241
246
  );
242
247
  const apiInfo = await $('>>>div.api-info');
243
248
  expect(await apiInfo.getText()).toContain('1.0.0');
@@ -385,7 +390,10 @@ describe('Topic-level versioning > topics without a version picker', () => {
385
390
  await getStarted.click();
386
391
  await page.waitForMarkdownTopicLoad();
387
392
 
388
- expect(await browser.getUrl()).toContain(`${BASE}/get-started.html`);
393
+ await browser.waitUntil(
394
+ async () => (await browser.getUrl()).includes(`${BASE}/get-started.html`),
395
+ { timeout: 15000, timeoutMsg: 'expected URL to contain /get-started.html' },
396
+ );
389
397
  expect(await browser.getUrl()).not.toContain('/v1/');
390
398
  });
391
399
  });