From d10ec69a436f6c12c92b43e0a7128a559783ecf6 Mon Sep 17 00:00:00 2001 From: Maximilian Franzke Date: Mon, 21 Jul 2025 22:53:23 +0200 Subject: [PATCH 1/9] feat: validate fixtures --- .gitignore | 2 + docs/FIXTURE_VALIDATION.md | 199 +++++++++ package-lock.json | 23 +- package.json | 4 +- scripts/validate-fixtures.js | 109 +++++ .../fixture-validation.playwright.config.js | 79 ++++ .../fixture-validation.test.js | 378 ++++++++++++++++++ 7 files changed, 790 insertions(+), 4 deletions(-) create mode 100644 docs/FIXTURE_VALIDATION.md create mode 100644 scripts/validate-fixtures.js create mode 100644 test/fixtures-validation/fixture-validation.playwright.config.js create mode 100644 test/fixtures-validation/fixture-validation.test.js diff --git a/.gitignore b/.gitignore index 617b69c..c575a09 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,5 @@ npm-debug.log dist _site packages/*/coverage +/test-results +/playwright-report diff --git a/docs/FIXTURE_VALIDATION.md b/docs/FIXTURE_VALIDATION.md new file mode 100644 index 0000000..f5a4bb6 --- /dev/null +++ b/docs/FIXTURE_VALIDATION.md @@ -0,0 +1,199 @@ +# CSS if() Polyfill Fixture Validation + +This document explains the fixture validation system for the CSS if() polyfill, which ensures that your CSS transformations work correctly in real browser environments. + +## Overview + +The fixture validation system uses **Playwright** to test CSS fixture pairs in a headless Chromium browser. This approach validates that: + +1. **Input CSS** (with `if()` functions) gets properly transformed by the polyfill +2. **Expected CSS** (the desired output) produces the same visual results +3. **Media queries** respond correctly to viewport changes +4. **@supports queries** work as expected + +## How It Works + +### 1. Fixture Structure + +Fixtures come in pairs located in `test/fixtures/`: + +- `*.input.css` - CSS containing `if()` functions +- `*.expected.css` - Expected transformation result + +Example: + +```text +test/fixtures/ +├── basic-media.input.css # if(media(max-width: 768px): 100%; else: 50%) +├── basic-media.expected.css # Standard media query equivalent +├── basic-style.input.css # if(style(--theme): var(--primary); else: blue) +└── basic-style.expected.css # Resolved conditional styles +``` + +### 2. Browser Testing Process + +For each fixture pair, the system: + +1. **Creates an HTML page** with both input and expected CSS +2. **Loads the polyfill** and applies it to the input CSS +3. **Captures computed styles** from the polyfilled version +4. **Switches to expected CSS** and captures those styles +5. **Compares the results** to ensure they match + +### 3. Validation Types + +#### Basic Style Validation + +Compares computed styles for properties like: + +- `color` +- `width` +- `display` +- `backgroundColor` +- `fontSize` +- `margin`, `padding`, `border` + +#### Media Query Testing + +Tests responsive behavior at different viewports: + +- Desktop (1200x800) +- Tablet (768x600) +- Mobile (375x667) + +#### @supports Testing + +Validates CSS feature detection with properties like: + +- `display: grid` +- `display: flex` +- `color: color(display-p3 1 0 0)` + +## Running Tests + +### Command Line + +```bash +# Run all fixture tests +npm run test:fixtures + +# Run specific fixture with config +npx playwright test --config=test/fixtures-validation/fixture-validation.playwright.config.js --grep "basic-media" + +# Run with browser UI (for debugging) +npx playwright test --config=test/fixtures-validation/fixture-validation.playwright.config.js --ui +``` + +### Using the Helper Script + +```bash +# Run all fixtures +node scripts/validate-fixtures.js + +# Run specific fixture +node scripts/validate-fixtures.js basic-media + +# List available fixtures +node scripts/validate-fixtures.js --list + +# Show help +node scripts/validate-fixtures.js --help +``` + +## Adding New Fixtures + +1. **Create input CSS** with `if()` functions: + + ```css + /* my-feature.input.css */ + .element { + color: if( + supports(color: lab(50% 20 -30)): lab(50% 20 -30) ; else: #blue + ); + } + ``` + +2. **Create expected CSS** with the desired output: + + ```css + /* my-feature.expected.css */ + .element { + color: #blue; + } + @supports (color: lab(50% 20 -30)) { + .element { + color: lab(50% 20 -30); + } + } + ``` + +3. **Test automatically** - the validation will pick up new fixtures + +## Understanding Test Results + +### ✅ Passing Tests + +All computed styles match between polyfill and expected CSS. + +### ❌ Failing Tests + +Common failure reasons: + +1. **Style Mismatch**: Polyfill produces different computed values + + ```text + Property 'width' should match between polyfill and expected CSS + Expected: "400px" + Received: "50%" + ``` + +2. **Media Query Issues**: Responsive behavior doesn't match + + ```text + Property 'width' should match at mobile viewport (375x667) + ``` + +3. **Polyfill Errors**: JavaScript errors in the polyfill code + ```text + Error: if() function parsing failed + ``` + +## Browser Support + +The tests run on: + +- **Chromium** (primary - matches most real-world usage) +- **Firefox** (cross-browser validation) +- **WebKit** (Safari compatibility) + +## Benefits + +This validation system provides: + +1. **Real Browser Testing** - No mocking, actual CSS computation +2. **Visual Accuracy** - Ensures polyfill produces identical rendering +3. **Regression Detection** - Catches breaking changes automatically +4. **Cross-Browser Validation** - Tests on multiple engines +5. **Responsive Testing** - Validates media query behavior +6. **Feature Detection** - Ensures @supports works correctly + +## Troubleshooting + +### Tests Won't Start + +- Ensure Playwright is installed: `npx playwright install` +- Check that fixtures exist in `test/fixtures/` + +### Style Mismatches + +- Check if polyfill is correctly transforming CSS +- Verify expected CSS is accurate +- Test manually in browser to confirm behavior + +### Performance Issues + +- Tests run in parallel by default +- Use `--workers=1` to run sequentially if needed +- Consider reducing viewport testing for faster runs + +This fixture validation system gives you confidence that your CSS if() polyfill works correctly across different browsers and scenarios, providing the same visual results as native CSS would. diff --git a/package-lock.json b/package-lock.json index 7c517c3..fa8505b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "@changesets/cli": "^2.29.5", "@commitlint/cli": "^19.8.1", "@commitlint/config-conventional": "^19.8.1", + "@playwright/test": "^1.54.1", "@vitest/coverage-v8": "^3.2.4", "http-server": "^14.1.1", "husky": "^9.1.7", @@ -4082,6 +4083,22 @@ "url": "https://opencollective.com/pkgr" } }, + "node_modules/@playwright/test": { + "version": "1.54.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.54.1.tgz", + "integrity": "sha512-FS8hQ12acieG2dYSksmLOF7BNxnVf2afRJdCuM1eMSxj6QTSE6G4InGF7oApGgDb65MX7AwMVlIkpru0yZA4Xw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.54.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@publint/pack": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/@publint/pack/-/pack-0.1.2.tgz", @@ -17873,7 +17890,7 @@ } }, "packages/css-if-polyfill": { - "version": "0.1.1", + "version": "0.1.3", "license": "MIT", "bin": { "css-if-polyfill": "bin/cli.js" @@ -17890,10 +17907,10 @@ } }, "packages/postcss-if-function": { - "version": "0.1.0", + "version": "0.1.3", "license": "MIT", "dependencies": { - "css-if-polyfill": "0.1.1" + "css-if-polyfill": "0.1.3" }, "devDependencies": { "microbundle": "^0.15.1", diff --git a/package.json b/package.json index 2429caf..1a2053c 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,8 @@ "prepare": "husky", "preserve": "npm run build --workspace=css-if-polyfill", "serve": "http-server -p 3000 -o examples/basic-examples.html", - "test": "npm test --workspaces" + "test": "npm test --workspaces", + "test:fixtures": "playwright test test/fixtures-validation/fixture-validation.test.js --config=test/fixtures-validation/fixture-validation.playwright.config.js" }, "devDependencies": { "@babel/core": "^7.28.0", @@ -49,6 +50,7 @@ "@changesets/cli": "^2.29.5", "@commitlint/cli": "^19.8.1", "@commitlint/config-conventional": "^19.8.1", + "@playwright/test": "^1.54.1", "@vitest/coverage-v8": "^3.2.4", "http-server": "^14.1.1", "husky": "^9.1.7", diff --git a/scripts/validate-fixtures.js b/scripts/validate-fixtures.js new file mode 100644 index 0000000..8d217bf --- /dev/null +++ b/scripts/validate-fixtures.js @@ -0,0 +1,109 @@ +#!/usr/bin/env node + +/** + * Fixture Test Runner + * + * This script helps validate CSS if() polyfill fixtures by running them through + * a real browser environment using Playwright. + * + * Usage: + * node scripts/validate-fixtures.js [fixture-name] + * + * Examples: + * node scripts/validate-fixtures.js # Run all fixtures + * node scripts/validate-fixtures.js basic-media # Run specific fixture + */ + +import { execSync } from 'node:child_process'; +import { readdirSync } from 'node:fs'; +import path from 'node:path'; +import process from 'node:process'; +import { fileURLToPath } from 'node:url'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const fixturesDir = path.join(__dirname, '..', 'test', 'fixtures'); + +function getAvailableFixtures() { + const files = readdirSync(fixturesDir); + const inputFiles = files.filter((file) => file.endsWith('.input.css')); + return inputFiles.map((file) => file.replace('.input.css', '')); +} + +function runFixtureTests(fixtureName = null) { + console.log('🎭 CSS if() Polyfill Fixture Validation'); + console.log('==========================================\n'); + + const availableFixtures = getAvailableFixtures(); + + if (fixtureName) { + if (!availableFixtures.includes(fixtureName)) { + console.error(`❌ Fixture "${fixtureName}" not found.`); + console.log('\nAvailable fixtures:'); + for (const name of availableFixtures) console.log(` - ${name}`); + process.exit(1); + } + + console.log(`🧪 Running fixture: ${fixtureName}\n`); + } else { + console.log(`🧪 Running all ${availableFixtures.length} fixtures:\n`); + for (const name of availableFixtures) console.log(` ✓ ${name}`); + console.log(''); + } + + try { + // Run Playwright tests + const grepPattern = fixtureName + ? `--grep "validates ${fixtureName} fixture"` + : ''; + const command = `npx playwright test test/fixtures-validation/fixture-validation.test.js --config=test/fixtures-validation/fixture-validation.playwright.config.js ${grepPattern}`; + + console.log('🚀 Starting browser-based validation...\n'); + + execSync(command, { + stdio: 'inherit', + cwd: path.join(__dirname, '..') + }); + + console.log('\n✅ All fixture validations passed!'); + console.log('\nThis means:'); + console.log(' • Your polyfill correctly transforms input CSS'); + console.log(' • Browser rendering matches expected output'); + console.log(' • Media queries work responsively'); + console.log(' • @supports queries function properly'); + } catch { + console.error('\n❌ Fixture validation failed!'); + console.error('Please check the test output above for details.'); + process.exit(1); + } +} + +// Parse command line arguments +const fixtureName = process.argv[2]; + +// Show help if requested +if (fixtureName === '--help' || fixtureName === '-h') { + console.log('CSS if() Polyfill Fixture Validator\n'); + console.log('Usage:'); + console.log(' node scripts/validate-fixtures.js [fixture-name]\n'); + console.log('Examples:'); + console.log( + ' node scripts/validate-fixtures.js # Run all fixtures' + ); + console.log( + ' node scripts/validate-fixtures.js basic-media # Run specific fixture' + ); + console.log( + ' node scripts/validate-fixtures.js --list # List available fixtures\n' + ); + process.exit(0); +} + +// List fixtures if requested +if (fixtureName === '--list' || fixtureName === '-l') { + console.log('Available fixtures:'); + for (const name of getAvailableFixtures()) console.log(` ${name}`); + process.exit(0); +} + +// Run the tests +runFixtureTests(fixtureName); diff --git a/test/fixtures-validation/fixture-validation.playwright.config.js b/test/fixtures-validation/fixture-validation.playwright.config.js new file mode 100644 index 0000000..269ed2d --- /dev/null +++ b/test/fixtures-validation/fixture-validation.playwright.config.js @@ -0,0 +1,79 @@ +import { defineConfig, devices } from '@playwright/test'; +import process from 'node:process'; + +/** + * Playwright Configuration for CSS if() Polyfill Fixture Validation + * + * This configuration is specifically for validating CSS fixture pairs + * by testing them in real browser environments. It's separate from any + * other Playwright tests that might exist in the project. + * + * @see https://playwright.dev/docs/test-configuration + */ +export default defineConfig({ + testDir: '../..', + testMatch: '**/test/fixtures-validation/fixture-validation.test.js', + /* Run tests in files in parallel */ + fullyParallel: true, + /* Fail the build on CI if you accidentally left test.only in the source code. */ + forbidOnly: Boolean(process.env.CI), + /* Retry on CI only */ + retries: process.env.CI ? 2 : 0, + /* Opt out of parallel tests on CI. */ + workers: process.env.CI ? 1 : undefined, + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ + reporter: 'html', + /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + use: { + /* Base URL to use in actions like `await page.goto('/')`. */ + // baseURL: 'http://127.0.0.1:3000', + + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: 'on-first-retry' + }, + + /* Configure projects for major browsers */ + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] } + }, + + { + name: 'firefox', + use: { ...devices['Desktop Firefox'] } + }, + + { + name: 'webkit', + use: { ...devices['Desktop Safari'] } + } + + /* Test against mobile viewports. */ + // { + // name: 'Mobile Chrome', + // use: { ...devices['Pixel 5'] }, + // }, + // { + // name: 'Mobile Safari', + // use: { ...devices['iPhone 12'] }, + // }, + + /* Test against branded browsers. */ + // { + // name: 'Microsoft Edge', + // use: { ...devices['Desktop Edge'], channel: 'msedge' }, + // }, + // { + // name: 'Google Chrome', + // use: { ...devices['Desktop Chrome'], channel: 'chrome' }, + // }, + ] + + /* Run your local dev server before starting the tests */ + // webServer: { + // command: 'npm run start', + // url: 'http://127.0.0.1:3000', + // reuseExistingServer: !process.env.CI, + // }, +}); diff --git a/test/fixtures-validation/fixture-validation.test.js b/test/fixtures-validation/fixture-validation.test.js new file mode 100644 index 0000000..09e5f7d --- /dev/null +++ b/test/fixtures-validation/fixture-validation.test.js @@ -0,0 +1,378 @@ +/** + * Fixture Validation Tests + * + * This test suite validates CSS fixture pairs by: + * 1. Loading the input CSS with the polyfill + * 2. Loading the expected CSS directly + * 3. Comparing the computed styles in a real browser environment + */ + +import { expect, test } from '@playwright/test'; +import { readdirSync, readFileSync } from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const fixturesDir = path.join(__dirname, '..', 'fixtures'); +const polyfillUmdPath = path.join( + __dirname, + '..', + '..', + 'packages', + 'css-if-polyfill', + 'dist', + 'index.umd.js' +); + +// Read polyfill UMD build once at module load +const polyfillUmdJs = readFileSync(polyfillUmdPath, 'utf8'); + +// Get all fixture pairs +const _getFixturePairs = () => { + const files = readdirSync(fixturesDir); + const inputFiles = files.filter((file) => file.endsWith('.input.css')); + + return inputFiles.map((inputFile) => { + const baseName = inputFile.replace('.input.css', ''); + const expectedFile = `${baseName}.expected.css`; + + if (!files.includes(expectedFile)) { + throw new Error(`Missing expected file for ${inputFile}`); + } + + return { + name: baseName, + inputFile: path.join(fixturesDir, inputFile), + expectedFile: path.join(fixturesDir, expectedFile), + inputCSS: readFileSync(path.join(fixturesDir, inputFile), 'utf8'), + expectedCSS: readFileSync( + path.join(fixturesDir, expectedFile), + 'utf8' + ) + }; + }); +}; + +/** + * Test media query responsive behavior + */ +async function testMediaQueryBehavior(page) { + // Test at different viewport sizes + const viewports = [ + { width: 1200, height: 800, name: 'desktop' }, + { width: 768, height: 600, name: 'tablet' }, + { width: 375, height: 667, name: 'mobile' } + ]; + + /* eslint-disable no-await-in-loop */ + for (const viewport of viewports) { + await page.setViewportSize(viewport); + await page.waitForTimeout(100); // Allow media queries to apply + + // Re-enable polyfill styles for this test + await page.evaluate(() => { + globalThis.document.querySelector('#polyfill-styles').disabled = + false; + globalThis.document.querySelector('#expected-styles').disabled = + true; + }); + + await page.waitForTimeout(50); + + const polyfillStyles = await page.evaluate(() => { + const testElement = + globalThis.document.querySelector('.test, .responsive'); + if (!testElement) return null; + + const computed = globalThis.getComputedStyle(testElement); + return { + width: computed.width, + color: computed.color, + display: computed.display + }; + }); + + // Switch to expected styles + await page.evaluate(() => { + globalThis.document.querySelector('#polyfill-styles').disabled = + true; + globalThis.document.querySelector('#expected-styles').disabled = + false; + }); + + await page.waitForTimeout(50); + + const expectedStyles = await page.evaluate(() => { + const testElement = + globalThis.document.querySelector('.test, .responsive'); + if (!testElement) return null; + + const computed = globalThis.getComputedStyle(testElement); + return { + width: computed.width, + color: computed.color, + display: computed.display + }; + }); + + // Compare styles at this viewport + if (polyfillStyles && expectedStyles) { + for (const [property, expectedValue] of Object.entries( + expectedStyles + )) { + expect( + polyfillStyles[property], + `Property '${property}' should match at ${viewport.name} viewport (${viewport.width}x${viewport.height})` + ).toBe(expectedValue); + } + } + } + /* eslint-enable no-await-in-loop */ +} + +/** + * Test @supports query behavior + */ +async function testSupportsQueryBehavior(page) { + // Test different CSS support scenarios + const supportTests = [ + { property: 'display', value: 'grid' }, + { property: 'display', value: 'flex' }, + { property: 'color', value: 'color(display-p3 1 0 0)' } + ]; + + /* eslint-disable no-await-in-loop */ + for (const supportTest of supportTests) { + const supportsResult = await page.evaluate( + ({ property, value }) => globalThis.CSS.supports(property, value), + supportTest + ); + + // Re-test styles knowing the actual support status + await page.evaluate(() => { + globalThis.document.querySelector('#polyfill-styles').disabled = + false; + globalThis.document.querySelector('#expected-styles').disabled = + true; + }); + + await page.waitForTimeout(50); + + const polyfillStyles = await page.evaluate(() => { + const testElement = globalThis.document.querySelector('.test'); + if (!testElement) return null; + + const computed = globalThis.getComputedStyle(testElement); + return { + color: computed.color, + display: computed.display, + width: computed.width + }; + }); + + // This test mainly ensures the polyfill doesn't break @supports functionality + expect(polyfillStyles).toBeTruthy(); + expect(supportsResult).toBeDefined(); + } + /* eslint-enable no-await-in-loop */ +} + +// Test configuration for different scenarios +test.describe('CSS if() Polyfill Fixture Validation', () => { + test.beforeEach(async ({ page }) => { + // Set up server to serve static files + await page.route('/packages/**', async (route) => { + const url = route.request().url(); + const filePath = url.replace( + /^.*\/packages\//, + '/Users/maximilianfranzke/Sites/contributions/css-if-polyfill/packages/' + ); + + try { + const content = readFileSync(filePath, 'utf8'); + const contentType = filePath.endsWith('.js') + ? 'application/javascript' + : 'text/plain'; + + await route.fulfill({ + status: 200, + contentType, + body: content + }); + } catch { + await route.fulfill({ + status: 404, + body: `File not found: ${filePath}` + }); + } + }); + }); + + // Create individual test for each fixture + test('validates basic-style fixture', async ({ page }) => { + await testFixture(page, 'basic-style'); + }); + + test('validates basic-media fixture', async ({ page }) => { + await testFixture(page, 'basic-media'); + }); + + test('validates basic-supports fixture', async ({ page }) => { + await testFixture(page, 'basic-supports'); + }); + + test('validates complex-media-query fixture', async ({ page }) => { + await testFixture(page, 'complex-media-query'); + }); + + test('validates mixed-conditions fixture', async ({ page }) => { + await testFixture(page, 'mixed-conditions'); + }); + + test('validates multiple-concatenated-conditions fixture', async ({ + page + }) => { + await testFixture(page, 'multiple-concatenated-conditions'); + }); + + test('validates multiple-functions-one-rule fixture', async ({ page }) => { + await testFixture(page, 'multiple-functions-one-rule'); + }); + + test('validates multiple-separate-functions fixture', async ({ page }) => { + await testFixture(page, 'multiple-separate-functions'); + }); + + test('validates no-if-functions fixture', async ({ page }) => { + await testFixture(page, 'no-if-functions'); + }); + + test('validates with-comments fixture', async ({ page }) => { + await testFixture(page, 'with-comments'); + }); +}); + +async function testFixture(page, fixtureName) { + // Read fixture files + const inputCSS = readFileSync( + path.join(fixturesDir, `${fixtureName}.input.css`), + 'utf8' + ); + const expectedCSS = readFileSync( + path.join(fixturesDir, `${fixtureName}.expected.css`), + 'utf8' + ); + + // Create HTML page with polyfill and test content + const htmlContent = ` + + + + + CSS if() Polyfill Test - ${fixtureName} + + + + +
Test Element
+
+
Nested Test
+
+ + + + `; + + // Set up the page content + await page.setContent(htmlContent, { + waitUntil: 'domcontentloaded' + }); + + // Wait for polyfill to be ready + await page.waitForFunction(() => globalThis.polyfillReady === true, { + timeout: 5000 + }); + + // Give polyfill time to process + await page.waitForTimeout(100); + + // Get computed styles after polyfill processing + const polyfillStyles = await page.evaluate(() => { + const testElement = globalThis.document.querySelector('.test'); + if (!testElement) return null; + + const computed = globalThis.getComputedStyle(testElement); + + return { + color: computed.color, + width: computed.width, + display: computed.display, + backgroundColor: computed.backgroundColor, + fontSize: computed.fontSize, + margin: computed.margin, + padding: computed.padding, + border: computed.border + }; + }); + + // Now test with expected CSS + await page.evaluate(() => { + // Disable polyfill styles and enable expected styles + globalThis.document.querySelector('#polyfill-styles').disabled = true; + globalThis.document.querySelector('#expected-styles').disabled = false; + }); + + // Wait a bit for styles to apply + await page.waitForTimeout(50); + + // Get computed styles with expected CSS + const expectedStyles = await page.evaluate(() => { + const testElement = globalThis.document.querySelector('.test'); + if (!testElement) return null; + + const computed = globalThis.getComputedStyle(testElement); + + return { + color: computed.color, + width: computed.width, + display: computed.display, + backgroundColor: computed.backgroundColor, + fontSize: computed.fontSize, + margin: computed.margin, + padding: computed.padding, + border: computed.border + }; + }); + + // Compare the styles + expect(polyfillStyles).toBeTruthy(); + expect(expectedStyles).toBeTruthy(); + + // Compare each style property + for (const [property, expectedValue] of Object.entries(expectedStyles)) { + expect( + polyfillStyles[property], + `Property '${property}' should match between polyfill and expected CSS` + ).toBe(expectedValue); + } + + // Test responsive behavior for media query fixtures + if (fixtureName.includes('media')) { + await testMediaQueryBehavior(page); + } + + // Test supports() functionality + if (fixtureName.includes('supports')) { + await testSupportsQueryBehavior(page); + } +} From aed8eac2935401884a54cf1a0bbdfe15a1d498a0 Mon Sep 17 00:00:00 2001 From: Maximilian Franzke Date: Mon, 21 Jul 2025 22:59:02 +0200 Subject: [PATCH 2/9] refactor: enhanced tests --- package.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 1a2053c..7f239da 100644 --- a/package.json +++ b/package.json @@ -41,8 +41,9 @@ "prepare": "husky", "preserve": "npm run build --workspace=css-if-polyfill", "serve": "http-server -p 3000 -o examples/basic-examples.html", - "test": "npm test --workspaces", - "test:fixtures": "playwright test test/fixtures-validation/fixture-validation.test.js --config=test/fixtures-validation/fixture-validation.playwright.config.js" + "test": "npm-run-all --sequential test:*", + "test:fixtures": "playwright test test/fixtures-validation/fixture-validation.test.js --config=test/fixtures-validation/fixture-validation.playwright.config.js", + "test:packages": "npm test --workspaces" }, "devDependencies": { "@babel/core": "^7.28.0", From 85567f92d337a38ed3a5e2732ad28380b8ebc7d4 Mon Sep 17 00:00:00 2001 From: Maximilian Franzke Date: Tue, 22 Jul 2025 05:46:08 +0200 Subject: [PATCH 3/9] fix: we need a build previous to a test --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 7f239da..6760ee7 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "prelint:packages": "npm run build", "prepare": "husky", "preserve": "npm run build --workspace=css-if-polyfill", + "pretest:fixtures": "npm run build", "serve": "http-server -p 3000 -o examples/basic-examples.html", "test": "npm-run-all --sequential test:*", "test:fixtures": "playwright test test/fixtures-validation/fixture-validation.test.js --config=test/fixtures-validation/fixture-validation.playwright.config.js", From 7b3ce19865f5c8cccee58654cd63b5ba0a9fa22d Mon Sep 17 00:00:00 2001 From: Maximilian Franzke <787658+mfranzke@users.noreply.github.com> Date: Sat, 9 Aug 2025 13:31:37 +0200 Subject: [PATCH 4/9] Update test/fixtures-validation/fixture-validation.test.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- test/fixtures-validation/fixture-validation.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/fixtures-validation/fixture-validation.test.js b/test/fixtures-validation/fixture-validation.test.js index 09e5f7d..00f85dd 100644 --- a/test/fixtures-validation/fixture-validation.test.js +++ b/test/fixtures-validation/fixture-validation.test.js @@ -185,7 +185,7 @@ test.describe('CSS if() Polyfill Fixture Validation', () => { const url = route.request().url(); const filePath = url.replace( /^.*\/packages\//, - '/Users/maximilianfranzke/Sites/contributions/css-if-polyfill/packages/' + path.join(__dirname, '..', '..', 'packages') ); try { From 65b3fa9fae1be215bf93b547a02e2d996a18b7e4 Mon Sep 17 00:00:00 2001 From: Maximilian Franzke <787658+mfranzke@users.noreply.github.com> Date: Sat, 9 Aug 2025 13:47:37 +0200 Subject: [PATCH 5/9] refactor: added lint-staged script again --- .config/.lintstagedrc.json | 1 + 1 file changed, 1 insertion(+) diff --git a/.config/.lintstagedrc.json b/.config/.lintstagedrc.json index 530996c..d3bb2a2 100644 --- a/.config/.lintstagedrc.json +++ b/.config/.lintstagedrc.json @@ -1,4 +1,5 @@ { + "*.md": "markdownlint --config .config/.markdown-lint.yml --fix", "test/fixtures/*.css": "pnpm run build:docs", "*.{js,ts,tsx,jsx,mjs,cjs}": "pnpm run lint:xo --fix", "pnpm-lock.yaml": "pnpm audit --fix" From 3ce85be0346ba20895b0b6e70da16ce6da41523d Mon Sep 17 00:00:00 2001 From: Maximilian Franzke <787658+mfranzke@users.noreply.github.com> Date: Sat, 9 Aug 2025 13:55:31 +0200 Subject: [PATCH 6/9] refactor: corrected node package manager --- docs/FIXTURE_VALIDATION.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/FIXTURE_VALIDATION.md b/docs/FIXTURE_VALIDATION.md index f5a4bb6..660bc9d 100644 --- a/docs/FIXTURE_VALIDATION.md +++ b/docs/FIXTURE_VALIDATION.md @@ -75,13 +75,13 @@ Validates CSS feature detection with properties like: ```bash # Run all fixture tests -npm run test:fixtures +pnpm run test:fixtures # Run specific fixture with config -npx playwright test --config=test/fixtures-validation/fixture-validation.playwright.config.js --grep "basic-media" +pnpm exec playwright test --config=test/fixtures-validation/fixture-validation.playwright.config.js --grep "basic-media" # Run with browser UI (for debugging) -npx playwright test --config=test/fixtures-validation/fixture-validation.playwright.config.js --ui +pnpm exec playwright test --config=test/fixtures-validation/fixture-validation.playwright.config.js --ui ``` ### Using the Helper Script From 691bb8c4e414c91f9a2877c4f03477f6958b1acd Mon Sep 17 00:00:00 2001 From: Maximilian Franzke <787658+mfranzke@users.noreply.github.com> Date: Sat, 9 Aug 2025 13:55:59 +0200 Subject: [PATCH 7/9] refactor: simplifications --- .../fixture-validation.test.js | 50 +++---------------- 1 file changed, 8 insertions(+), 42 deletions(-) diff --git a/test/fixtures-validation/fixture-validation.test.js b/test/fixtures-validation/fixture-validation.test.js index 00f85dd..98b1a5c 100644 --- a/test/fixtures-validation/fixture-validation.test.js +++ b/test/fixtures-validation/fixture-validation.test.js @@ -28,7 +28,7 @@ const polyfillUmdPath = path.join( const polyfillUmdJs = readFileSync(polyfillUmdPath, 'utf8'); // Get all fixture pairs -const _getFixturePairs = () => { +const getFixturePairs = () => { const files = readdirSync(fixturesDir); const inputFiles = files.filter((file) => file.endsWith('.input.css')); @@ -208,48 +208,14 @@ test.describe('CSS if() Polyfill Fixture Validation', () => { }); }); - // Create individual test for each fixture - test('validates basic-style fixture', async ({ page }) => { - await testFixture(page, 'basic-style'); - }); - - test('validates basic-media fixture', async ({ page }) => { - await testFixture(page, 'basic-media'); - }); - - test('validates basic-supports fixture', async ({ page }) => { - await testFixture(page, 'basic-supports'); - }); - - test('validates complex-media-query fixture', async ({ page }) => { - await testFixture(page, 'complex-media-query'); - }); - - test('validates mixed-conditions fixture', async ({ page }) => { - await testFixture(page, 'mixed-conditions'); - }); - - test('validates multiple-concatenated-conditions fixture', async ({ - page - }) => { - await testFixture(page, 'multiple-concatenated-conditions'); - }); - - test('validates multiple-functions-one-rule fixture', async ({ page }) => { - await testFixture(page, 'multiple-functions-one-rule'); - }); - - test('validates multiple-separate-functions fixture', async ({ page }) => { - await testFixture(page, 'multiple-separate-functions'); - }); + // Dynamically generate tests for all fixture pairs + const fixturePairs = getFixturePairs(); - test('validates no-if-functions fixture', async ({ page }) => { - await testFixture(page, 'no-if-functions'); - }); - - test('validates with-comments fixture', async ({ page }) => { - await testFixture(page, 'with-comments'); - }); + for (const fixture of fixturePairs) { + test(`validates ${fixture.name} fixture`, async ({ page }) => { + await testFixture(page, fixture.name); + }); + } }); async function testFixture(page, fixtureName) { From 57077488768024d333570545eae9edd0eab6f450 Mon Sep 17 00:00:00 2001 From: Maximilian Franzke <787658+mfranzke@users.noreply.github.com> Date: Sat, 9 Aug 2025 14:01:17 +0200 Subject: [PATCH 8/9] refactor: let's run the fixtures test within the pipeline --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a704a57..08c04ff 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "preserve": "pnpm --filter=css-if-polyfill run build", "pretest:fixtures": "npm run build", "serve": "http-server -p 3000 -o examples/basic-examples.html", - "test": "pnpm --recursive run test", + "test": "pnpm --recursive run test && pnpm run test:fixtures", "test:fixtures": "playwright test test/fixtures-validation/fixture-validation.test.js --config=test/fixtures-validation/fixture-validation.playwright.config.js" }, "devDependencies": { From 91b294784aa8ead1a504d041d495dfdbefa3afb3 Mon Sep 17 00:00:00 2001 From: Maximilian Franzke <787658+mfranzke@users.noreply.github.com> Date: Sat, 9 Aug 2025 14:18:28 +0200 Subject: [PATCH 9/9] Apply suggestions from code review --- docs/FIXTURE_VALIDATION.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/FIXTURE_VALIDATION.md b/docs/FIXTURE_VALIDATION.md index 660bc9d..62873e1 100644 --- a/docs/FIXTURE_VALIDATION.md +++ b/docs/FIXTURE_VALIDATION.md @@ -196,4 +196,4 @@ This validation system provides: - Use `--workers=1` to run sequentially if needed - Consider reducing viewport testing for faster runs -This fixture validation system gives you confidence that your CSS if() polyfill works correctly across different browsers and scenarios, providing the same visual results as native CSS would. +This fixture validation system gives you confidence that your CSS `if()` polyfill works correctly across different browsers and scenarios, providing the same visual results as native CSS would.