Skip to content

Commit

Permalink
Merge branch 'master' into mapol/persona-dual-presence
Browse files Browse the repository at this point in the history
* master: (46 commits)
  Enable conditional rendering of page sections depending on query present in the url to enable iframe rendering of examples on docs.microsoft OUFR portal (microsoft#9438)
  Applying package updates.
  Make more examples exportable to codepen (microsoft#9468)
  Fix create-package script (microsoft#9491)
  Card: Making elevation themable by using the effects from theme instead of directly referencing Depths (microsoft#9472)
  Revert "Tooltip: improve perf by preventing all render until after de… (microsoft#9495)
  a11y-tests: Run a11y tests on all component examples (microsoft#9479)
  Website (mobile): Add source code links, remove generic library link (microsoft#9487)
  Facepile - Adding OnRenderPersona and OnRenderPersonaCoin as optional… (microsoft#9480)
  Applying package updates.
  Accessibility improvement for DetailsList while placeholder data is being displayed (microsoft#9484)
  Button: Getting anchor native props if href prop is specified (microsoft#9456)
  Fabric website: add documentation entry points. (microsoft#9477)
  Website: Add overview and control request sections to mobile pages. (microsoft#9483)
  Fixing some styling bugs in theme designer: theme slots pivot was not centered & Main part of the page with the 3 stacks had a margin when it was unnecessary. Also cleaned up the code by creating a separate component for ThemeSlots that does the pivoting. (microsoft#9458)
  Only run KeytipManager update when relevant keytip props have changed (microsoft#9414)
  Theme colors page (microsoft#9369)
  Prevent Callout from being dismissed after mouse pressed inside but released outside (microsoft#9415)
  Add data viz separator for HorizontalBarChart (microsoft#9482)
  Applying package updates.
  ...
  • Loading branch information
Markionium committed Jun 19, 2019
2 parents 6e9081e + bac4e15 commit b252b15
Show file tree
Hide file tree
Showing 417 changed files with 112,406 additions and 7,888 deletions.
20 changes: 10 additions & 10 deletions apps/a11y-tests/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "a11y-tests",
"version": "1.0.0",
"version": "7.0.0",
"private": true,
"description": "A11y Tests for Office UI Fabric React",
"scripts": {
Expand All @@ -11,27 +11,27 @@
"update-snapshots": "npx just-scripts jest -u"
},
"dependencies": {
"@uifabric/fabric-website-resources": "^6.18.4",
"@uifabric/fabric-website-resources": "^7.0.2",
"axe-core": "^3.2.2",
"axe-puppeteer": "^1.0.0",
"axe-sarif-converter": "^1.0.0",
"glob": "^7.1.2",
"mkdirp": "^0.5.1",
"office-ui-fabric-react": "^7.0.0",
"office-ui-fabric-react": "^7.4.1",
"puppeteer": "^1.13.0",
"tslib": "^1.7.1",
"react": "^16.8.6",
"react-dom": "^16.8.6"
"react": "16.8.6",
"react-dom": "16.8.6"
},
"devDependencies": {
"@types/glob": "^7.1.1",
"@types/jest": "23.0.0",
"@types/node": "^8.10.29",
"@types/puppeteer": "1.12.3",
"@types/react": ">=16.8.0 <17.0.0",
"@types/react-dom": ">=16.8.0 <17.0.0",
"@uifabric/build": "*",
"@uifabric/icons": "^6.5.2",
"@uifabric/tslint-rules": "^1.0.2"
"@types/react": "16.8.11",
"@types/react-dom": "16.8.4",
"@uifabric/build": "^7.0.0",
"@uifabric/icons": "^7.0.2",
"@uifabric/tslint-rules": "^7.0.2"
}
}
6 changes: 5 additions & 1 deletion apps/a11y-tests/src/getSarifReport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { AxePuppeteer } from 'axe-puppeteer';
import * as puppeteer from 'puppeteer';
import { convertAxeToSarif, SarifLog } from 'axe-sarif-converter';
import { Stylesheet, InjectionMode, resetIds } from 'office-ui-fabric-react';
import * as path from 'path';
import * as os from 'os';

const disabledAxeRules = ['document-title', 'html-has-lang', 'landmark-one-main', 'page-has-heading-one', 'region', 'bypass'];

Expand All @@ -25,7 +27,9 @@ function renderTestHtml(element: React.ReactElement<any>): string {

/* tslint:disable-next-line:no-any */
export async function getSarifReport(element: React.ReactElement<any>): Promise<SarifLog> {
const browser = await puppeteer.launch();
const browser = await puppeteer.launch({
userDataDir: path.resolve(os.tmpdir(), 'oufr-a11y-test-profile')
});

const page = await browser.newPage();
const testHtml = renderTestHtml(element);
Expand Down
90 changes: 67 additions & 23 deletions apps/a11y-tests/src/tests/ComponentExamples.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { getSarifReport } from '../getSarifReport';
import { SarifLog } from 'axe-sarif-converter/dist/sarif/sarif-log';
import { Result } from 'axe-sarif-converter/dist/sarif/sarif-2.0.0';

const ReactDOM = require('react-dom');

// Keep only errors to reduce snapshot size
function dehydrateSarifReport(report: SarifLog): Result[] {
return report.runs[0]!.results!.filter(item => item.level === 'error');
Expand All @@ -15,14 +17,17 @@ function dehydrateSarifReport(report: SarifLog): Result[] {
async function testComponent(component: { name: string; pageName: string; elem: React.ReactElement<any> }) {
it(`checks accessibility of ${component.name} (${component.pageName})`, async () => {
const sarifReport: SarifLog = await getSarifReport(component.elem);
const errors = dehydrateSarifReport(sarifReport);

// Save the report into `dist/reports` folder
fs.writeFileSync(path.resolve(__dirname, `../../dist/reports/${component.pageName}.sarif`), JSON.stringify(sarifReport), {
encoding: 'utf8'
});
// Save the report into `dist/reports` folder (only when there're errors)
if (errors.length > 0) {
fs.writeFileSync(path.resolve(__dirname, `../../dist/reports/${component.pageName}.sarif`), JSON.stringify(sarifReport), {
encoding: 'utf8'
});
}

// Match the 'errors' section with snapshot
expect(dehydrateSarifReport(sarifReport)).toMatchSnapshot();
expect(errors).toMatchSnapshot();
});
}

Expand All @@ -31,28 +36,67 @@ function getControlAndPageName(exampleFilePath: string): [string, string] {
return [match[1], match[2]];
}

// List of controls we expose to a11y tests
const enabledControls: string[] = ['Button', 'TextField'];
const files: string[] = [];
const excludedExampleFiles: string[] = ['Keytips.Basic.Example', 'List.Basic.Example', 'Picker.CustomResult.Example'];

/* tslint:disable-next-line:no-any */
declare const global: any;

describe('a11y test', () => {
const constantDate = new Date(Date.UTC(2017, 0, 6, 4, 41, 20));

beforeAll(() => {
// Mock createPortal to capture its component hierarchy in snapshot output.
ReactDOM.createPortal = jest.fn(element => {
return element;
});

// Ensure test output is consistent across machine locale and time zone config.
const mockToLocaleString = () => {
return constantDate.toUTCString();
};

enabledControls.forEach((control: string) => {
global.Date.prototype.toLocaleString = mockToLocaleString;
global.Date.prototype.toLocaleTimeString = mockToLocaleString;
global.Date.prototype.toLocaleDateString = mockToLocaleString;

// Prevent random and time elements from failing repeated tests.
global.Date = class {
public static now() {
return constantDate;
}

constructor() {
return constantDate;
}
};

jest.spyOn(Math, 'random').mockImplementation(() => {
return 0;
});
});

const files: string[] = [];
const exampleFiles: string[] = glob.sync(
path.resolve(process.cwd(), `node_modules/office-ui-fabric-react/lib-commonjs/components/${control}/examples/*Example*.js`)
path.resolve(process.cwd(), `node_modules/office-ui-fabric-react/lib-commonjs/components/**/examples/*Example*.js`)
);
files.push(...exampleFiles);
});

files.forEach((componentFile: string) => {
const componentModule = require(componentFile);
const [controlName, pageName] = getControlAndPageName(componentFile);
Object.keys(componentModule)
.filter(key => typeof componentModule[key] === 'function')
.forEach(key => {
const ComponentUnderTest: React.ComponentClass = componentModule[key];
testComponent({
name: controlName,
pageName: pageName,
elem: <ComponentUnderTest />
});
files
.filter((componentFile: string) => {
return !excludedExampleFiles.some(excludedFile => componentFile.indexOf('/' + excludedFile) !== -1);
})
.forEach((componentFile: string) => {
const componentModule = require(componentFile);
const [controlName, pageName] = getControlAndPageName(componentFile);
Object.keys(componentModule)
.filter(key => typeof componentModule[key] === 'function')
.forEach(key => {
const ComponentUnderTest: React.ComponentClass = componentModule[key];
testComponent({
name: controlName,
pageName: pageName,
elem: <ComponentUnderTest />
});
});
});
});
Loading

0 comments on commit b252b15

Please sign in to comment.