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
1 change: 0 additions & 1 deletion .github/workflows/cd-production.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ jobs:
with:
publish-dir: './docs-dist'
production-deploy: true
github-token: ${{ secrets.GITHUB_TOKEN }}
deploy-message: "Production deployment"
env:
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
Expand Down
1 change: 0 additions & 1 deletion .github/workflows/cd-staging.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ jobs:
with:
publish-dir: './docs-dist'
production-deploy: false
github-token: ${{ secrets.GITHUB_TOKEN }}
deploy-message: "Staging deployment from develop"
alias: staging
env:
Expand Down
11 changes: 11 additions & 0 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ jobs:
name: E2E (chromium)
runs-on: ubuntu-latest
timeout-minutes: 30
env:
E2E_COVERAGE: '1'
steps:
- uses: actions/checkout@v6
- name: Setup Node.js
Expand All @@ -27,6 +29,15 @@ jobs:
run: npm run build
- name: Run E2E tests
run: npx playwright test --project=chromium
- name: Merge E2E coverage
run: node scripts/merge-e2e-coverage.js
- name: Upload E2E coverage to Codecov
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./coverage-e2e/lcov.info
flags: e2e
fail_ci_if_error: false
- name: Upload report
if: always()
uses: actions/upload-artifact@v6
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ out

# Testing
coverage
coverage-e2e
.nyc_output
playwright-report
test-results
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

[![CI](https://github.com/OpenSyntaxHQ/autodocs/workflows/CI/badge.svg)](https://github.com/OpenSyntaxHQ/autodocs/actions)
[![codecov](https://codecov.io/gh/OpenSyntaxHQ/autodocs/branch/main/graph/badge.svg)](https://codecov.io/gh/OpenSyntaxHQ/autodocs)
[![npm version](https://badge.fury.io/js/@opensyntaxhq%2Fautodocs.svg)](https://www.npmjs.com/package/@opensyntaxhq/autodocs)
[![npm version](https://img.shields.io/npm/v/%40opensyntaxhq%2Fautodocs?logo=npm&color=cb3837)](https://www.npmjs.com/package/@opensyntaxhq/autodocs)
[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)

## Features
Expand Down
2 changes: 1 addition & 1 deletion e2e/accessibility.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { test, expect } from '@playwright/test';
import { test, expect } from './coverage';
import AxeBuilder from '@axe-core/playwright';

test('homepage has no critical accessibility violations', async ({ page }) => {
Expand Down
76 changes: 76 additions & 0 deletions e2e/coverage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { test as base, expect, type Page, type TestInfo } from '@playwright/test';
import { createHash } from 'crypto';
import fs from 'fs';
import path from 'path';
import v8toIstanbul from 'v8-to-istanbul';

const COVERAGE_ENV = 'E2E_COVERAGE';
const ROOT_DIR = process.cwd();
const DOCS_DIST = path.join(ROOT_DIR, 'docs-dist');
const RAW_DIR = path.join(ROOT_DIR, 'coverage-e2e', 'raw');

function shouldCollect(testInfo: TestInfo): boolean {
return process.env[COVERAGE_ENV] === '1' && testInfo.project.name === 'chromium';
}

function getOutputPath(testInfo: TestInfo): string {
const hash = createHash('sha256').update(testInfo.testId).digest('hex').slice(0, 12);
return path.join(
RAW_DIR,
`${testInfo.project.name}-${String(testInfo.workerIndex)}-${hash}.json`
);
}

async function convertCoverage(page: Page, testInfo: TestInfo): Promise<void> {
const entries = await page.coverage.stopJSCoverage();
const coverageMap: Record<string, unknown> = {};

for (const entry of entries) {
if (!entry.url || !entry.url.startsWith('http')) {
continue;
}

const url = new URL(entry.url);
if (!url.pathname.endsWith('.js')) {
continue;
}

const filePath = path.join(DOCS_DIST, decodeURIComponent(url.pathname));
if (!fs.existsSync(filePath)) {
continue;
}

const converter = v8toIstanbul(filePath, 0, { source: entry.source });
await converter.load();
converter.applyCoverage(entry.functions);
Object.assign(coverageMap, converter.toIstanbul());
}

if (Object.keys(coverageMap).length === 0) {
return;
}

fs.mkdirSync(RAW_DIR, { recursive: true });
fs.writeFileSync(getOutputPath(testInfo), JSON.stringify(coverageMap));
}

export const test = base.extend<{ page: Page }>({
page: async ({ page }, use, testInfo) => {
const collect = shouldCollect(testInfo);

if (collect) {
await page.coverage.startJSCoverage({
resetOnNavigation: false,
reportAnonymousScripts: true,
});
}

await use(page);

if (collect) {
await convertCoverage(page, testInfo);
}
},
});

export { expect };
2 changes: 1 addition & 1 deletion e2e/homepage.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { test, expect } from '@playwright/test';
import { test, expect } from './coverage';

test('homepage renders stats and navigation', async ({ page }) => {
await page.goto('/');
Expand Down
2 changes: 1 addition & 1 deletion e2e/markdown-pages.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { test, expect } from '@playwright/test';
import { test, expect } from './coverage';

test('markdown pages render content', async ({ page }) => {
await page.goto('/docs/intro.md');
Expand Down
2 changes: 1 addition & 1 deletion e2e/performance.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { test, expect } from '@playwright/test';
import { test, expect } from './coverage';

test('homepage loads within acceptable time', async ({ page }) => {
const start = Date.now();
Expand Down
2 changes: 1 addition & 1 deletion e2e/responsive.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { test, expect } from '@playwright/test';
import { test, expect } from './coverage';

test('mobile navigation shows menu button', async ({ page }) => {
await page.setViewportSize({ width: 375, height: 800 });
Expand Down
4 changes: 2 additions & 2 deletions e2e/search.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { test, expect } from '@playwright/test';
import { test, expect } from './coverage';

test('search navigates to result', async ({ page }) => {
await page.goto('/');
Expand All @@ -14,5 +14,5 @@ test('search navigates to result', async ({ page }) => {
await expect(dialog.getByText('DocKind').first()).toBeVisible();

await dialog.getByText('DocKind').first().click();
await expect(page).toHaveURL(/\/type\/DocKind/);
await expect(page).toHaveURL(/\/type\/[0-9a-f]{8}\/dockind$/);
});
2 changes: 1 addition & 1 deletion e2e/sidebar.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { test, expect } from '@playwright/test';
import { test, expect } from './coverage';

test('sidebar shows overview and groups', async ({ page }) => {
await page.goto('/');
Expand Down
2 changes: 1 addition & 1 deletion e2e/theme.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { test, expect } from '@playwright/test';
import { test, expect } from './coverage';

test('theme toggle updates document class', async ({ page }) => {
await page.goto('/');
Expand Down
20 changes: 18 additions & 2 deletions e2e/type-documentation.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,23 @@
import { test, expect } from '@playwright/test';
import { test, expect } from './coverage';

test('type documentation page renders signature', async ({ page }) => {
await page.goto('/type/DocKind');
const docsResponse = await page.request.get('/docs.json');
const docsJson = (await docsResponse.json()) as {
entries: Array<{ id: string; name: string; kind: string }>;
};

const entry = docsJson.entries.find((doc) => doc.kind === 'type' && doc.name === 'DocKind');
if (!entry) {
throw new Error('Expected DocKind type entry to exist in docs.json');
}

const slug = entry.name
.toLowerCase()
.trim()
.replace(/[^a-z0-9]+/g, '-')
.replace(/(^-|-$)+/g, '');

await page.goto(`/${entry.kind}/${entry.id}/${slug}`);

await expect(page.getByRole('heading', { name: 'DocKind' })).toBeVisible();
await expect(page.getByText('Signature')).toBeVisible();
Expand Down
Loading