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
39 changes: 26 additions & 13 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ jobs:
test:
runs-on:
- codebuild-defguard-core-runner-${{ github.run_id }}-${{ github.run_attempt }}
instance-size:2xlarge
outputs:
tests_ok: ${{ steps.run-test.outcome == 'success' }}
instance-size:medium
strategy:
fail-fast: false
matrix:
shard: [1, 2, 3, 4, 5, 6, 7, 8]

steps:
- uses: actions/checkout@v6
Expand All @@ -30,13 +32,10 @@ jobs:

- name: Export image tag
run: |
# strip "refs/heads.” to get just the branch name
BRANCH=${GITHUB_REF#refs/heads/}
if [[ "$BRANCH" == release/* ]]; then
# replace '/' with '-' on release branches
IMAGE_TAG=${BRANCH//\//-}
else
# main/dev branch
IMAGE_TAG=$BRANCH
fi
echo "IMAGE_TAG=$IMAGE_TAG" >> $GITHUB_ENV
Expand Down Expand Up @@ -76,33 +75,47 @@ jobs:
working-directory: ./e2e
run: pnpm install --frozen-lockfile

- name: Cache Playwright browsers
id: playwright-cache
uses: actions/cache@v5
with:
path: ~/.cache/ms-playwright
key: ${{ runner.os }}-playwright-${{ hashFiles('e2e/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-playwright-

- name: Install playwright chromium
working-directory: ./e2e
run: npx playwright install chromium
run: |
if [[ "${{ steps.playwright-cache.outputs.cache-hit }}" == "true" ]]; then
# Browsers are cached; only install missing system dependencies.
npx playwright install-deps chromium
else
npx playwright install --with-deps chromium
fi

- name: run tests
id: run-test
continue-on-error: true
working-directory: ./e2e
env:
DEFGUARD_LICENSE_KEY: ${{ secrets.DEFGUARD_LICENSE_KEY }}
run: pnpm test
run: pnpm test --shard=${{ matrix.shard }}/8

- name: Stop compose
if: always()
run: docker compose --file './docker-compose.e2e.yaml' down

- uses: actions/upload-artifact@v7
if: steps.run-test.outcome == 'failure'
if: failure()
with:
name: playwright-report
name: playwright-report-shard-${{ matrix.shard }}
path: |
./e2e/playwright-report
retention-days: 7

trigger-dev-deploy:
needs: test
if: ${{ github.event_name != 'pull_request' && github.ref_name == 'dev' }}
if: ${{ github.event_name != 'pull_request' && github.ref_name == 'dev' && needs.test.result == 'success' }}
uses: ./.github/workflows/dev-deployment.yml
secrets: inherit

Expand All @@ -111,6 +124,6 @@ jobs:
if: |
github.event_name != 'pull_request' &&
startsWith(github.ref_name, 'release/') &&
needs.test.outputs.tests_ok == 'true'
needs.test.result == 'success'
uses: ./.github/workflows/staging-deployment.yml
secrets: inherit
1 change: 1 addition & 0 deletions docker-compose.e2e.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ services:
DEFGUARD_DB_PASSWORD: defguard
DEFGUARD_DB_NAME: defguard
DEFGUARD_URL: http://localhost:8000
DEFGUARD_LICENSE_KEY: ${DEFGUARD_LICENSE_KEY:-}
RUST_BACKTRACE: 1
ports:
- "8000:8000"
Expand Down
2 changes: 1 addition & 1 deletion e2e/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const defaultConfig: TestsConfig = {
BASE_URL: 'http://localhost:8000',
CORE_BASE_URL: 'http://localhost:8000/api/v1',
ENROLLMENT_URL: 'http://localhost:8080',
TEST_TIMEOUT: 240,
TEST_TIMEOUT: 60,
};

const envConfig: Partial<TestsConfig> = {
Expand Down
8 changes: 8 additions & 0 deletions e2e/playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ export default defineConfig({
globalSetup: './utils/globalSetup',
timeout: testsConfig.TEST_TIMEOUT * 1000,
testDir: './tests',
// Exclude files that consist entirely of skipped tests to avoid Playwright
// collecting and reporting them as empty suites on every shard.
testIgnore: [
'**/enrollment.spec.ts',
'**/externalopenid.spec.ts',
'**/externalopenidmfa.spec.ts',
'**/openid.spec.ts',
],
/* Run tests in files in parallel */
fullyParallel: false,
/* Fail the build on CI if you accidentally left test.only in the source code. */
Expand Down
63 changes: 63 additions & 0 deletions e2e/tests/apiTokens.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { expect, test } from '@playwright/test';

import { defaultUserAdmin, routes, testUserTemplate } from '../config';
import { User } from '../types';
import { createUser } from '../utils/controllers/createUser';
import { loginBasic } from '../utils/controllers/login';
import { dockerRestart } from '../utils/docker';
import { waitForBase } from '../utils/waitForBase';

test.describe('API tokens management', () => {
let testUser: User;
const token_name = 'test token name';

test.beforeEach(() => {
dockerRestart();
testUser = { ...testUserTemplate, username: 'test' };
});

test('Add API token as default admin', async ({ page }) => {
await waitForBase(page);
await loginBasic(page, defaultUserAdmin);
await page.goto(
routes.base + routes.profile + defaultUserAdmin.username + routes.tab.api_tokens,
);
await page.getByTestId('add-token').click();
await page.getByTestId('field-name').fill(token_name);
await page.getByTestId('submit').click();
const api_token = await page.getByTestId('copy-field').textContent();
await page.getByTestId('close').click();

const row = await page
.locator('.table-row-container')
.filter({ hasText: token_name });
await row.locator('.icon-button').click();
await page.getByTestId('delete').click();
await page.locator('button[data-variant="critical"]').click();
await expect(row).not.toBeVisible();
expect(api_token).toBeDefined();
});

test('Add API token as new user with admin privileges', async ({ page, browser }) => {
await waitForBase(page);
await createUser(browser, testUser, ['admin']);
await loginBasic(page, testUser);
await page.goto(
routes.base + routes.profile + testUser.username + routes.tab.api_tokens,
);
await page.getByTestId('add-token').click();
await page.getByTestId('field-name').fill(token_name);
await page.getByTestId('submit').click();
const api_token = await page.getByTestId('copy-field').textContent();
await page.getByTestId('close').click();

const row = await page
.locator('.table-row-container')
.filter({ hasText: token_name });
await row.locator('.icon-button').click();
await page.getByTestId('delete').click();
await page.locator('button[data-variant="critical"]').click();
await expect(row).not.toBeVisible();
expect(api_token).toBeDefined();
});
});
Loading
Loading