diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 0f8b428a..2b520c06 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -10,6 +10,7 @@ on: env: FRONT_DOOR_USERNAME: ${{ secrets.FRONT_DOOR_USERNAME }} FRONT_DOOR_PASSWORD: ${{ secrets.FRONT_DOOR_PASSWORD }} + COVEO_CREDENTIALS_BASE_URL: ${{ secrets.COVEO_CREDENTIALS_BASE_URL }} jobs: playwright: name: Run Playwright diff --git a/assets/js/coveo.js b/assets/js/coveo.js index b2579d65..e9888dd0 100644 --- a/assets/js/coveo.js +++ b/assets/js/coveo.js @@ -71,28 +71,38 @@ async function getValidSearchCredentials() { } async function atomicCoveo() { - await customElements.whenDefined('atomic-search-interface'); - const credentials = await getValidSearchCredentials(); - - document.querySelectorAll('atomic-search-interface').forEach(async (el) => { - await el.initialize({ - ...credentials, - analytics: { analyticsMode: 'legacy' }, - preprocessRequest: (request) => { - const body = JSON.parse(request.body); - body.q = `<@- ${body.q} -@>`; - request.body = JSON.stringify(body); - return request; - }, + try { + await customElements.whenDefined('atomic-search-interface'); + const credentials = await getValidSearchCredentials(); + + document.querySelectorAll('atomic-search-interface').forEach(async (el) => { + await el.initialize({ + ...credentials, + analytics: { analyticsMode: 'legacy' }, + preprocessRequest: (request) => { + const body = JSON.parse(request.body); + body.q = `<@- ${body.q} -@>`; + request.body = JSON.stringify(body); + return request; + }, + }); + + // No standalone searchboxes should be getting executing first search. + if (el.id === 'search-v2') await el.executeFirstSearch(); }); - // No standalone searchboxes should be getting executing first search. - if (el.id === 'search-v2') await el.executeFirstSearch(); - }); - - const headerSearchBar = document.querySelector('#search-standalone-header'); - if (headerSearchBar?.shadowRoot) { - hideShadowElement(headerSearchBar.shadowRoot, 'atomic-relevance-inspector'); + const headerSearchBar = document.querySelector('#search-standalone-header'); + if (headerSearchBar?.shadowRoot) { + hideShadowElement( + headerSearchBar.shadowRoot, + 'atomic-relevance-inspector' + ); + } + } catch (error) { + // Handle coveo error from only a LACK of credentials. + // INCORRECT credentials will cause the page to load but spin waiting. + const coveoErrorContainer = document.getElementById('coveo-error-content'); + coveoErrorContainer.style.display = 'block'; } } diff --git a/layouts/partials/search-error.html b/layouts/partials/search-error.html new file mode 100644 index 00000000..4a3b0418 --- /dev/null +++ b/layouts/partials/search-error.html @@ -0,0 +1,10 @@ +{{ $coveoEnabled := partial "get-feature-flags.html" "disable_coveo" }} + +
+
+

+ Search functionality is unavailable. +

+ Return to the {{ site.Title }} homepage. +
+
\ No newline at end of file diff --git a/layouts/search/single.html b/layouts/search/single.html index 7744a93f..37a86558 100644 --- a/layouts/search/single.html +++ b/layouts/search/single.html @@ -2,16 +2,10 @@ {{ $coveoEnabled := partial "get-feature-flags.html" "disable_coveo" }} {{ if $coveoEnabled }} + {{ partial "search-error.html" }} {{ else }} -
-
-

- Search functionality is unavailable. -

- Return to the {{ .Site.Title }} homepage. -
-
+ {{ partial "search-error.html" }} {{ end }} {{ end }} diff --git a/tests/.env.example b/tests/.env.example index dadd2b05..ccf6dc3f 100644 --- a/tests/.env.example +++ b/tests/.env.example @@ -1,2 +1,3 @@ FRONT_DOOR_USERNAME= -FRONT_DOOR_PASSWORD= \ No newline at end of file +FRONT_DOOR_PASSWORD= +COVEO_CREDENTIALS_BASE_URL= \ No newline at end of file diff --git a/tests/src/constants/constants.js b/tests/src/constants/constants.js index e731fc44..1f144fb6 100644 --- a/tests/src/constants/constants.js +++ b/tests/src/constants/constants.js @@ -1 +1,2 @@ export const TIMEOUT = 4000; +export const COVEO_CREDENTIALS_ENDPOINT = 'api/v1/auth/search_token'; diff --git a/tests/src/coveo.spec.js b/tests/src/coveo.spec.js index ce09f659..6bb8cb19 100644 --- a/tests/src/coveo.spec.js +++ b/tests/src/coveo.spec.js @@ -18,15 +18,22 @@ async function submitSearchQuery(page, query) { test.describe('Coveo test', () => { test.beforeEach(async ({ page, request }) => { + // Setup to start on landing page await page.goto('/'); await page.waitForLoadState('load'); await waitFor(async () => await handleConsentPopup(page)); - await mockCoveoCredentials(page, request); + + // Conditionally mock credentials + const excludedTests = ['missing coveo credentials']; + if (!excludedTests.includes(test.info().title)) { + await mockCoveoCredentials(page, request); + } }); test.afterEach(async ({ page }) => { - // Run basic smoke tests on all valid queries - if (!test.info().title.includes('invalid search query')) { + // Conditionally run a smoke test only on valid queries + const excludedTests = ['invalid search query', 'missing coveo credentials']; + if (!excludedTests.includes(test.info().title)) { await runSmokeTestCoveo(page); } }); @@ -57,4 +64,12 @@ test.describe('Coveo test', () => { await page.reload(); expect(page.url()).toContain(endpoint); }); + + test('missing coveo credentials', async ({ page }) => { + const searchEndpoint = 'search.html'; + await page.goto(`/${searchEndpoint}`); + + const coveoErrorContent = page.getByTestId('coveo-error-content'); + await expect(coveoErrorContent).toBeVisible(); + }); }); diff --git a/tests/src/mock/coveo.mock.js b/tests/src/mock/coveo.mock.js index 627909ba..79c6ca05 100644 --- a/tests/src/mock/coveo.mock.js +++ b/tests/src/mock/coveo.mock.js @@ -1,4 +1,5 @@ import { expect } from '@playwright/test'; +import { COVEO_CREDENTIALS_ENDPOINT } from '../constants'; export const mockCoveoData = { validQuery: 'proxy', @@ -8,16 +9,18 @@ export const mockCoveoData = { export async function mockCoveoCredentials(page, request) { // Get credentials - const tokenBaseURL = 'https://docs-dev.nginx.com'; - const tokenEndpoint = '/api/v1/auth/search_token'; const username = process.env.FRONT_DOOR_USERNAME; const password = process.env.FRONT_DOOR_PASSWORD; - const response = await request.get(tokenBaseURL + tokenEndpoint, { - headers: { - Authorization: - 'Basic ' + Buffer.from(`${username}:${password}`).toString('base64'), - }, - }); + const baseURL = process.env.COVEO_CREDENTIALS_BASE_URL; + const response = await request.get( + `${baseURL}/${COVEO_CREDENTIALS_ENDPOINT}`, + { + headers: { + Authorization: + 'Basic ' + Buffer.from(`${username}:${password}`).toString('base64'), + }, + } + ); expect(response.ok()).toBeTruthy(); expect(response.status()).toBe(200); @@ -25,7 +28,7 @@ export async function mockCoveoCredentials(page, request) { const credentials = await response.json(); // Mock the local request to be successful, then reload the page. - await page.route(`**${tokenEndpoint}`, async (route) => { + await page.route(`**/${COVEO_CREDENTIALS_ENDPOINT}`, async (route) => { await route.fulfill({ status: 200, contentType: 'application/json',