Skip to content
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
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,8 @@ jobs:
mv artifact-qwikreact/lib packages/qwik-react/lib

- run: pnpm install --frozen-lockfile
- name: Check Docs Links (issue #6333)
run: node packages/docs/test-urls.js

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CI should preferably run pnpm scripts only, so perhaps pnpm -C packages/docs run test.urls?

- name: Build Qwik Docs
run: pnpm run build.packages.docs && echo ok > docs-build-completed.txt

Expand Down
1 change: 1 addition & 0 deletions packages/docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
"private": true,
"scripts": {
"build": "qwik build",
"test.urls": "node test-urls.js",
"build.client": "vite build",
"build.preview": "NODE_OPTIONS=--max-old-space-size=8192 vite build --ssr src/entry.preview.tsx",
"build.repl-sw": "vite --config vite.config-repl-sw.mts build",
Expand Down
145 changes: 56 additions & 89 deletions packages/docs/test-urls.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,20 @@
// Test script to verify all edit URLs are working
// Verify that all "edit on GitHub" edit URLs generated by makeEditPageUrl
// (see packages/docs/src/components/on-this-page/on-this-page.tsx) point to
// real source files in the docs tree.
//
// Run locally: node packages/docs/test-urls.js
// In CI: invoked from .github/workflows/ci.yml (see "Check Docs Links" step).
//
// The script intentionally validates against the local filesystem rather than
// HEAD-requesting github.com so that the check is deterministic and never
// flakes due to network/rate-limit issues. It exits non-zero on any failure
// so CI fails loudly when a doc is renamed or moved without updating these
// route group arrays (or vice versa).
//
// Refs: https://github.com/QwikDev/qwik/issues/6333

const fs = require('fs');
const path = require('path');
const https = require('https');

// Import the arrays from on-this-page.tsx (these are simplified copies for testing)
const QWIK_GROUP = [
Expand Down Expand Up @@ -107,115 +120,69 @@ function makeEditPageUrl(url) {
return segments.join('/');
}

// Check if a URL exists
function checkUrl(url) {
return new Promise((resolve) => {
const options = {
method: 'HEAD',
host: 'github.com',
path: url.replace('https://github.com', ''),
timeout: 5000,
};

const req = https.request(options, (res) => {
resolve({
url,
status: res.statusCode,
ok: res.statusCode < 400,
});
});

req.on('error', (err) => {
resolve({
url,
status: 0,
ok: false,
error: err.message,
});
});

req.on('timeout', () => {
req.destroy();
resolve({
url,
status: 0,
ok: false,
error: 'Timeout',
});
});

req.end();
});
const ROUTES_DIR = path.join(__dirname, 'src', 'routes');

// Resolve an edit path (relative to packages/docs/src/routes) to an absolute
// local file path. The qwik docs use folder-routed index.mdx files; some
// segments contain characters that need no special handling because the FS
// supports them on every CI-supported platform (Linux/macOS/Windows runner
// images all allow `(`, `)` and `$` in filenames).
function resolveLocalMdx(editPath) {
return path.join(ROUTES_DIR, editPath, 'index.mdx');
}

// Generate paths for testing
async function testAllPaths() {
console.log('Testing URL paths for documentation pages...');
function main() {
console.log('Verifying docs edit URLs resolve to real .mdx files...');

// Generate test paths
const testPaths = [];

// Test QWIK_GROUP paths
for (const path of QWIK_GROUP) {
if (path === 'index') {
// Special case for index
testPaths.push('/docs');
} else {
testPaths.push(`/docs/${path}`);
}
for (const p of QWIK_GROUP) {
testPaths.push(p === 'index' ? '/docs' : `/docs/${p}`);
}

// Test QWIK_ADVANCED_GROUP paths
for (const path of QWIK_ADVANCED_GROUP) {
testPaths.push(`/docs/advanced/${path}`);
for (const p of QWIK_ADVANCED_GROUP) {
testPaths.push(`/docs/advanced/${p}`);
}

// Test QWIKCITY_GROUP paths
for (const path of QWIKCITY_GROUP) {
testPaths.push(`/docs/${path}`);
for (const p of QWIKCITY_GROUP) {
testPaths.push(`/docs/${p}`);
}

// Test QWIKCITY_ADVANCED_GROUP paths
for (const path of QWIKCITY_ADVANCED_GROUP) {
testPaths.push(`/docs/advanced/${path}`);
for (const p of QWIKCITY_ADVANCED_GROUP) {
testPaths.push(`/docs/advanced/${p}`);
}

// Test each path
let failCount = 0;
let successCount = 0;
let failedPaths = [];
const failedPaths = [];

console.log(`Testing ${testPaths.length} URLs...`);
console.log(`Checking ${testPaths.length} URLs...`);

for (const testPath of testPaths) {
const editPath = makeEditPageUrl(testPath);
const filePath = resolveLocalMdx(editPath);
const editUrl = `https://github.com/QwikDev/qwik/blob/main/packages/docs/src/routes/${editPath}/index.mdx`;

try {
const result = await checkUrl(editUrl);
if (result.ok) {
console.log(`✅ ${editUrl}`);
successCount++;
} else {
console.error(`❌ ${editUrl} (Status: ${result.status})`);
failCount++;
failedPaths.push(testPath);
}
} catch (error) {
console.error(`❌ Error checking ${editUrl}: ${error.message}`);
if (fs.existsSync(filePath)) {
console.log(`✅ ${testPath} -> ${editPath}/index.mdx`);
successCount++;
} else {
console.error(`❌ ${testPath} -> ${editPath}/index.mdx (missing) [${editUrl}]`);
failCount++;
failedPaths.push(testPath);
failedPaths.push({ testPath, editPath, filePath });
}
}

console.log(`\nTest complete: ${successCount} successful, ${failCount} failed`);
console.log(`\nResult: ${successCount} ok, ${failCount} broken`);

if (failCount > 0) {
console.log('\nFailed paths:');
failedPaths.forEach((path) => {
console.log(`- ${path}`);
});
console.error('\nBroken edit URLs:');
for (const f of failedPaths) {
console.error(` - ${f.testPath} (expected ${f.filePath})`);
}
console.error(
'\nFix: update QWIK_GROUP / QWIK_ADVANCED_GROUP / QWIKCITY_GROUP /' +
' QWIKCITY_ADVANCED_GROUP in packages/docs/src/components/on-this-page/on-this-page.tsx' +
' (and the mirrored arrays in packages/docs/test-urls.js) to match the docs tree.'
);
process.exit(1);
}
}

// Run the tests
testAllPaths();
main();
Loading