Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/playwright.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ on:
- 'main'
pull_request:

env:
FRONT_DOOR_USERNAME: ${{ secrets.FRONT_DOOR_USERNAME }}
FRONT_DOOR_PASSWORD: ${{ secrets.FRONT_DOOR_PASSWORD }}
jobs:
playwright:
name: Run Playwright
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*.key
*~
\#*
.env

# OS Specific #
###############
Expand Down Expand Up @@ -34,6 +35,7 @@ exampleSite/hugo
/coverage
*/test-results
*/playwright-report
*.bkp

# Biome
biome.rb
Expand Down
2 changes: 1 addition & 1 deletion layouts/partials/coveo-atomic.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<atomic-search-interface id="search-v2">
<atomic-search-interface id="search-v2" data-testid="search-results-page">
<atomic-search-layout>
<!-- Search/Metadata Section -->
<atomic-layout-section section="search">
Expand Down
2 changes: 2 additions & 0 deletions tests/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
FRONT_DOOR_USERNAME=
FRONT_DOOR_PASSWORD=
16 changes: 15 additions & 1 deletion tests/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion tests/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@
"name": "nginx-docs-theme-test",
"version": "1.0.0",
"private": "true",
"scripts": {
"test": "playwright test --workers 6"
},
"devDependencies": {
"@playwright/test": "1.48.0"
"@playwright/test": "1.48.0",
"dotenv": "^17.2.3"
}
}
9 changes: 7 additions & 2 deletions tests/playwright.config.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import { defineConfig, devices } from '@playwright/test';
import dotenv from 'dotenv';

const BASE_URL = 'http://127.0.0.1';
const PORT = 1313;

dotenv.config({ quiet: true });

export default defineConfig({
testDir: './src',
fullyParallel: true,
workers: 1,
outputDir: './test-results',
snapshotPathTemplate: '{testDir}/__screenshots__/{testFilePath}/{arg}{ext}',
reporter: [['html', { outputFolder: './playwright-report' }]],
reporter: [['html', { open: 'never', outputFolder: './playwright-report' }]],
use: {
baseURL: `${BASE_URL}:${PORT}`,
screenshots: 'only-on-failure',
Expand All @@ -29,9 +33,10 @@ export default defineConfig({
},
],
webServer: {
command: `cd ../exampleSite && hugo mod get && hugo --gc --config hugo.toml,hugo.test.toml && hugo serve --port ${PORT} --config hugo.toml,hugo.test.toml`,
command: `cd ../exampleSite && sed -i.bkp 's/disable_coveo = .*/disable_coveo = false/' hugo.toml && hugo mod get && hugo --gc --config hugo.toml,hugo.test.toml && hugo serve --port ${PORT} --config hugo.toml,hugo.test.toml`,
url: `${BASE_URL}:${PORT}`,
stdout: 'ignore',
reuseExistingServer: !process.env.CI,
},
expect: {
toHaveScreenshot: {
Expand Down
60 changes: 60 additions & 0 deletions tests/src/coveo.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { expect, test } from '@playwright/test';
import { mockCoveoCredentials, mockCoveoData } from './mock';
import {
buildURLFragment,
handleConsentPopup,
runSmokeTestCoveo,
waitFor,
} from './utils';

async function submitSearchQuery(page, query) {
const headerSearchBarContainer = page.getByTestId('header__search');
const searchBar = headerSearchBarContainer.locator('[part="textarea"]');
await searchBar.fill(query);
await page.keyboard.press('Enter');
await page.waitForURL(`**/search.html#q=${query}`);
await page.waitForSelector('#search-v2');
}

test.describe('Coveo test', () => {
test.beforeEach(async ({ page, request }) => {
await page.goto('/');
await page.waitForLoadState('load');
await waitFor(async () => await handleConsentPopup(page));
await mockCoveoCredentials(page, request);
});

test.afterEach(async ({ page }) => {
// Run basic smoke tests on all valid queries
if (!test.info().title.includes('invalid search query')) {
await runSmokeTestCoveo(page);
}
});

test('valid search query', async ({ page }) => {
await submitSearchQuery(page, mockCoveoData.validQuery);
});

test('invalid search query', async ({ page }) => {
await submitSearchQuery(page, mockCoveoData.invalidQuery);
const resultsPage = page.getByTestId('search-results-page');
const main = resultsPage.locator('atomic-layout-section[section="main"]');
const noResultsMessage = main.locator('[part="no-results"]');
await expect(noResultsMessage).toBeVisible();
});

test('inbound link do not reset URL', async ({ page }) => {
// Use ONLY generic filters. Do not add any product specific filters, particularly from the facet.
// If these basic filters work, then its safe to assume, adding facet filters will not reset the URL.
const endpoint = `/search.html#q=${mockCoveoData.validQuery}${buildURLFragment(mockCoveoData.filters)}`;
await page.goto(endpoint);
await page.waitForSelector('#search-v2');

// should retain the same link instead of resetting
expect(page.url()).toContain(endpoint);

// reloading should retain the same link instead of resetting
await page.reload();
expect(page.url()).toContain(endpoint);
});
});
36 changes: 36 additions & 0 deletions tests/src/mock/coveo.mock.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { expect } from '@playwright/test';

export const mockCoveoData = {
validQuery: 'proxy',
invalidQuery: 'abcdefghijkl',
filters: ['numberOfResults=100', 'sortCriteria=date descending'],
};

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'),
},
});

expect(response.ok()).toBeTruthy();
expect(response.status()).toBe(200);

const credentials = await response.json();

// Mock the local request to be successful, then reload the page.
await page.route(`**${tokenEndpoint}`, async (route) => {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify(credentials),
});
});
await page.reload();
}
1 change: 1 addition & 0 deletions tests/src/mock/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './coveo.mock';
19 changes: 19 additions & 0 deletions tests/src/utils/utils.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
import { expect } from '@playwright/test';
import { TIMEOUT } from '../constants';

export async function runSmokeTestCoveo(page) {
// Separate into variables in case we need to decouple from Coveo Atomic, but we still keep the basic structure of a search page.
const resultsPage = page.getByTestId('search-results-page');
const status = resultsPage.locator('atomic-layout-section[section="status"]');
const facet = resultsPage.locator('atomic-layout-section[section="facets"]');
const main = resultsPage.locator('atomic-layout-section[section="main"]');

await expect(status).toBeVisible();
await expect(facet).toBeVisible();
await expect(main).toBeVisible();
}

export async function runSmokeTestOnPage(page) {
/* Ensure each page follows the following dom structure */
await expect(await page.getByTestId('content').count()).toBeTruthy();
Expand All @@ -27,3 +39,10 @@ export const waitFor = async function waitFor(f, ftimeout = TIMEOUT) {
while (!f()) await sleep(ftimeout);
return f();
};

export function buildURLFragment(fragments) {
return fragments
.map((fragment) => `&${fragment}`)
.join('')
.replaceAll(' ', '%20');
}
Loading