Skip to content

Commit

Permalink
Merge pull request #370 from creativedotdesign/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
danimalweb authored Nov 27, 2023
2 parents 5d7e7ef + 9061e3e commit 46b5d2c
Show file tree
Hide file tree
Showing 11 changed files with 446 additions and 386 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,11 @@ npm run dev

Note that the Vite Dev Server runs on port 3000. You access the website via the hostname and Vite will HMR or refresh automatically. If the Vite Dev Server is not running the website will pull it's assets from the /dist directory.

Important: You MUST set `WP_ENVIRONMENT_TYPE` to `development` or `local` in your wp-config.php file for the Vite Dev Server to work. Local by Flywheel does this automatically.

## Features

- [TailwindCSS](http://tailwindcss.com/)(v3.3)
- [TailwindCSS](http://tailwindcss.com/) (v3.3)
- Multilingual ready (WPML)
- Responsive
- General Options via ACF
Expand Down
2 changes: 2 additions & 0 deletions cypress.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ const { defineConfig } = require('cypress');

module.exports = defineConfig({
video: false,
enableScreenshots: true,
axeIgnoreContrast: true,
e2e: {
// We've imported your old cypress plugins here.
// You may want to clean this up later by importing these.
Expand Down
94 changes: 94 additions & 0 deletions cypress/e2e/axe.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import 'cypress-axe';
import resolveConfig from 'tailwindcss/resolveConfig';
import tailwindConfig from '../../tailwind.config.ts';

const fullConfig = resolveConfig(tailwindConfig);
const screens = fullConfig.theme.screens;

let allViolations = []; // Holds violations from all pages

before(() => {
allViolations = []; // Reset before the test suite runs
});

import { screenshotViolations, cypressLog, terminalLog } from '../support/helpers';

export const createAccessibilityCallback = (pageName, breakpointName) => {
cy.task('log', `Running accessibility checks for ${pageName} at ${breakpointName} breakpoint`);

return (violations) => {
cypressLog(violations);
terminalLog(violations);

if (Cypress.config('enableScreenshots')) {
screenshotViolations(violations, pageName, breakpointName);
}

allViolations.push(...violations);
};
};

const viewportSizes = [
{
name: 'Mobile',
width: 320,
height: 812,
},
{
name: 'Tablet',
width: parseInt(screens.md, 10),
height: 1024,
},
{
name: 'Desktop',
width: parseInt(screens.lg, 10),
height: 660,
},
];

describe('Accessibility Tests', () => {
it('should be accessible', () => {
cy.task('sitemapLocations').then((pages) => {
pages.forEach((page) => {
cy.visit(page);
cy.injectAxe();

if (Cypress.config('axeIgnoreContrast')) {
cy.configureAxe({
rules: [
{
id: 'color-contrast',
enabled: false,
},
],
});
}

const url = new URL(page);
const path = url.pathname;

viewportSizes.forEach((viewport) => {
cy.viewport(viewport.width, viewport.height);

cy.checkA11y(
null,
null,
// {
// runOnly: {
// type: 'tag',
// values: ['wcag2a', 'wcag2aa', 'best-practice', 'section508'],
// },
// },
createAccessibilityCallback(path, viewport.name),
true // Do not fail the test when there are accessibility failures
);
});
});
});
});
});

after(() => {
// Send the accumulated violations to the custom task
cy.task('processAccessibilityViolations', allViolations);
});
51 changes: 51 additions & 0 deletions cypress/plugins/index.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,62 @@
const { startDevServer } = require('@cypress/vite-dev-server');
require('dotenv').config();
const axios = require('axios');
const { createHtmlReport } = require('axe-html-reporter');

module.exports = (on, config) => {
on('dev-server:start', (options) => {
return startDevServer({ options });
});

on('task', {
processAccessibilityViolations(violations) {
createHtmlReport({
results: { violations: violations },
options: {
outputDir: './cypress/axe-reports',
reportFileName: 'a11yReport.html',
},
});

return null;
},
});

on('task', {
async sitemapLocations() {
try {
const response = await axios.get(`${process.env.VITE_ASSET_URL}/page-sitemap.xml`, {
headers: {
'Content-Type': 'application/xml',
},
});

const xml = response.data;
const locs = [...xml.matchAll(`<loc>(.|\n)*?</loc>`)].map(([loc]) =>
loc.replace('<loc>', '').replace('</loc>', '')
);

return locs;
} catch (error) {
console.error('Error fetching sitemap:', error);
throw error; // Re-throw the error to ensure Cypress is aware of the failure
}
},
});

on('task', {
log(message) {
console.log(message);

return null;
},
table(message) {
console.table(message);

return null;
},
});

config.env.baseUrl = process.env.VITE_ASSET_URL;

return config;
Expand Down
142 changes: 142 additions & 0 deletions cypress/support/helpers/accessibility-helper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
export const screenshotViolations = (violations, pageName, breakpointName) => {
cy.task('log', `Screenshotting violations for ${pageName} at ${breakpointName} breakpoint`);

violations.forEach((violation, index) => {
violation.nodes.forEach((node, nodeIndex) => {
const elementSelector = node.target[0];

if (Cypress.$(elementSelector).length) {
cy.get(elementSelector).then(($el) => {
// Check selector is not :root
if ($el.is(':root')) {
return;
}

// Scroll to the element
cy.get($el).scrollIntoView();

$el.addClass('highlight-violation');

if (pageName === '/') {
pageName = 'home';
}

// Remove leading slash
if (pageName.charAt(0) === '/') {
pageName = pageName.substr(1);
}

// Remove trailing slash
if (pageName.charAt(pageName.length - 1) === '/') {
pageName = pageName.substr(0, pageName.length - 1);
}

// convert the pageName to a valid filename
pageName = pageName.replace(/\//g, '-');

// Ensure the element is visible
cy.get($el).then(() => {
const screenshotName = `${pageName}-${
violation.id
}-${breakpointName.toLowerCase()}-${index}-${nodeIndex}`;
cy.screenshot(screenshotName, {
capture: 'viewport',
onAfterScreenshot($el) {
$el.removeClass('highlight-violation'); // Remove highlight class
},
});
});
});
} else {
cy.log(`No element selector found for violation ${violation.id}. Skipping screenshot.`);
}
});
});
};

const extractHtmlDetails = (htmlString) => {
// Using regular expressions to find the tag name and text content
const tagNameRegex = /<(\w+)/;
const textContentRegex = />([^<]+)</;

// Extracting tag name
const tagNameMatch = htmlString.match(tagNameRegex);
const tagName = tagNameMatch ? tagNameMatch[1] : null;

// Extracting text content
const textContentMatch = htmlString.match(textContentRegex);
let textContent = textContentMatch ? textContentMatch[1].trim() : null;

// Replacing HTML entities with their character equivalents for text content
if (textContent) {
const htmlEntities = {
'&amp;': '&',
'&lt;': '<',
'&gt;': '>',
'&quot;': '"',
'&#39;': "'",
};
textContent = textContent.replace(/&amp;|&lt;|&gt;|&quot;|&#39;/g, function (match) {
return htmlEntities[match];
});
}

return { tagName, textContent };
};

/**
* Display the accessibility violation table in the terminal
* @param violations array of results returned by Axe
* @link https://github.com/component-driven/cypress-axe#in-your-spec-file
*/
export const terminalLog = (violations) => {
cy.task('log', 'Violations: ' + violations.length);

// pluck specific keys to keep the table readable
const violationData = violations.map(({ description, id, impact, nodes }) => ({
description,
id,
impact,
nodes: nodes.length,
domNodes: nodes.map(({ html }) => {
const { tagName, textContent } = extractHtmlDetails(html);
return `${tagName}: ${textContent}`;
}),
}));

cy.task('table', violationData);
};

const severityIndicators = {
minor: '⚪️',
moderate: '🟡',
serious: '🟠',
critical: '🔴',
};

export const cypressLog = (violations) => {
violations.forEach((violation) => {
const targets = violation.nodes.map(({ target }) => target);
const nodes = Cypress.$(targets.join(','));
const consoleProps = () => violation;
const { help, helpUrl, impact } = violation;

Cypress.log({
$el: nodes,
consoleProps,
message: `[${help}](${helpUrl})`,
name: `${severityIndicators[impact]} A11Y`,
});

targets.forEach((target) => {
const el = Cypress.$(target.join(','));

Cypress.log({
$el: el,
consoleProps,
message: target,
name: '🔧',
});
});
});
};
1 change: 1 addition & 0 deletions cypress/support/helpers/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './accessibility-helper';
25 changes: 23 additions & 2 deletions inc/lib/init.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
function php_version_check()
{
$php_version = phpversion();
if (version_compare($php_version, '7.4.0', '<')) {
wp_die('<div class="error notice"><p>' . __('PHP version >= 7.4.0 is required for this theme to work correctly.', 'tofino') . '</p></div>', 'An error occured.');
if (version_compare($php_version, '8.0.0', '<')) {
wp_die('<div class="error notice"><p>' . __('PHP version >= 8.0.0 is required for this theme to work correctly.', 'tofino') . '</p></div>', 'An error occured.');
}
}
add_action('after_setup_theme', __NAMESPACE__ . '\\php_version_check');
Expand Down Expand Up @@ -346,3 +346,24 @@ function truncate_excerpt_length($length)
return 55;
}
add_filter('excerpt_length', __NAMESPACE__ . '\\truncate_excerpt_length');


// Clear cache on options save
function clear_cache_options_save($post_id)
{
$screen = get_current_screen();

if (strpos($screen->id, 'general-options') == true && $post_id == 'general-options') {
// Clear object cache
wp_cache_flush();

// Check if class WpeCommon exists
if (class_exists('\WpeCommon')) {
error_log('WpeCommon exists');

WpeCommon::purge_memcached();
WpeCommon::purge_varnish_cache();
}
}
}
add_action('acf/save_post', __NAMESPACE__ . '\\clear_cache_options_save', 20);
13 changes: 3 additions & 10 deletions inc/lib/vite.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
class Vite
{
public static $serverUrl = 'http://localhost:3000';
// public static $serverUrl = 'https://tofino.loca.lt';

public static function getBrowserSyncUrl($port = 3001)
{
Expand All @@ -28,17 +27,11 @@ public static function isBrowserSyncRunning() {

public static function isDevServerRunning()
{
$file = @fopen(self::$serverUrl . "/@vite/client", "r");

if (!$file) {
error_clear_last();

if (in_array(wp_get_environment_type(), ['local', 'development']) && is_array(wp_remote_get(self::$serverUrl))) {
return true;
} else {
return false;
}

fclose($file);

return true;
}

public static function base_path()
Expand Down
Loading

0 comments on commit 46b5d2c

Please sign in to comment.