Skip to content

Signed in user e2e test #14109

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
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
10 changes: 9 additions & 1 deletion .github/workflows/cicd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,20 @@
with:
container-image: ${{ needs.container.outputs.container-image }}

playwright-secure:
needs: [container]
uses: ./.github/workflows/playwright-secure.yml
with:
container-image: ${{ needs.container.outputs.container-image }}
secrets:
IDAPI_CLIENT_ACCESS_TOKEN: ${{ secrets.IDAPI_CLIENT_ACCESS_TOKEN }}

publish:

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {}
permissions:
id-token: write
contents: read
pull-requests: write # required by riff-raff action
needs: [container, prettier, jest, lint, playwright]
needs: [container, prettier, jest, lint, playwright, playwright-secure]
uses: ./.github/workflows/publish.yml
with:
container-image: ${{ needs.container.outputs.container-image }}
Expand Down
102 changes: 102 additions & 0 deletions .github/workflows/playwright-secure.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
on:
workflow_call:
secrets:
IDAPI_CLIENT_ACCESS_TOKEN:
required: true
inputs:
container-image:
description: 'Image used by DCR service'
required: true
type: string

permissions:
contents: read

jobs:
playwright-secure:
runs-on: ubuntu-latest
services:
DCR:
image: ${{ inputs.container-image }}
ports:
- 9000:9000
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Node environment
uses: ./.github/actions/setup-node-env

- name: Set up nginx secure server
run: |
# Turn off mandb updates
sudo mv /usr/bin/mandb /usr/bin/mandb-OFF
sudo cp -p /bin/true /usr/bin/mandb
# Update dependencies
sudo apt-get update -y
# Install libnss3-tools
sudo apt-get install -y libnss3-tools
# Install mkcert
wget -q https://github.com/FiloSottile/mkcert/releases/download/v1.4.3/mkcert-v1.4.3-linux-amd64
sudo cp mkcert-v1.4.3-linux-amd64 /usr/local/bin/mkcert
sudo chmod +x /usr/local/bin/mkcert
# Install dev-nginx
wget -q https://github.com/guardian/dev-nginx/releases/latest/download/dev-nginx.tar.gz
sudo mkdir -p /usr/local/bin/dev-nginx
sudo tar -xzf dev-nginx.tar.gz -C /usr/local
sudo chmod +x /usr/local/bin/dev-nginx
# Create https://r.thegulocal.com to proxy to locahost:9000
sudo ./dotcom-rendering/scripts/nginx/setup-ci.sh
# Move the dotcom-rendering nginx config to the right place for GitHub runners and restart nginx
sudo cp /etc/nginx/servers/dotcom-rendering.conf /etc/nginx/conf.d/dotcom-rendering.conf
sudo dev-nginx restart-nginx

- name: Check local server
run: |
STATUS=$(curl -o /dev/null -s -w "%{http_code}" -k http://localhost:9000)
echo "HTTP Status: $STATUS"
if [ "$STATUS" -eq 200 ]; then
echo "Local server is reachable with status 200"
else
echo "Local server is not reachable. Status: $STATUS"
exit 1
fi

- name: Check secure server
run: |
STATUS=$(curl -o /dev/null -s -w "%{http_code}" -k https://r.thegulocal.com)
echo "HTTPS Status: $STATUS"
if [ "$STATUS" -eq 200 ]; then
echo "Secure server is reachable with status 200"
else
echo "Secure server is not reachable. Status: $STATUS"
exit 1
fi

- name: Check IDAPI_CLIENT_ACCESS_TOKEN is set
run: |
if [ -z "$IDAPI_CLIENT_ACCESS_TOKEN" ]; then
echo "IDAPI_CLIENT_ACCESS_TOKEN is empty or undefined"
else
echo "IDAPI_CLIENT_ACCESS_TOKEN is defined"
fi
env:
IDAPI_CLIENT_ACCESS_TOKEN: ${{ secrets.IDAPI_CLIENT_ACCESS_TOKEN }}

- name: Install Playwright Browsers
run: pnpm playwright install --with-deps chromium
working-directory: ./dotcom-rendering

- name: Run Playwright
run: pnpm playwright test e2e.secure.spec.ts
working-directory: ./dotcom-rendering
env:
NODE_TLS_REJECT_UNAUTHORIZED: 0
IDAPI_CLIENT_ACCESS_TOKEN: ${{ secrets.IDAPI_CLIENT_ACCESS_TOKEN }}

- uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report-secure
path: ./dotcom-rendering/playwright-report
retention-days: 5
7 changes: 5 additions & 2 deletions .github/workflows/playwright.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@ on:
required: true
type: string

permissions:
contents: read

jobs:
playwright:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
# keep aligned with the number of shards used by `playwright test`
# keep aligned with --shard parameter below
group: [1, 2, 3, 4, 5, 6, 7, 8]
services:
DCR:
Expand All @@ -31,7 +34,7 @@ jobs:
working-directory: ./dotcom-rendering

- name: Run Playwright
run: pnpm playwright test --shard=${{ matrix.group }}/8
run: pnpm playwright test e2e.spec.ts --shard=${{ matrix.group }}/8
working-directory: ./dotcom-rendering
env:
NODE_ENV: production
Expand Down
11 changes: 6 additions & 5 deletions dotcom-rendering/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { defineConfig, devices } from '@playwright/test';

const isDev = process.env.NODE_ENV !== 'production';
/**
* The server port for local development or CI
*/
export const isCI = !!process.env.CI;
export const isDev = !isCI;
export const PORT = isDev ? 3030 : 9000;
export const ORIGIN = `http://localhost:${PORT}`;
export const ORIGIN_SECURE = `https://r.thegulocal.com`;

/**
* See https://playwright.dev/docs/test-configuration.
Expand All @@ -30,6 +30,7 @@ export default defineConfig({
video: {
mode: 'retain-on-failure',
},
ignoreHTTPSErrors: true,
},
// Configure projects for major browsers
projects: [
Expand All @@ -41,7 +42,7 @@ export default defineConfig({
webServer: {
// On CI the server is already started so a no-op
command: isDev ? 'make dev' : ':',
url: `http://localhost:${PORT}`,
url: ORIGIN,
reuseExistingServer: true,
stdout: 'pipe',
stderr: 'pipe',
Expand Down
102 changes: 58 additions & 44 deletions dotcom-rendering/playwright/lib/load-page.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
import type { Page } from '@playwright/test';
import { PORT } from 'playwright.config';
import { ORIGIN, ORIGIN_SECURE } from '../../playwright.config';
import type { FEArticle } from '../../src/frontend/feArticle';
import { validateAsFEArticle } from '../../src/model/validate';

const BASE_URL = `http://localhost:${PORT}`;
type LoadPageOptions = {
queryParams?: Record<string, string>;
queryParamsOn?: boolean;
fragment?: `#${string}`;
waitUntil?: 'domcontentloaded' | 'load';
region?: 'GB' | 'US' | 'AU' | 'INT';
preventSupportBanner?: boolean;
useSecure?: boolean;
};

const getOrigin = (useSecure?: boolean): string =>
useSecure ? ORIGIN_SECURE : ORIGIN;

/**
* Loads a page in Playwright and centralises setup
Expand All @@ -17,16 +28,11 @@ const loadPage = async ({
waitUntil = 'domcontentloaded',
region = 'GB',
preventSupportBanner = true,
useSecure = false,
}: {
page: Page;
path: string;
queryParams?: Record<string, string>;
queryParamsOn?: boolean;
fragment?: `#${string}`;
waitUntil?: 'domcontentloaded' | 'load';
region?: 'GB' | 'US' | 'AU' | 'INT';
preventSupportBanner?: boolean;
}): Promise<void> => {
} & LoadPageOptions): Promise<void> => {
await page.addInitScript(
(args) => {
// Set the geo region, defaults to GB
Expand Down Expand Up @@ -57,9 +63,12 @@ const loadPage = async ({

// The default Playwright waitUntil: 'load' ensures all requests have completed
// Use 'domcontentloaded' to speed up tests and prevent hanging requests from timing out tests
await page.goto(`${BASE_URL}${path}${paramsString}${fragment ?? ''}`, {
waitUntil,
});
await page.goto(
`${getOrigin(useSecure)}${path}${paramsString}${fragment ?? ''}`,
{
waitUntil,
},
);
};

/**
Expand All @@ -73,33 +82,37 @@ const loadPageWithOverrides = async (
configOverrides?: Record<string, unknown>;
switchOverrides?: Record<string, unknown>;
},
options?: LoadPageOptions,
): Promise<void> => {
const path = `/Article`;
await page.route(`${BASE_URL}${path}`, async (route) => {
const postData = {
...article,
config: {
...article.config,
...overrides?.configOverrides,
switches: {
...article.config.switches,
...overrides?.switchOverrides,
await page.route(
`${getOrigin(options?.useSecure)}${path}`,
async (route) => {
const postData = {
...article,
config: {
...article.config,
...overrides?.configOverrides,
switches: {
...article.config.switches,
...overrides?.switchOverrides,
},
},
},
};
await route.continue({
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
postData,
});
});
await loadPage({ page, path, queryParamsOn: false });
};
await route.continue({
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
postData,
});
},
);
await loadPage({ page, path, queryParamsOn: false, ...options });
};

/**
* Fetch the page json from PROD then load it as a POST with overrides
* Fetch the page json from the provided URL then load it locally as a POST with overrides
*/
const fetchAndloadPageWithOverrides = async (
page: Page,
Expand All @@ -108,21 +121,22 @@ const fetchAndloadPageWithOverrides = async (
configOverrides?: Record<string, unknown>;
switchOverrides?: Record<string, unknown>;
},
options?: LoadPageOptions,
): Promise<void> => {
const article = validateAsFEArticle(
await fetch(`${url}.json?dcr`).then((res) => res.json()),
);
await loadPageWithOverrides(page, article, {
configOverrides: overrides?.configOverrides,
switchOverrides: {
...overrides?.switchOverrides,
await loadPageWithOverrides(
page,
article,
{
configOverrides: overrides?.configOverrides,
switchOverrides: {
...overrides?.switchOverrides,
},
},
});
options,
);
};

export {
BASE_URL,
fetchAndloadPageWithOverrides,
loadPage,
loadPageWithOverrides,
};
export { fetchAndloadPageWithOverrides, loadPage, loadPageWithOverrides };
16 changes: 16 additions & 0 deletions dotcom-rendering/playwright/lib/secure-server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { ORIGIN_SECURE } from '../../playwright.config';

const isSecureServerAvailable = async (): Promise<boolean> => {
try {
const response = await fetch(ORIGIN_SECURE, {
method: 'HEAD',
});
return response.status === 200;
} catch (error) {
// eslint-disable-next-line no-console -- test code
console.error('Error in isSecureServerAvailable:', error);
return false;
}
};

export { isSecureServerAvailable };
Loading