@salesforcedevs/sfdocs-e2e 1.0.12-e2e-topic-version-7 → 1.0.12-fix-ver-picker-1

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,14 +89,10 @@ 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
92
+ chrome,
93
+ firefox: { version: '0.35.0' }, // https://github.com/mozilla/geckodriver/releases - 0.30.0+ supports ARM64 Mac
93
94
  };
94
95
 
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
-
100
96
  exports.config = {
101
97
  //
102
98
  // ====================
@@ -192,8 +188,8 @@ exports.config = {
192
188
  [
193
189
  'selenium-standalone',
194
190
  {
195
- installArgs: { version: seleniumServerVersion, drivers }, // drivers to install
196
- args: { version: seleniumServerVersion, drivers }, // drivers to use
191
+ installArgs: { drivers }, // drivers to install
192
+ args: { drivers }, // drivers to use
197
193
  },
198
194
  ],
199
195
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforcedevs/sfdocs-e2e",
3
- "version": "1.0.12-e2e-topic-version-7",
3
+ "version": "1.0.12-fix-ver-picker-1",
4
4
  "description": "E2E test suite for SFDocs static portal pages (SSG)",
5
5
  "license": "ISC",
6
6
  "bin": {
@@ -149,9 +149,17 @@ 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
+
152
160
  const firstItem = (await sidebarTrees[0].shadow$$('dx-tree-item'))[0];
153
161
  const firstChildItem = (await firstItem.shadow$$('dx-tree-item'))[0];
154
- const firstChildText = await page.getTreeItemLabel(firstChildItem);
162
+ const firstChildText = await getItemText(firstChildItem);
155
163
 
156
164
  expect(firstChildText).toBe('Accordion');
157
165
  });
@@ -256,10 +264,18 @@ describe('Component Specification', () => {
256
264
  'div.container div.sidebar-content dx-tree',
257
265
  );
258
266
 
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
+
259
275
  const auraItem = (await sidebarTrees[1].shadow$$('dx-tree-item'))[0];
260
276
  const lightningItem = (await auraItem.shadow$$('dx-tree-item'))[0];
261
277
  const componentsItem = (await lightningItem.shadow$$('dx-tree-item'))[0];
262
- const firstComponentText = await page.getTreeItemLabel(
278
+ const firstComponentText = await getItemText(
263
279
  (
264
280
  await componentsItem.shadow$$('dx-tree-item')
265
281
  )[0],
@@ -56,9 +56,7 @@ describe('Test Sitemap Routes', () => {
56
56
  );
57
57
 
58
58
  // Spec (Redoc) topics are published without an extension.
59
- expect(data.body).toContain(
60
- '<loc>/docs/docs-content-type-example/widgets-api-reference</loc>',
61
- );
59
+ expect(data.body).toContain('<loc>/docs/docs-content-type-example/sample-api</loc>');
62
60
  expect(data.body).toContain('<loc>/docs/docs-content-type-example/new-sample-api</loc>');
63
61
 
64
62
  // Each markdown URL appears exactly once.
@@ -19,23 +19,73 @@ 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
+ await treeItem.waitForExist();
37
+ return treeItem.shadow$('a > dx-tree-tile');
38
+ }
39
+
40
+ private async getTreeItemLabel(treeItem: WebdriverIO.Element): Promise<string> {
41
+ const treeTile = await this.getTreeTile(treeItem);
42
+ await treeTile.waitForExist();
43
+ const label = await treeTile.shadow$('span.tile-label');
44
+ return (await label.getText()).trim();
45
+ }
46
+
47
+ private async findTreeItemByLabel(
48
+ treeItem: WebdriverIO.Element,
49
+ label: string,
50
+ ): Promise<WebdriverIO.Element | null> {
51
+ if ((await this.getTreeItemLabel(treeItem)) === label) {
52
+ return treeItem;
53
+ }
54
+
55
+ const children = await treeItem.shadow$$('dx-tree-item');
56
+ for (const child of children) {
57
+ const match = await this.findTreeItemByLabel(child, label);
58
+ if (match) {
59
+ return match;
60
+ }
61
+ }
62
+ return null;
63
+ }
64
+
22
65
  async getSidebarTile(label: string): Promise<WebdriverIO.Element> {
23
- return this.getSidebarTreeItemByLabel(label);
66
+ const trees = await this.getSidebarTrees();
67
+ for (const tree of trees) {
68
+ const rootItem = await tree.shadow$('dx-tree-item');
69
+ const match = await this.findTreeItemByLabel(rootItem, label);
70
+ if (match) {
71
+ return match;
72
+ }
73
+ }
74
+ throw new Error(`No sidebar tile found with label "${label}"`);
24
75
  }
25
76
 
26
77
  async getTileForwardArrow(label: string): Promise<WebdriverIO.Element> {
27
78
  const treeItem = await this.getSidebarTile(label);
28
- return (await this.getTreeItemTile(treeItem)).shadow$('.tile-forward-arrow');
79
+ const treeTile = await this.getTreeTile(treeItem);
80
+ await treeTile.waitForExist();
81
+ return treeTile.shadow$('.tile-forward-arrow');
29
82
  }
30
83
 
31
84
  async getTileExternalLinkIcon(label: string): Promise<WebdriverIO.Element> {
32
85
  const treeItem = await this.getSidebarTile(label);
33
- return (await this.getTreeItemTile(treeItem)).shadow$('.tile-external-link-icon');
34
- }
35
-
36
- async getTileAnchor(label: string): Promise<WebdriverIO.Element> {
37
- const treeItem = await this.getSidebarTile(label);
38
- return (await this.getTreeItemTile(treeItem)).shadow$('a');
86
+ const treeTile = await this.getTreeTile(treeItem);
87
+ await treeTile.waitForExist();
88
+ return treeTile.shadow$('.tile-external-link-icon');
39
89
  }
40
90
 
41
91
  async getRedocBackLink(): Promise<WebdriverIO.Element> {
@@ -69,16 +119,8 @@ class DocsPage extends Page {
69
119
  }
70
120
 
71
121
  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
-
77
122
  const options = await $$('>>>dx-dropdown-option');
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
+ for (const option of options) {
82
124
  const link = await option.shadow$('a.option');
83
125
  if ((await link.getText()).trim() === displayText) {
84
126
  await browser.execute((el: { click: () => void }) => el.click(), link);
@@ -84,69 +84,6 @@ export default class Page {
84
84
  return await $('>>>doc-specification-content');
85
85
  }
86
86
 
87
- async getTreeItemTile(treeItem: WebdriverIO.Element): Promise<WebdriverIO.Element> {
88
- return treeItem.shadow$('dx-tree-tile');
89
- }
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
-
140
- /**
141
- * Options render in a single shared overlay, one per picker on the page, hidden until their
142
- * own dropdown opens — filtering to visible ones scopes this to whichever just opened.
143
- */
144
- async getVersionOptions(): Promise<WebdriverIO.Element[]> {
145
- const allOptions = await $$('>>>doc-version-picker dx-dropdown-option');
146
- const isDisplayed = await Promise.all(allOptions.map((option) => option.isDisplayed()));
147
- return allOptions.filter((_, index) => isDisplayed[index]);
148
- }
149
-
150
87
  async waitForSpinnerToDisappear(element: WebdriverIO.Element): Promise<void> {
151
88
  await browser.waitUntil(
152
89
  async function () {
@@ -28,6 +28,7 @@ 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();
31
32
  await versionOptions[1].click();
32
33
  }
33
34
 
@@ -85,7 +85,7 @@ describe('Docs content type', () => {
85
85
  await parentTile.click();
86
86
  await page.waitPageLoad();
87
87
 
88
- expect(await browser.getUrl()).toContain('errors-and-rate-limits.html');
88
+ expect(await browser.getUrl()).toContain('parent-with-refs.html');
89
89
  expect(await (await page.getContentHeading()).getText()).toContain(
90
90
  'Errors and Rate Limits',
91
91
  );
@@ -197,7 +197,7 @@ describe('Docs content type > internal links', () => {
197
197
 
198
198
  describe('Docs content type > spec (Redoc) topic', () => {
199
199
  beforeEach(async () => {
200
- page = new DocsPage('/docs/docs-content-type-example/widgets-api-reference');
200
+ page = new DocsPage('/docs/docs-content-type-example/sample-api');
201
201
  page.open();
202
202
  await page.waitForRedocLoad();
203
203
  });
@@ -218,7 +218,7 @@ describe('Docs content type > spec (Redoc) topic', () => {
218
218
 
219
219
  it('falls back to the landing topic when the back link is clicked on a directly opened spec', async () => {
220
220
  await browser.reloadSession();
221
- page = new DocsPage('/docs/docs-content-type-example/widgets-api-reference');
221
+ page = new DocsPage('/docs/docs-content-type-example/sample-api');
222
222
  page.open();
223
223
  await page.waitForRedocLoad();
224
224
 
@@ -245,7 +245,7 @@ describe('Docs content type > locale picker', () => {
245
245
  });
246
246
 
247
247
  it('shows the language picker on a spec topic of a localized project', async () => {
248
- page = new DocsPage('/docs/docs-content-type-example/widgets-api-reference');
248
+ page = new DocsPage('/docs/docs-content-type-example/sample-api');
249
249
  page.open();
250
250
  await page.waitForRedocLoad();
251
251
 
@@ -256,7 +256,7 @@ describe('Docs content type > locale picker', () => {
256
256
  });
257
257
 
258
258
  it('re-renders the spec in the selected locale when switching language on a spec page', async () => {
259
- page = new DocsPage('/docs/docs-content-type-example/widgets-api-reference');
259
+ page = new DocsPage('/docs/docs-content-type-example/sample-api');
260
260
  page.open();
261
261
  await page.waitForRedocLoad();
262
262
 
@@ -306,7 +306,7 @@ describe('Docs content type > localized (ja-jp) rendering', () => {
306
306
  });
307
307
 
308
308
  it('renders the Redoc view from a ja-jp spec URL', async () => {
309
- page = new DocsPage('/docs/ja-jp/docs-content-type-example/widgets-api-reference');
309
+ page = new DocsPage('/docs/ja-jp/docs-content-type-example/sample-api');
310
310
  page.open();
311
311
  await page.waitForRedocLoad();
312
312
 
@@ -355,7 +355,8 @@ describe('Docs content type > external link', () => {
355
355
  await page.waitPageLoad();
356
356
  await page.waitForSidebar();
357
357
 
358
- const anchor = await page.getTileAnchor('Salesforce Developers');
358
+ const tile = await page.getSidebarTile('Salesforce Developers');
359
+ const anchor = await tile.shadow$('a');
359
360
  expect(await anchor.getAttribute('href')).toContain('https://developer.salesforce.com');
360
361
  expect(await anchor.getAttribute('target')).toBe('_blank');
361
362
  });
@@ -66,24 +66,24 @@ describe('Test Guides page components functionality> ', () => {
66
66
 
67
67
  expect(sideBarContent.length).toEqual(6);
68
68
 
69
- const nextStepOption = await (
70
- await sideBarContent[3].shadow$('dx-tree-item')
71
- ).shadow$('dx-tree-tile');
72
-
73
- expect(nextStepOption.isExisting()).toBeTruthy();
74
-
75
- const installVSCodeOptionTile = await (
76
- await sideBarContent[1].shadow$('dx-tree-item')
77
- ).shadow$('dx-tree-tile');
78
-
79
- expect(installVSCodeOptionTile.isExisting()).toBeTruthy();
80
-
81
- await (await sideBarContent[1].shadow$('dx-tree-item')).click();
69
+ const nextStepTreeItem = await sideBarContent[3].shadow$('dx-tree-item');
70
+ await nextStepTreeItem.waitForExist();
71
+ const nextStepOption = await nextStepTreeItem.shadow$('dx-tree-tile');
72
+ await nextStepOption.waitForExist();
73
+ expect(await nextStepOption.isExisting()).toBeTruthy();
74
+
75
+ const installVSCodeTreeItem = await sideBarContent[1].shadow$('dx-tree-item');
76
+ await installVSCodeTreeItem.waitForExist();
77
+ const installVSCodeOptionTile = await installVSCodeTreeItem.shadow$('dx-tree-tile');
78
+ await installVSCodeOptionTile.waitForExist();
79
+ expect(await installVSCodeOptionTile.isExisting()).toBeTruthy();
80
+
81
+ await installVSCodeTreeItem.click();
82
82
  await page.waitPageLoad();
83
83
 
84
- const installVSCodeOption = await (
85
- await sideBarContent[1].shadow$('dx-tree-item')
86
- ).shadow$$('dx-tree-item');
84
+ const installVSCodeTreeItemAfterNav = await sideBarContent[1].shadow$('dx-tree-item');
85
+ await installVSCodeTreeItemAfterNav.waitForExist();
86
+ const installVSCodeOption = await installVSCodeTreeItemAfterNav.shadow$$('dx-tree-item');
87
87
 
88
88
  expect(installVSCodeOption.length).toEqual(3);
89
89
 
@@ -304,9 +304,15 @@ describe('Search guides', () => {
304
304
  await browser.keys('SFRA');
305
305
 
306
306
  const trees = await sidebar.shadow$$('div.container div.sidebar-content dx-tree');
307
- const servicesTree = await (await trees[0].shadow$('dx-tree-item')).shadow$('dx-tree-item');
307
+ const rootTreeItem = await trees[0].shadow$('dx-tree-item');
308
+ await rootTreeItem.waitForExist();
309
+ const servicesTree = await rootTreeItem.shadow$('dx-tree-item');
310
+ await servicesTree.waitForExist();
308
311
  const treeItem = await servicesTree.shadow$('dx-tree-item');
309
- const item = await (await treeItem.shadow$('dx-tree-tile')).shadow$('span');
312
+ await treeItem.waitForExist();
313
+ const treeTile = await treeItem.shadow$('dx-tree-tile');
314
+ await treeTile.waitForExist();
315
+ const item = await treeTile.shadow$('span');
310
316
  const itemText = await item.getText();
311
317
  expect(itemText).toBe('Find SFRA Tools');
312
318
  });
@@ -322,7 +328,10 @@ describe('Search guides', () => {
322
328
  await browser.keys('SFRA');
323
329
 
324
330
  const trees = await (await sidebar).shadow$$('div.container div.sidebar-content dx-tree');
325
- const servicesTree = await (await trees[0].shadow$('dx-tree-item')).shadow$('dx-tree-item');
331
+ const rootTreeItem = await trees[0].shadow$('dx-tree-item');
332
+ await rootTreeItem.waitForExist();
333
+ const servicesTree = await rootTreeItem.shadow$('dx-tree-item');
334
+ await servicesTree.waitForExist();
326
335
  const treeItem = await servicesTree.shadow$('dx-tree-item');
327
336
  await treeItem.click();
328
337
  await page.waitPageLoad();
@@ -190,10 +190,13 @@ describe('Redoc API Reference page', () => {
190
190
 
191
191
  const secondItem = sideBarContent[1];
192
192
  const treeItem = await secondItem.shadow$('dx-tree-item');
193
- const treeTile = await refPage.getTreeItemTile(treeItem);
193
+ await treeItem.waitForExist();
194
+ const treeTile = await treeItem.shadow$('a > dx-tree-tile');
195
+ await treeTile.waitForExist();
194
196
  const arrowIcon = await treeTile.shadow$(
195
197
  'a > div[role="heading"] > div.tile-forward-arrow',
196
198
  );
199
+ await arrowIcon.waitForExist();
197
200
 
198
201
  expect(await arrowIcon.isExisting()).toBeTruthy();
199
202
  });
@@ -106,9 +106,11 @@ 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 referencePage.getVersionOptions();
109
+ const versionOptions = await $$('>>>doc-version-picker dx-dropdown-option');
110
110
 
111
111
  expect(versionOptions.length).toEqual(2);
112
+ await versionOptions[0].waitForClickable();
113
+ await versionOptions[1].waitForClickable();
112
114
  expect(await (await versionOptions[0].shadow$('div>div')).getText()).toEqual('0.1.3');
113
115
  expect(await (await versionOptions[1].shadow$('div>div')).getText()).toEqual('0.1.2');
114
116
  });
@@ -118,19 +120,20 @@ describe('Reference Versioning', () => {
118
120
  const searchBox = await dxInput.shadow$('input');
119
121
 
120
122
  await searchBox.click();
121
- await browser.keys('coupon');
122
- await browser.pause(1000);
123
+ await browser.keys('/cart-coupons');
123
124
 
124
125
  const dxTreeItems = await $$('>>>dx-tree-item');
125
126
 
126
- await dxTreeItems[2].click();
127
+ expect(dxTreeItems.length > 2).toBeTruthy();
128
+
129
+ await (await dxTreeItems[2]).click();
127
130
 
128
131
  const versionBtn = await $('>>>.version-picker-button');
129
132
 
130
133
  await versionBtn.waitForDisplayed();
131
134
  await versionBtn.click();
132
135
 
133
- const versionOptions = await referencePage.getVersionOptions();
136
+ const versionOptions = await $$('>>>doc-version-picker dx-dropdown-option');
134
137
 
135
138
  await versionOptions[1].waitForClickable();
136
139
  await versionOptions[1].click();
@@ -149,7 +152,9 @@ describe('Reference Versioning', () => {
149
152
  await versionPicker.waitForDisplayed();
150
153
  await versionPicker.click();
151
154
 
152
- const versionOptions = await referencePage.getVersionOptions();
155
+ const versionOptions = await $$('>>>doc-version-picker dx-dropdown-option');
156
+
157
+ await versionOptions[1].waitForClickable();
153
158
  const selectedVersionText = await (await versionOptions[1].shadow$('div>div')).getText();
154
159
 
155
160
  await versionOptions[1].click();
@@ -221,11 +226,12 @@ describe('Markdown-Based Flexible References', () => {
221
226
  const sidebar = await (await $('>>>doc-content-layout')).shadow$('div > dx-sidebar-old');
222
227
  const sideBarContent = await sidebar.shadow$$('div.container div.sidebar-content dx-tree');
223
228
  expect(sideBarContent.length).toEqual(1);
224
- const tile = await (
225
- await sideBarContent[0].shadow$('dx-tree-item')
226
- ).shadow$('dx-tree-tile');
227
- expect(tile.isExisting()).toBeTruthy();
228
- await (await sideBarContent[0].shadow$('dx-tree-item')).click();
229
+ const treeItem = await sideBarContent[0].shadow$('dx-tree-item');
230
+ await treeItem.waitForExist();
231
+ const tile = await treeItem.shadow$('dx-tree-tile');
232
+ await tile.waitForExist();
233
+ expect(await tile.isExisting()).toBeTruthy();
234
+ await treeItem.click();
229
235
  await referencePage.waitForMarkdownBasedReferencePageLoad();
230
236
  const title = await $('>>>h1');
231
237
  const titleText = await title.getText();
@@ -1,149 +0,0 @@
1
- import Page from './page';
2
-
3
- /**
4
- * Page object for topic-level (per-topic) version picker interactions.
5
- *
6
- * Two `doc-version-picker` instances exist for a versioned topic:
7
- * - the SIDEBAR picker (small variant), rendered as a sibling of `dx-tree-tile` inside the
8
- * versioned tree row's own shadow root (`class="tree-item-version-picker"`).
9
- * - the REDOC SPEC-PAGE picker (default variant), rendered inside `div.redoc-project-header`
10
- * alongside the back-link/spec title. Redoc mounts this header into BOTH `.menu-content` and
11
- * `.api-content`, so we always scope through the sidebar's copy (`getRedocProjectHeader`) to
12
- * avoid matching the duplicate.
13
- */
14
- export default class TopicVersionPage extends Page {
15
- async waitForMarkdownTopicLoad(): Promise<void> {
16
- await (
17
- await $('>>>doc-content-layout')
18
- ).waitForDisplayed({ timeout: this.REFERENCE_LOAD_WAIT_TIME });
19
- }
20
-
21
- async waitForRedocTopicLoad(): Promise<void> {
22
- await $('doc-redoc-reference > .redoc-container').waitForDisplayed({
23
- timeout: this.REFERENCE_LOAD_WAIT_TIME,
24
- });
25
- }
26
-
27
- async getSidebarTreeItem(label: string): Promise<WebdriverIO.Element> {
28
- return this.getSidebarTreeItemByLabel(label);
29
- }
30
-
31
- /** The sidebar row's own doc-version-picker (small variant), or null if it has none. */
32
- async getSidebarPicker(rowLabel: string): Promise<WebdriverIO.Element | null> {
33
- const treeItem = await this.getSidebarTreeItem(rowLabel);
34
- const picker = await treeItem.shadow$('.tree-item-version-picker');
35
- return (await picker.isExisting()) ? picker : null;
36
- }
37
-
38
- // --- Redoc spec-page project header (default variant) ---
39
-
40
- private async getRedocSidebar(): Promise<WebdriverIO.Element> {
41
- return $('.redoc-wrap > .menu-content');
42
- }
43
-
44
- private async getRedocProjectHeader(): Promise<WebdriverIO.Element> {
45
- return (await this.getRedocSidebar()).$('div.redoc-project-header');
46
- }
47
-
48
- /** The Redoc spec-page's own doc-version-picker, or null if this spec has no versions. */
49
- async getRedocPicker(): Promise<WebdriverIO.Element | null> {
50
- const header = await this.getRedocProjectHeader();
51
- const wrapper = await header.$('.redoc-version-picker');
52
- if (!(await wrapper.isExisting())) {
53
- return null;
54
- }
55
- return wrapper.$('doc-version-picker');
56
- }
57
-
58
- /**
59
- * The dismissible "Newer Version Available" banner shown on a non-latest Redoc spec version.
60
- * Reuses the `doc-phase` component, inserted at the top of `.api-content`. Returns null when
61
- * absent (latest version, single version, or no versions).
62
- */
63
- async getRedocVersionBanner(): Promise<WebdriverIO.Element | null> {
64
- const banner = await $(
65
- 'doc-redoc-reference > .redoc-container .api-content .doc-phase-wrapper doc-phase',
66
- );
67
- return (await banner.isExisting()) ? banner : null;
68
- }
69
-
70
- /**
71
- * The markdown-topic analog of `getRedocVersionBanner`.
72
- */
73
- async getMarkdownVersionBanner(): Promise<WebdriverIO.Element | null> {
74
- const banner = await $('>>>doc-content-layout doc-phase[slot="version-banner"]');
75
- return (await banner.isExisting()) ? banner : null;
76
- }
77
-
78
- async getBannerTitle(banner: WebdriverIO.Element): Promise<string> {
79
- return (await banner.shadow$('.doc-phase-title')).getText();
80
- }
81
-
82
- async getBannerLinkHref(banner: WebdriverIO.Element): Promise<string> {
83
- const link = await (await banner.shadow$('.doc-phase-body')).$('a');
84
- return link.getAttribute('href');
85
- }
86
-
87
- // --- Generic picker interactions (work for either variant) ---
88
-
89
- async getSelectedVersionLabel(picker: WebdriverIO.Element): Promise<string> {
90
- const label = await picker.shadow$('p.selected-version-label');
91
- return (await label.getText()).trim();
92
- }
93
-
94
- async isReadOnly(picker: WebdriverIO.Element): Promise<boolean> {
95
- return (await picker.shadow$('p.version-picker-readonly')).isExisting();
96
- }
97
-
98
- /** Default (non-small) variant only — the redoc spec-page picker. */
99
- async isLatestBadgeShown(picker: WebdriverIO.Element): Promise<boolean> {
100
- return (await picker.shadow$('dx-type-badge.latest-badge')).isExisting();
101
- }
102
-
103
- async isNotLatestBadgeShown(picker: WebdriverIO.Element): Promise<boolean> {
104
- return (await picker.shadow$('dx-type-badge.not-latest-badge')).isExisting();
105
- }
106
-
107
- /** Small variant only — the sidebar picker shows a colored dot instead of a badge. */
108
- async isLatestDotShown(picker: WebdriverIO.Element): Promise<boolean> {
109
- return (await picker.shadow$('span.version-picker-dot-latest')).isExisting();
110
- }
111
-
112
- async isNotLatestDotShown(picker: WebdriverIO.Element): Promise<boolean> {
113
- return (await picker.shadow$('span.version-picker-dot-not-latest')).isExisting();
114
- }
115
-
116
- async openPicker(picker: WebdriverIO.Element): Promise<void> {
117
- const trigger = await picker.shadow$('.version-picker-button');
118
- await trigger.waitForDisplayed();
119
- await trigger.waitForClickable();
120
- await trigger.click();
121
- }
122
-
123
- async getOptionLabel(option: WebdriverIO.Element): Promise<string> {
124
- const label = await option.shadow$('div>div');
125
- return (await label.getText()).trim();
126
- }
127
-
128
- async selectVersionByLabel(picker: WebdriverIO.Element, label: string): Promise<void> {
129
- await this.openPicker(picker);
130
- const options = await this.getVersionOptions();
131
- for (const option of options) {
132
- if ((await this.getOptionLabel(option)) === label) {
133
- await option.waitForClickable();
134
- await option.click();
135
- return;
136
- }
137
- }
138
- throw new Error(`No version option found with label "${label}"`);
139
- }
140
-
141
- async readSessionVersionSelections(): Promise<Record<string, string> | null> {
142
- // The tsconfig's lib list omits "DOM", so sessionStorage isn't a known global here even
143
- // though this callback runs in the browser — route through globalThis to sidestep that.
144
- const raw = await browser.execute(() =>
145
- (globalThis as any).sessionStorage.getItem('sfdocs-topic-version-selections'),
146
- );
147
- return raw ? JSON.parse(raw) : null;
148
- }
149
- }
@@ -1,412 +0,0 @@
1
- import { it } from 'mocha';
2
- import TopicVersionPage from '../pageobjects/topic-version.page';
3
-
4
- // Fixtures live in the `docs-content-type-example` project, kept in sync between this monorepo's
5
- // local `packages/example-repository` and the remote `salesforcedocs/example-repository`'s
6
- // `nd/e2e-topic-version` branch (pinned in example-portal/docsportal.config.js).
7
- const BASE = '/docs/docs-content-type-example';
8
-
9
- let page: TopicVersionPage;
10
-
11
- describe('Topic-level versioning > versioned markdown topic', () => {
12
- // fixture: OAuth — latest v2 'WI'26' / older v1 'SU'26'.
13
- beforeEach(async () => {
14
- page = new TopicVersionPage(`${BASE}/oauth.html`);
15
- page.open();
16
- await page.waitForMarkdownTopicLoad();
17
- });
18
-
19
- it('should render the picker and default to the latest version', async () => {
20
- const picker = await page.getSidebarPicker('OAuth');
21
- expect(picker).not.toBeNull();
22
- expect(await page.getSelectedVersionLabel(picker!)).toEqual("WI'26");
23
- expect(await page.isLatestDotShown(picker!)).toBeTruthy();
24
- });
25
-
26
- it('should list all versions in declared order when the picker is opened', async () => {
27
- const picker = await page.getSidebarPicker('OAuth');
28
- await page.openPicker(picker!);
29
-
30
- const options = await page.getVersionOptions();
31
- expect(options.length).toEqual(2);
32
- expect(await page.getOptionLabel(options[0])).toEqual("WI'26");
33
- expect(await page.getOptionLabel(options[1])).toEqual("SU'26");
34
- });
35
-
36
- it('should switch the page content and URL when a non-latest version is selected', async () => {
37
- const picker = await page.getSidebarPicker('OAuth');
38
- await page.selectVersionByLabel(picker!, "SU'26");
39
- await page.waitForMarkdownTopicLoad();
40
-
41
- expect(await (await $('>>>h1')).getText()).toEqual('OAuth 1.0');
42
- expect(await browser.getUrl()).toContain(`${BASE}/v1/oauth.html`);
43
- });
44
-
45
- it('should return to the canonical URL when the latest version is reselected', async () => {
46
- page = new TopicVersionPage(`${BASE}/v1/oauth.html`);
47
- page.open();
48
- await page.waitForMarkdownTopicLoad();
49
-
50
- const picker = await page.getSidebarPicker('OAuth');
51
- await page.selectVersionByLabel(picker!, "WI'26");
52
- await page.waitForMarkdownTopicLoad();
53
-
54
- const url = await browser.getUrl();
55
- expect(url.endsWith(`${BASE}/oauth.html`)).toBeTruthy();
56
- expect(url).not.toContain('/v2/');
57
- expect(await (await $('>>>h1')).getText()).toEqual('OAuth 2.0');
58
- });
59
-
60
- it('should show the "Newer Version Available" banner when the version is not latest', async () => {
61
- page = new TopicVersionPage(`${BASE}/v1/oauth.html`);
62
- page.open();
63
- await page.waitForMarkdownTopicLoad();
64
-
65
- const banner = await page.getMarkdownVersionBanner();
66
- expect(banner).not.toBeNull();
67
- });
68
-
69
- it('should not show the "Newer Version Available" banner when the version is latest', async () => {
70
- const banner = await page.getMarkdownVersionBanner();
71
- expect(banner).toBeNull();
72
- });
73
- });
74
-
75
- describe('Topic-level versioning > versioned parent with versioned child subtree', () => {
76
- // fixture: Client SDKs — latest v2 'WI'26' / older v1 'SU'26'.
77
- beforeEach(async () => {
78
- page = new TopicVersionPage(`${BASE}/android.html`);
79
- page.open();
80
- await page.waitForMarkdownTopicLoad();
81
- });
82
-
83
- it('should render the picker on the parent and default to the latest version', async () => {
84
- const picker = await page.getSidebarPicker('Client SDKs');
85
- expect(picker).not.toBeNull();
86
- expect(await page.getSelectedVersionLabel(picker!)).toEqual("WI'26");
87
- });
88
-
89
- it('should switch the whole child subtree when the parent version changes', async () => {
90
- const picker = await page.getSidebarPicker('Client SDKs');
91
- await page.selectVersionByLabel(picker!, "SU'26");
92
- await page.waitForMarkdownTopicLoad();
93
-
94
- expect(await browser.getUrl()).toContain(`${BASE}/v1/android.html`);
95
- expect(await (await $('>>>h1')).getText()).toEqual('Android SDK v1');
96
-
97
- // Sibling child (iOS SDK) should also have moved to the v1 URL, not just this one.
98
- const iosItem = await page.getSidebarTreeItem('iOS SDK');
99
- const iosTile = await page.getTreeItemTile(iosItem);
100
- const iosAnchor = await iosTile.shadow$('a');
101
- expect(await iosAnchor.getAttribute('href')).toContain('/v1/ios.html');
102
- });
103
-
104
- // TODO: known gap — navigating directly to a different child version URL doesn't sync the
105
- // parent's picker or sibling hrefs back to that version; both stay stuck on the version the
106
- // page first loaded with. Enable once that reverse sync ships.
107
- it.skip('should update the picker and sibling hrefs when navigating directly to a different child version', async () => {
108
- page = new TopicVersionPage(`${BASE}/v1/android.html`);
109
- page.open();
110
- await page.waitForMarkdownTopicLoad();
111
-
112
- page = new TopicVersionPage(`${BASE}/v2/android.html`);
113
- page.open();
114
- await page.waitForMarkdownTopicLoad();
115
-
116
- const picker = await page.getSidebarPicker('Client SDKs');
117
- expect(await page.getSelectedVersionLabel(picker!)).toEqual("WI'26");
118
-
119
- const iosItem = await page.getSidebarTreeItem('iOS SDK');
120
- const iosTile = await page.getTreeItemTile(iosItem);
121
- const iosAnchor = await iosTile.shadow$('a');
122
- const href = await iosAnchor.getAttribute('href');
123
- expect(href).toContain('/ios.html');
124
- expect(href).not.toContain('/v1/');
125
- });
126
- });
127
-
128
- describe('Topic-level versioning > versioned parent with own content, subtopics and a spec subtopic', () => {
129
- // fixture: Errors and Rate Limits — latest v2 'SP'26' / older v1 'WI'25'.
130
- beforeEach(async () => {
131
- page = new TopicVersionPage(`${BASE}/errors-and-rate-limits.html`);
132
- page.open();
133
- await page.waitForMarkdownTopicLoad();
134
- });
135
-
136
- it('should render the picker and default to the latest version', async () => {
137
- const picker = await page.getSidebarPicker('Errors and Rate Limits');
138
- expect(picker).not.toBeNull();
139
- expect(await page.getSelectedVersionLabel(picker!)).toEqual("SP'26");
140
- });
141
-
142
- it("should switch the parent's own page content when the version changes", async () => {
143
- const picker = await page.getSidebarPicker('Errors and Rate Limits');
144
- await page.selectVersionByLabel(picker!, "WI'25");
145
- await page.waitForMarkdownTopicLoad();
146
-
147
- expect(await (await $('>>>h1')).getText()).toEqual('Errors and Rate Limits (v1)');
148
- expect(await browser.getUrl()).toContain(`${BASE}/v1/errors-and-rate-limits.html`);
149
- });
150
-
151
- it('should switch the markdown subtopics when the version changes', async () => {
152
- const picker = await page.getSidebarPicker('Errors and Rate Limits');
153
- await page.selectVersionByLabel(picker!, "WI'25");
154
- await page.waitForMarkdownTopicLoad();
155
-
156
- const child = await page.getSidebarTreeItem('Common Error Codes');
157
- await child.click();
158
- await page.waitForMarkdownTopicLoad();
159
-
160
- expect(await (await $('>>>h1')).getText()).toEqual('Common Error Codes (v1)');
161
- expect(await browser.getUrl()).toContain(`${BASE}/v1/extra-child-a.html`);
162
- });
163
-
164
- it('should switch the spec (Redoc) subtopic to the matching version when the version changes', async () => {
165
- // Key cross-type cascade: parent v1 ⇒ the nested spec subtopic is v1 too.
166
- const picker = await page.getSidebarPicker('Errors and Rate Limits');
167
- await page.selectVersionByLabel(picker!, "WI'25");
168
- await page.waitForMarkdownTopicLoad();
169
-
170
- const child = await page.getSidebarTreeItem('Another Spec API');
171
- await child.click();
172
- await page.waitForRedocTopicLoad();
173
-
174
- expect(await browser.getUrl()).toContain(`${BASE}/v1/new-sample-api`);
175
- // v1's spec lacks the `GET /errors/{code}` operation that only v2 has — its absence
176
- // confirms the v1 spec asset loaded, since both versions share the same spec title.
177
- const getErrorCodeOperation = await $('a[href*="operation/getErrorCode"]');
178
- expect(await getErrorCodeOperation.isExisting()).toBeFalsy();
179
- });
180
-
181
- // TODO: enable once redoc→parent version-sync ships — the elements already exist, only the
182
- // sync behavior is unimplemented.
183
- it.skip('should sync the parent topic version when the version is changed on the nested Redoc page', async () => {
184
- const child = await page.getSidebarTreeItem('Another Spec API');
185
- await child.click();
186
- await page.waitForRedocTopicLoad();
187
-
188
- const redocPicker = await page.getRedocPicker();
189
- await page.selectVersionByLabel(redocPicker!, "WI'25");
190
- await page.waitForRedocTopicLoad();
191
-
192
- page = new TopicVersionPage(`${BASE}/errors-and-rate-limits.html`);
193
- page.open();
194
- await page.waitForMarkdownTopicLoad();
195
-
196
- const parentPicker = await page.getSidebarPicker('Errors and Rate Limits');
197
- expect(await page.getSelectedVersionLabel(parentPicker!)).toEqual("WI'25");
198
- });
199
- });
200
-
201
- describe('Topic-level versioning > standalone Redoc reference', () => {
202
- // fixture: Widgets API Reference — a spec (forward-arrow) topic, so it never gets a sidebar
203
- // picker; the picker lives on the Redoc page's own project header instead.
204
- beforeEach(async () => {
205
- page = new TopicVersionPage(`${BASE}/widgets-api-reference`);
206
- page.open();
207
- await page.waitForRedocTopicLoad();
208
- });
209
-
210
- it('should render the Redoc version picker and default to the latest version', async () => {
211
- const picker = await page.getRedocPicker();
212
- expect(picker).not.toBeNull();
213
- expect(await page.getSelectedVersionLabel(picker!)).toEqual("SU'26");
214
- });
215
-
216
- it('should switch the rendered spec when a different version is selected', async () => {
217
- const picker = await page.getRedocPicker();
218
- await page.selectVersionByLabel(picker!, "SP'26");
219
- await page.waitForRedocTopicLoad();
220
-
221
- expect(await browser.getUrl()).toContain(`${BASE}/v1/widgets-api-reference`);
222
- const apiInfo = await $('>>>div.api-info');
223
- expect(await apiInfo.getText()).toContain('1.0.0');
224
- });
225
-
226
- it('should show the "Newer Version Available" banner when the version is not latest', async () => {
227
- page = new TopicVersionPage(`${BASE}/v1/widgets-api-reference`);
228
- page.open();
229
- await page.waitForRedocTopicLoad();
230
-
231
- const banner = await page.getRedocVersionBanner();
232
- expect(banner).not.toBeNull();
233
- expect(await page.getBannerTitle(banner!)).toEqual('Newer Version Available');
234
- expect(await page.getBannerLinkHref(banner!)).toContain(`${BASE}/widgets-api-reference`);
235
- });
236
-
237
- it('should not show the "Newer Version Available" banner when the version is latest', async () => {
238
- const banner = await page.getRedocVersionBanner();
239
- expect(banner).toBeNull();
240
- });
241
- });
242
-
243
- describe('Topic-level versioning > URL resolution (markdown)', () => {
244
- it('should serve the latest version at the canonical URL', async () => {
245
- page = new TopicVersionPage(`${BASE}/oauth.html`);
246
- page.open();
247
- await page.waitForMarkdownTopicLoad();
248
- expect(await (await $('>>>h1')).getText()).toEqual('OAuth 2.0');
249
- });
250
-
251
- it('should serve the latest version at its versioned alias URL', async () => {
252
- page = new TopicVersionPage(`${BASE}/v2/oauth.html`);
253
- page.open();
254
- await page.waitForMarkdownTopicLoad();
255
- expect(await (await $('>>>h1')).getText()).toEqual('OAuth 2.0');
256
- });
257
-
258
- it('should serve an older version at its versioned URL', async () => {
259
- page = new TopicVersionPage(`${BASE}/v1/oauth.html`);
260
- page.open();
261
- await page.waitForMarkdownTopicLoad();
262
- expect(await (await $('>>>h1')).getText()).toEqual('OAuth 1.0');
263
- });
264
-
265
- it('should initialize the picker from the URL version', async () => {
266
- page = new TopicVersionPage(`${BASE}/v1/oauth.html`);
267
- page.open();
268
- await page.waitForMarkdownTopicLoad();
269
-
270
- const picker = await page.getSidebarPicker('OAuth');
271
- expect(await page.getSelectedVersionLabel(picker!)).toEqual("SU'26");
272
- });
273
- });
274
-
275
- describe('Topic-level versioning > URL resolution (spec)', () => {
276
- it('should serve the latest spec at the canonical URL', async () => {
277
- page = new TopicVersionPage(`${BASE}/widgets-api-reference`);
278
- page.open();
279
- await page.waitForRedocTopicLoad();
280
- expect(await (await $('>>>div.api-info')).getText()).toContain('2.0.0');
281
- });
282
-
283
- it('should serve the latest spec at its versioned alias URL', async () => {
284
- page = new TopicVersionPage(`${BASE}/v2/widgets-api-reference`);
285
- page.open();
286
- await page.waitForRedocTopicLoad();
287
- expect(await (await $('>>>div.api-info')).getText()).toContain('2.0.0');
288
- });
289
-
290
- it('should serve an older spec at its versioned URL', async () => {
291
- page = new TopicVersionPage(`${BASE}/v1/widgets-api-reference`);
292
- page.open();
293
- await page.waitForRedocTopicLoad();
294
- expect(await (await $('>>>div.api-info')).getText()).toContain('1.0.0');
295
- });
296
- });
297
-
298
- describe('Topic-level versioning > single-version picker (read-only)', () => {
299
- // A topic with exactly one authored version still renders a picker, but read-only (no
300
- // dropdown) and never with the "Newer Version Available" banner, since a single version is
301
- // always the latest by definition.
302
- // fixture: Widgets API (Beta) — single version 'Beta'.
303
- it('should render a read-only picker with no dropdown when a Redoc spec has a single version', async () => {
304
- page = new TopicVersionPage(`${BASE}/widgets-api-beta`);
305
- page.open();
306
- await page.waitForRedocTopicLoad();
307
- const picker = await page.getRedocPicker();
308
- expect(await page.isReadOnly(picker!)).toBeTruthy();
309
- expect(await page.getRedocVersionBanner()).toBeNull();
310
- });
311
-
312
- // fixture: Link Between Topics — already single version ('WI'26'). TODO: enable once
313
- // markdown topics render a read-only picker for single-version entries (the markdown
314
- // counterpart to the Redoc case above).
315
- it.skip('should render a read-only picker with no dropdown when a markdown topic has a single version', async () => {
316
- page = new TopicVersionPage(`${BASE}/internal-linking.html`);
317
- page.open();
318
- await page.waitForMarkdownTopicLoad();
319
- const picker = await page.getSidebarPicker('Link Between Topics');
320
- expect(await page.isReadOnly(picker!)).toBeTruthy();
321
- expect(await page.getMarkdownVersionBanner()).toBeNull();
322
- });
323
- });
324
-
325
- describe('Topic-level versioning > topics without a version picker', () => {
326
- it('should not render a sidebar picker when the markdown topic is evergreen', async () => {
327
- page = new TopicVersionPage(`${BASE}/get-started.html`);
328
- page.open();
329
- await page.waitForMarkdownTopicLoad();
330
-
331
- const picker = await page.getSidebarPicker('Get Started');
332
- expect(picker).toBeNull();
333
- expect(await browser.getUrl()).not.toMatch(/\/v\d+\//);
334
- expect(await page.getMarkdownVersionBanner()).toBeNull();
335
- });
336
-
337
- // fixture: Widgets Status API — plain spec:, no versions: at all.
338
- it('should not render a picker when the Redoc spec is evergreen', async () => {
339
- page = new TopicVersionPage(`${BASE}/widgets-status-api`);
340
- page.open();
341
- await page.waitForRedocTopicLoad();
342
- expect(await page.getRedocPicker()).toBeNull();
343
- expect(await page.getRedocVersionBanner()).toBeNull();
344
- });
345
-
346
- it('should show the picker only on versioned rows when the sidebar is mixed', async () => {
347
- page = new TopicVersionPage(`${BASE}/get-started.html`);
348
- page.open();
349
- await page.waitForMarkdownTopicLoad();
350
- await page.waitForSidebar();
351
-
352
- expect(await page.getSidebarPicker('OAuth')).not.toBeNull();
353
- expect(await page.getSidebarPicker('Errors and Rate Limits')).not.toBeNull();
354
- expect(await page.getSidebarPicker('Client SDKs')).not.toBeNull();
355
- expect(await page.getSidebarPicker('Get Started')).toBeNull();
356
- expect(await page.getSidebarPicker('Widget Concepts')).toBeNull();
357
- });
358
-
359
- it('should not leak a version segment when navigating to an evergreen topic', async () => {
360
- page = new TopicVersionPage(`${BASE}/v1/oauth.html`);
361
- page.open();
362
- await page.waitForMarkdownTopicLoad();
363
-
364
- const getStarted = await page.getSidebarTreeItem('Get Started');
365
- await getStarted.click();
366
- await page.waitForMarkdownTopicLoad();
367
-
368
- expect(await browser.getUrl()).toContain(`${BASE}/get-started.html`);
369
- expect(await browser.getUrl()).not.toContain('/v1/');
370
- });
371
- });
372
-
373
- describe('Topic-level versioning > persistence on reload', () => {
374
- it('should keep the selected version and its content after a refresh', async () => {
375
- page = new TopicVersionPage(`${BASE}/oauth.html`);
376
- page.open();
377
- await page.waitForMarkdownTopicLoad();
378
-
379
- const picker = await page.getSidebarPicker('OAuth');
380
- await page.selectVersionByLabel(picker!, "SU'26");
381
- await page.waitForMarkdownTopicLoad();
382
-
383
- await browser.refresh();
384
- await page.waitForMarkdownTopicLoad();
385
-
386
- const pickerAfterReload = await page.getSidebarPicker('OAuth');
387
- expect(await page.getSelectedVersionLabel(pickerAfterReload!)).toEqual("SU'26");
388
- expect(await (await $('>>>h1')).getText()).toEqual('OAuth 1.0');
389
- expect(await browser.getUrl()).toContain(`${BASE}/v1/oauth.html`);
390
-
391
- const selections = await page.readSessionVersionSelections();
392
- expect(selections).not.toBeNull();
393
- });
394
- });
395
-
396
- describe('Topic-level versioning > invalid version in URL', () => {
397
- it('should return a 404 when the markdown URL has an invalid version', async () => {
398
- browser.url(`${BASE}/v999/oauth.html`);
399
-
400
- const errorComponent = await $('dx-error');
401
- await errorComponent.waitForDisplayed();
402
- expect(await browser.getTitle()).toEqual('404 Error | Salesforce Developers');
403
- });
404
-
405
- it('should return a 404 when the spec URL has an invalid version', async () => {
406
- browser.url(`${BASE}/v999/widgets-api-reference`);
407
-
408
- const errorComponent = await $('dx-error');
409
- await errorComponent.waitForDisplayed();
410
- expect(await browser.getTitle()).toEqual('404 Error | Salesforce Developers');
411
- });
412
- });