Skip to content

Commit 86b8633

Browse files
authored
Coveo: Handle failure to fetch credentials in UI (#438)
1 parent 85892e5 commit 86b8633

File tree

8 files changed

+77
-42
lines changed

8 files changed

+77
-42
lines changed

.github/workflows/playwright.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ on:
1010
env:
1111
FRONT_DOOR_USERNAME: ${{ secrets.FRONT_DOOR_USERNAME }}
1212
FRONT_DOOR_PASSWORD: ${{ secrets.FRONT_DOOR_PASSWORD }}
13+
COVEO_CREDENTIALS_BASE_URL: ${{ secrets.COVEO_CREDENTIALS_BASE_URL }}
1314
jobs:
1415
playwright:
1516
name: Run Playwright

assets/js/coveo.js

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -71,28 +71,38 @@ async function getValidSearchCredentials() {
7171
}
7272

7373
async function atomicCoveo() {
74-
await customElements.whenDefined('atomic-search-interface');
75-
const credentials = await getValidSearchCredentials();
76-
77-
document.querySelectorAll('atomic-search-interface').forEach(async (el) => {
78-
await el.initialize({
79-
...credentials,
80-
analytics: { analyticsMode: 'legacy' },
81-
preprocessRequest: (request) => {
82-
const body = JSON.parse(request.body);
83-
body.q = `<@- ${body.q} -@>`;
84-
request.body = JSON.stringify(body);
85-
return request;
86-
},
74+
try {
75+
await customElements.whenDefined('atomic-search-interface');
76+
const credentials = await getValidSearchCredentials();
77+
78+
document.querySelectorAll('atomic-search-interface').forEach(async (el) => {
79+
await el.initialize({
80+
...credentials,
81+
analytics: { analyticsMode: 'legacy' },
82+
preprocessRequest: (request) => {
83+
const body = JSON.parse(request.body);
84+
body.q = `<@- ${body.q} -@>`;
85+
request.body = JSON.stringify(body);
86+
return request;
87+
},
88+
});
89+
90+
// No standalone searchboxes should be getting executing first search.
91+
if (el.id === 'search-v2') await el.executeFirstSearch();
8792
});
8893

89-
// No standalone searchboxes should be getting executing first search.
90-
if (el.id === 'search-v2') await el.executeFirstSearch();
91-
});
92-
93-
const headerSearchBar = document.querySelector('#search-standalone-header');
94-
if (headerSearchBar?.shadowRoot) {
95-
hideShadowElement(headerSearchBar.shadowRoot, 'atomic-relevance-inspector');
94+
const headerSearchBar = document.querySelector('#search-standalone-header');
95+
if (headerSearchBar?.shadowRoot) {
96+
hideShadowElement(
97+
headerSearchBar.shadowRoot,
98+
'atomic-relevance-inspector'
99+
);
100+
}
101+
} catch (error) {
102+
// Handle coveo error from only a LACK of credentials.
103+
// INCORRECT credentials will cause the page to load but spin waiting.
104+
const coveoErrorContainer = document.getElementById('coveo-error-content');
105+
coveoErrorContainer.style.display = 'block';
96106
}
97107
}
98108

layouts/partials/search-error.html

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{{ $coveoEnabled := partial "get-feature-flags.html" "disable_coveo" }}
2+
3+
<div class="content" data-testid="coveo-error-content" id="coveo-error-content" style="{{- if $coveoEnabled -}}display: none;{{- else -}}{{- end -}}">
4+
<div class="not-found-container" data-testid="not-found-container">
5+
<h1 class="info-header">
6+
Search functionality is unavailable.
7+
</h1>
8+
<a href="{{ site.BaseURL | relLangURL }}" aria-label="Return home">Return to the {{ site.Title }} homepage.</a>
9+
</div>
10+
</div>

layouts/search/single.html

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,10 @@
22
{{ $coveoEnabled := partial "get-feature-flags.html" "disable_coveo" }}
33
{{ if $coveoEnabled }}
44
<section class="search no-sidebar">
5-
{{ partial "coveo-atomic.html" .}}
5+
{{ partial "coveo-atomic.html" . }}
66
</section>
7+
{{ partial "search-error.html" }}
78
{{ else }}
8-
<div class="content" data-testid="content">
9-
<div class="not-found-container" data-testid="not-found-container">
10-
<h1 class="info-header">
11-
Search functionality is unavailable.
12-
</h1>
13-
<a href="{{ .Site.BaseURL | relLangURL }}" aria-label="Return home">Return to the {{ .Site.Title }} homepage.</a>
14-
</div>
15-
</div>
9+
{{ partial "search-error.html" }}
1610
{{ end }}
1711
{{ end }}

tests/.env.example

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
FRONT_DOOR_USERNAME=
2-
FRONT_DOOR_PASSWORD=
2+
FRONT_DOOR_PASSWORD=
3+
COVEO_CREDENTIALS_BASE_URL=

tests/src/constants/constants.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
export const TIMEOUT = 4000;
2+
export const COVEO_CREDENTIALS_ENDPOINT = 'api/v1/auth/search_token';

tests/src/coveo.spec.js

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,22 @@ async function submitSearchQuery(page, query) {
1818

1919
test.describe('Coveo test', () => {
2020
test.beforeEach(async ({ page, request }) => {
21+
// Setup to start on landing page
2122
await page.goto('/');
2223
await page.waitForLoadState('load');
2324
await waitFor(async () => await handleConsentPopup(page));
24-
await mockCoveoCredentials(page, request);
25+
26+
// Conditionally mock credentials
27+
const excludedTests = ['missing coveo credentials'];
28+
if (!excludedTests.includes(test.info().title)) {
29+
await mockCoveoCredentials(page, request);
30+
}
2531
});
2632

2733
test.afterEach(async ({ page }) => {
28-
// Run basic smoke tests on all valid queries
29-
if (!test.info().title.includes('invalid search query')) {
34+
// Conditionally run a smoke test only on valid queries
35+
const excludedTests = ['invalid search query', 'missing coveo credentials'];
36+
if (!excludedTests.includes(test.info().title)) {
3037
await runSmokeTestCoveo(page);
3138
}
3239
});
@@ -57,4 +64,12 @@ test.describe('Coveo test', () => {
5764
await page.reload();
5865
expect(page.url()).toContain(endpoint);
5966
});
67+
68+
test('missing coveo credentials', async ({ page }) => {
69+
const searchEndpoint = 'search.html';
70+
await page.goto(`/${searchEndpoint}`);
71+
72+
const coveoErrorContent = page.getByTestId('coveo-error-content');
73+
await expect(coveoErrorContent).toBeVisible();
74+
});
6075
});

tests/src/mock/coveo.mock.js

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { expect } from '@playwright/test';
2+
import { COVEO_CREDENTIALS_ENDPOINT } from '../constants';
23

34
export const mockCoveoData = {
45
validQuery: 'proxy',
@@ -8,24 +9,26 @@ export const mockCoveoData = {
89

910
export async function mockCoveoCredentials(page, request) {
1011
// Get credentials
11-
const tokenBaseURL = 'https://docs-dev.nginx.com';
12-
const tokenEndpoint = '/api/v1/auth/search_token';
1312
const username = process.env.FRONT_DOOR_USERNAME;
1413
const password = process.env.FRONT_DOOR_PASSWORD;
15-
const response = await request.get(tokenBaseURL + tokenEndpoint, {
16-
headers: {
17-
Authorization:
18-
'Basic ' + Buffer.from(`${username}:${password}`).toString('base64'),
19-
},
20-
});
14+
const baseURL = process.env.COVEO_CREDENTIALS_BASE_URL;
15+
const response = await request.get(
16+
`${baseURL}/${COVEO_CREDENTIALS_ENDPOINT}`,
17+
{
18+
headers: {
19+
Authorization:
20+
'Basic ' + Buffer.from(`${username}:${password}`).toString('base64'),
21+
},
22+
}
23+
);
2124

2225
expect(response.ok()).toBeTruthy();
2326
expect(response.status()).toBe(200);
2427

2528
const credentials = await response.json();
2629

2730
// Mock the local request to be successful, then reload the page.
28-
await page.route(`**${tokenEndpoint}`, async (route) => {
31+
await page.route(`**/${COVEO_CREDENTIALS_ENDPOINT}`, async (route) => {
2932
await route.fulfill({
3033
status: 200,
3134
contentType: 'application/json',

0 commit comments

Comments
 (0)