This document explains the automated testing system for the website to prevent breaking changes.
⚠️ CRITICAL: All testing happens LOCALLY. GitHub Actions does NOT run tests - it only builds and deploys!
Looking for a step-by-step tutorial? See Testing & Deployment Tutorial for practical, hands-on guidance with examples.
The site uses a comprehensive testing suite that runs on your local machine before pushing. Your Lefthook git hooks automatically enforce these tests:
yamllint .
This is very fast and checks:
.yml and .yaml filesNote: Automatically ignores node_modules/, vendor/, _site/, and .bundle/ directories.
bundle exec rake test
This is fast and checks:
bundle exec rake test_external
This is slower but also checks:
npm run lighthouse
Note: Requires Chrome/Chromium to be installed. In WSL environments, this may not work locally. Install Chrome dependencies if you need to run these tests locally.
Tests:
npm run test:print
Validates print layouts and generates PDFs for manual review. Tests:
Output: PDFs saved to ./print-test-results/ directory for manual inspection.
Note: Requires Chrome/Chromium with proper dependencies. In WSL or headless environments where Chrome is not available, print tests will automatically skip locally. Install Chrome dependencies if you need to run these tests locally.
Pages tested:
npm run test:all
Runs html-proofer, Lighthouse CI, and print tests sequentially.
# Just build the site
bundle exec rake build
# Clean the build directory
bundle exec rake clean
# Default task (runs test)
bundle exec rake
⚠️ IMPORTANT: This project uses Lefthook git hooks to enforce ALL tests locally. GitHub Actions does NOT run tests.
Pre-commit hook (runs when you git commit):
Pre-push hook (runs when you git push):
./test-before-push.shgit commit - pre-commit hook validates YAMLgit push - pre-push hook runs ALL tests automaticallySkip hooks (use with extreme caution):
LEFTHOOK=0 git push
# or
git push --no-verify
⚠️ WARNING: Skipping hooks bypasses ALL testing. Only skip if you’ve already run ./test-before-push.sh manually and all tests passed.
.github/workflows/jekyll.yml - Build and deployment ONLY:
bundle exec jekyll buildThat’s it! No tests run in GitHub Actions. All quality gates are enforced locally via Lefthook.
Lighthouse Reports:
.lighthouseci/ directoryPrint Test PDFs:
./print-test-results/ directorysyntax error: expected <block end>, but found '<block mapping start>'
Solution: Check indentation - YAML requires consistent 2-space indentation. Ensure no tabs are used.
line too long (150 > 120 characters)
Solution:
line-length.max in .yamllint if neededwrong indentation: expected 4 but found 2
Solution: Ensure consistent 2-space indentation. Check parent element indentation level.
trailing spaces
Solution: Remove whitespace at end of lines. Most editors can do this automatically on save.
image has no src or srcset attribute
Solution: Ensure all <img> tags have a src attribute or use srcset for responsive images.
internally linking to /works/foo/, which does not exist
Solution:
_portfolio/permalink in front matterinternally linking to /assets/cv.pdf, which does not exist
Solution:
Performance: 72/100 (required: 85/100)
Common causes:
Solutions:
Categories:accessibility: 78/100 (required: 85/100)
Common causes:
Solutions:
Document does not have a meta description
Solution:
Background and foreground colors do not have sufficient contrast ratio
Solution:
QR codes found but none are valid/visible in print mode
Solution:
Print-only elements not visible
Solution:
display: block !important in @media printNo-print elements still visible
Solution:
display: none !important to .no-print in @media printEdit .yamllint to customize YAML linting behavior:
---
extends: default
ignore: |
node_modules/
vendor/
_site/
.bundle/
rules:
line-length:
max: 150 # Maximum line length
level: warning # Can be: warning, error, or disable
indentation:
spaces: 2 # Number of spaces for indentation
indent-sequences: true
truthy:
allowed-values: ['true', 'false', 'yes', 'no', 'on', 'off']
document-start: disable # Don't require --- at start
Key settings:
ignore: Directories/patterns to skipline-length.max: Maximum characters per lineindentation.spaces: Spaces per indent leveltruthy.allowed-values: Accepted boolean values (for Jekyll compatibility)document-start: Whether to require --- at file startEdit Rakefile to customize test behavior:
options = {
:disable_external => true, # Set to false to check external links
:ignore_missing_alt => true, # Set to false to enforce alt tags
:check_html => true, # Validate HTML structure
:enforce_https => false, # Set to true to require HTTPS
# Add URLs to ignore:
:ignore_urls => [
/localhost/,
/example\.com/
]
}
Edit lighthouserc.json to customize Lighthouse behavior:
{
"ci": {
"collect": {
"numberOfRuns": 3,
"url": [
"http://localhost/index.html",
"http://localhost/bio/index.html"
]
},
"assert": {
"assertions": {
"categories:performance": ["warn", {"minScore": 0.85}],
"categories:accessibility": ["error", {"minScore": 0.85}],
"categories:seo": ["warn", {"minScore": 0.85}]
}
}
}
}
Key settings:
numberOfRuns: How many times to test each page (median score used)url: Array of pages to testminScore: Minimum score (0.0 to 1.0) required to pass"error": Fails build if score below threshold"warn": Shows warning but doesn’t fail build"off": Disables specific checkEdit test-print.js to customize print testing:
const CONFIG = {
siteDir: './_site', // Built site directory
outputDir: './print-test-results', // PDF output directory
pages: [
{
path: 'index.html',
name: 'homepage',
checks: ['qr-code', 'print-layout']
},
// Add more pages to test
],
a4: {
width: 210, // mm
height: 297, // mm
margin: 15 // mm
}
};
Available checks:
qr-code: Validates QR codes are visible and validprint-layout: Checks .print-only and .no-print elementssocial-links: Validates social links (bio page)ticket-links: Checks ticket QR codes (events page)contact-info: Validates contact information displaymetadata: Checks metadata visibility (work pages)Adding new pages:
{
path: 'works/new-work/index.html',
name: 'new-work',
checks: ['qr-code', 'print-layout', 'metadata']
}
./test-before-push.sh (automatic)./test-before-push.sh manually./test-before-push.sh
Run this while developing to catch issues early, before committing. The pre-push hook will run the same tests again automatically when you push (but will pass quickly since you already fixed everything).
yamllint .
Fast check for YAML syntax errors in config files and front matter.
Fix issues immediately - Don’t accumulate broken links or test failures
bundle exec rake test_external
npm run test:print
Review generated PDFs in ./print-test-results/ to ensure print styles work correctly.
./test-before-push.sh --full
Includes performance, accessibility, and SEO validation.
./test-before-push.sh successfullyignore_urls in Rakefile if needed.To ignore specific files or URLs, edit Rakefile:
# Ignore specific URLs
:ignore_urls => [
%r{/works/template-example/} # Ignore template works
]
# Ignore specific files
:ignore_files => [
/templates\//
]
If you need even more comprehensive testing, consider:
Current testing suite (yamllint + html-proofer + Lighthouse CI + print tests) enforced locally covers:
All critical tests run automatically via Lefthook hooks before you can push!
This is comprehensive for most portfolio websites!