import { describe, it } from 'node:test'; import { strict as assert } from 'node:assert'; import { readFileSync, existsSync } from 'node:fs'; import { resolve } from 'node:path'; import { parse } from 'node:url'; const deployPath = resolve('.github/workflows/deploy.yml'); const azurePath = resolve('.github/workflows/azure-static-web-apps-salmon-sea-0f0c8771e.yml'); function readDeploy() { return readFileSync(deployPath, 'utf-8'); } function readAzure() { return readFileSync(azurePath, 'utf-8'); } // Snapshot the Azure file content before any tests run const azureOriginal = readAzure(); describe('GitHub Pages deploy workflow', () => { it('should exist at .github/workflows/deploy.yml', () => { assert.ok(existsSync(deployPath), 'deploy.yml should exist'); }); it('should be valid YAML (parseable without error)', async () => { // Use a simple structural check - YAML should have proper structure const content = readDeploy(); assert.ok(content.length > 0, 'file should not be empty'); // Check it starts with a name field (standard workflow structure) assert.match(content, /^name:/, 'should start with name field'); }); describe('Triggers', () => { it('should trigger on push to master branch', () => { const content = readDeploy(); assert.match(content, /on:/, 'should have on: trigger'); assert.match(content, /push:/, 'should have push trigger'); assert.match(content, /branches:[\s\S]*?-\s*master/, 'should trigger on master branch'); }); it('should trigger on workflow_dispatch', () => { const content = readDeploy(); assert.match(content, /workflow_dispatch/, 'should have workflow_dispatch trigger'); }); }); describe('Permissions', () => { it('should set contents: read permission', () => { const content = readDeploy(); assert.match(content, /contents:\s*read/, 'should have contents read permission'); }); it('should set pages: write permission', () => { const content = readDeploy(); assert.match(content, /pages:\s*write/, 'should have pages write permission'); }); it('should set id-token: write permission', () => { const content = readDeploy(); assert.match(content, /id-token:\s*write/, 'should have id-token write permission'); }); }); describe('Concurrency', () => { it('should have concurrency group pages', () => { const content = readDeploy(); assert.match(content, /concurrency:/, 'should have concurrency block'); assert.match(content, /group:\s*['"]?pages['"]?/, 'should have pages concurrency group'); }); it('should cancel in-progress deployments', () => { const content = readDeploy(); assert.match(content, /cancel-in-progress:\s*true/, 'should cancel in-progress'); }); }); describe('Build job', () => { it('should have a build job on ubuntu-latest', () => { const content = readDeploy(); assert.match(content, /build:/, 'should have build job'); assert.match(content, /runs-on:\s*ubuntu-latest/, 'should run on ubuntu-latest'); }); it('should checkout with actions/checkout@v4', () => { const content = readDeploy(); assert.match(content, /actions\/checkout@v4/, 'should use checkout@v4'); }); it('should setup Node 22 with npm cache', () => { const content = readDeploy(); assert.match(content, /actions\/setup-node@v4/, 'should use setup-node@v4'); assert.match(content, /node-version:\s*['"]?22['"]?/, 'should use Node 22'); assert.match(content, /cache:\s*['"]?npm['"]?/, 'should cache npm'); }); it('should run npm ci', () => { const content = readDeploy(); assert.match(content, /npm ci/, 'should run npm ci'); }); it('should run npm run build', () => { const content = readDeploy(); assert.match(content, /npm run build/, 'should run npm run build'); }); it('should upload pages artifact from dist/', () => { const content = readDeploy(); assert.match(content, /actions\/upload-pages-artifact@v3/, 'should use upload-pages-artifact@v3'); assert.match(content, /path:\s*['"]?dist\/?['"]?/, 'should upload from dist/'); }); }); describe('Deploy job', () => { it('should have a deploy job', () => { const content = readDeploy(); assert.match(content, /deploy:/, 'should have deploy job'); }); it('should depend on build job', () => { const content = readDeploy(); assert.match(content, /needs:\s*build/, 'should need build job'); }); it('should run on ubuntu-latest', () => { const content = readDeploy(); // The deploy job section should have its own runs-on // Extract deploy job section const deploySection = content.split(/\n\s+deploy:/)[1]; assert.ok(deploySection, 'deploy section should exist'); assert.match(deploySection, /runs-on:\s*ubuntu-latest/, 'deploy should run on ubuntu-latest'); }); it('should have github-pages environment', () => { const content = readDeploy(); assert.match(content, /environment:/, 'should have environment'); assert.match(content, /name:\s*['"]?github-pages['"]?/, 'should use github-pages environment'); }); it('should set URL from deployment output', () => { const content = readDeploy(); assert.match(content, /url:\s*\$\{\{/, 'should set URL from deployment output'); }); it('should use deploy-pages@v4', () => { const content = readDeploy(); assert.match(content, /actions\/deploy-pages@v4/, 'should use deploy-pages@v4'); }); }); describe('Existing Azure workflow', () => { it('should still exist', () => { assert.ok(existsSync(azurePath), 'Azure workflow should still exist'); }); it('should be unchanged', () => { const current = readAzure(); assert.equal(current, azureOriginal, 'Azure workflow content should not be modified'); }); }); });