docsify/test/e2e/example.test.js
John Hildenbiddle 7f0c42eda5
Jest + Playwright Testing (#1276)
* Initial Jest+Playwright setup

* Fix eslint warnings

* Add placeholder for common options

* Fix load order of scriptURLs

* Add docsifyURL and waitForSelector options

* Add executeScript scenarios

* Enable firefox and webkit tests by default

* Prevent prettier from reformatting browsers list

* Update options and add JSDoc comments

* Initial commit

* Complete initial example tests

* Minor tweaks

* Change suite name

* Rename file

* Add npm script to run jest+playwright example test

* Clean up server properties

* Isolate e2e, integration, and unit test environments

- Setup projects for e2e, integration, and unit tests in Jest configuration
- Setup /tests/e2e/ tests for Jest+PlayWright
- Setup /tests/integration/ tests for Jest
- Setup /tests/unit/ tests for Jest
- Setup eslint for Jest and Jest+Playwright environments
- Move e2e configuration files into separate folder
- Update e2e example tests
- Update unit example tests
- Update HTML fixtures

* Update docsifyInit helper

- Add `debug` option
- Append `Markdown` suffix to markdown-related options
- Reorder options alphabetically

* Add jestPlaywright.debug() to e2e examples

* Removed need to pass page as arg to docsifyInit()

* Add VSCode debug configurations

* Change test server port from 8080 to 3001

* Clean up test names and expect() order

* Update npm scripts to ignore example tests

* Add CLI commands and resources section

* Add Jest unit test snapshot example

* Added Jest unit and e2e tests to Github workflows

* Add npm script to run Jest examples

Added for new devs who are exploring the examples, as the need for passing --testPathIgnorePatterns is not obvious. This flag is required because the example tests are excluded by default.

* Remove node 10.x and add 14.x from tests

Required for jest+playwright testitng

* Temporarily disable testPathIgnorePatterns for ci

* Temporarily remove node 14 from matrix

Cypress fails on node 14.x

* Alternate workflow setup for new test env

* Update workflow platforms and node versions

Also cleaning up naming

* Restoring original workflow names

* Remove Cypress tests

* Remove/Reset GitHub actions

* Restore/reset workflows

* Bump actions/checkout@v1 to v2

* Use playright GH action

* Update playwright GH action configuration

* Remove unnecessary steps and update checkout version

* Add install step

* Add --ignore-scripts flag

Linting does not require running scripts automatically executred after install.

* Swicth from ci to i for install

* Add new Build & Test workflow

* Add OS tests

* Wait for network idle for more reliable ready state

* Configure image snapshot testing and add example

* Remove example fixture file

* Adjust image snapshot threshold for local & CI tests

* Upload diff-output artifact

* Add failure() check to upload-artifact

* Bump image snapshot threshold for local & CI tests

* Set diffDirection

* Fix XHR caching in playwright tests

* Update image snapshot example and theshold

* Bump image snapshot threshold for local & CI tests

* Remove old workflows

* Bump image snapshot threshold for local & CI tests

* Bump image snapshot failureThreshold for local & CI tests

* Set failureThresholdType to percent

* Change image snapshot comparisonMethod to ssim

* Remove pixelMatch options (incompatible with ssim)

* Bump image snapshot failureThreshold

* Bump image snapshot failureThreshold

* Disable fast-fail to allow all checks to complete

* Rename workflow

* Disable fast-fail to allow all checks to complete

* Store pixel and ssim comparison configs

* Add link to image snapshot test

* Fix CI errors on Windows due to image size mismatch

* Attempt to fix macos e2e exit code 134 issue

* Update test.yml

* Update test.yml

* Update test.yml

* Fix --ci flag

* Set Jets to use --runInBand for macOS e2e

* Remove unnecessary macOS check

* Set image snapshot to runInProcess (macOS CI fix)

* Update test.yml

* Temporary failureThreshold change for debugging

* Upload os+node-specific diff artifacts

* Remove node version from diff artifact

* Revert "Remove node version from diff artifact"

This reverts commit 9cfcc4342bb22f18da30363a4c52758f13ba0cc9.

* Revert "Revert "Remove node version from diff artifact""

This reverts commit ad6c1891e48c70c9973a5c0d7b876b2d4621f3b9.

* Restoring failureThreshold after debugging

* Remove runInProcess option

* Revert "Remove runInProcess option"

This reverts commit 667ed6c870fc56f0349fc5eccc2cdeead3eff4ea.

* Add node-specific artifact uploads

* Set ssim mode to fast and restore runInProcess

* Set failureThreshold to 0.01

* Updating to playwright@next for webkit fix

* Restore optimal ssim configuration

* Testing pixel vs. ssim image comparison

* Move shared test setup to unit/int/e2e setups

* Refactor test server setup

* Replace live-server with browser-sync

* Update script for running local docs site

- Only init GA plugin on public site
- Only init Gitter plugin on public site
- Only init Matomo plugin on public site

NOTE: Large diff is a result of initial Prettier formatting. Actual change limited to last <script> block on page.

* Allow launching test server preview with --start

* Add integration tests and refactor unit + e2e

* Unify docsifyInit() and cleanup

- Create unified docsifyInit() for jest and playwright
- Move shared helpers to /tests/helpers
- Update tests
- Update globals

* Fixed webkit routes by specifying ContentType

* Update snapshot

* Update dependencies

* Update tests to use unified docsifyInit()

* Remove Cypress (old e2e test environment)

* Update tests to run (unit/integration/e2e)

- Add new integration tests (Jest)
- Remove old unit tests (mocha+jsdom)
- Remove old e2e tests (Cypress)

* Remove Cypress-related dependency

* Remove mocha+chai+jsdom (old unit/int test env)

* Rename testing directories and scripts

- Rename /tests/ directory to /test/
- Rename script/task names by removing “jest” identifier
- Remove “test:jest-examples” script

* - Configure test server for availability with all tests (previously e2e only)
- Create identical docsifyInit() tests using Jest (integration) and Playwright (e2e)
- Update docsifyInit() to convert relative URLs to absolute URLs to work in both JSDOM and Playwright
- Update docsifyInit() to append style- and js-related tags using createElement instead of insertAdjacentHTML
- Update paths in test files to use unified docsifyInit()
- Added option to docsifyInit() to enable/disable formatted HTML output to console
- Removed vue.css as default docsify theme from docsifyInit()
- Removed outdated files

* Reorganize test files

* Fix basePath option

* Replace do-mock-ajax with xhr-mock

- Allows mocking all XMLHttpRequests instead of just those initiated via /src/core/fetch/ajax.js
- Allows JSDOM tests to use /lib/docsify.js instead of /src/core/index.js (same as Playwright tests)
- Allows JSDOM tests to use /docs site as test content

* Added new waitFor helpers

* Clean up globals

- Import globals from various files instead of manually adding them to ensure they are availability in Jest and eslint configurations
- Add middleware to server configuration for serving virtual “_blank.html” file
- Add BLANK_URL
- Rename TEST_URL to TEST_HOST
- Removed ./test/fixtures/ directory (blank page now served via server.js middleware)
- Added page.goto(BLANK_URL) call to global Playwright beforeEach() setup

* Add try/catch for waitForFunction callback

* Move playwright config into jest.config.js

* Add runInBand option to tests for reliability

* Remove unnecessary XHR Mock teardown

* Add —runInBand to test script

* Merge develop

* Cleanup

- Relocate carbon plugin script with other scripts
- Update zh-cn docs URL to align with other translatins (GitHub, not jsdelivr)
- Add major version locks to URLs

* Removed fixed host value from docs

* Updated test after merge (docsify version change)

* Added startPath for manual instance

* Remove Node 10 from CI tests

* Remove Node 10 from CI lint tests
2020-10-05 14:10:30 -05:00

345 lines
12 KiB
JavaScript

// Modules, constants, and variables
// -----------------------------------------------------------------------------
const docsifyInit = require('../helpers/docsify-init');
// Suite
// -----------------------------------------------------------------------------
describe(`Example Tests`, function() {
// Tests
// ---------------------------------------------------------------------------
test('dom manipulation', async () => {
const testText = 'This is a test';
const testHTML = `<h1>Test</h1><p>${testText}</p>`;
// Inject HTML
// https://playwright.dev/#path=docs%2Fapi.md&q=pagesetcontenthtml-options
await page.setContent(testHTML);
// Add class to <body> element and test
// https://playwright.dev/#path=docs%2Fapi.md&q=pageevalselector-pagefunction-arg
await page.$eval('body', elm => elm.classList.add('foo'));
expect(await page.getAttribute('body', 'class')).toEqual('foo');
// Test using helper methods from expect-playright (via jest-playwright)
// https://github.com/playwright-community/expect-playwright
// https://playwright.tech/blog/using-jest-with-playwright
await expect(page).toHaveText('body', 'Test');
await expect(page).toHaveSelector('p');
await expect(page).toEqualText('p', testText);
await expect(page).not.toHaveSelector('table', { timeout: 1 });
// Test using standard jest + playwrite methods
// https://playwright.dev/#path=docs%2Fapi.md&q=pagetextcontentselector-options
expect(await page.textContent('body')).toMatch(/Test/);
await page.waitForSelector('p');
expect(await page.textContent('p')).toEqual(testText);
await page.waitForSelector('table', { state: 'detached' });
// Debug mode
// https://github.com/playwright-community/jest-playwright#put-in-debug-mode
// await jestPlaywright.debug();
});
test('javascript in browser context', async () => {
// Get native DOM values
// https://playwright.dev/#path=docs%2Fapi.md&q=pageevaluatepagefunction-arg
const clientDimensions = await page.evaluate(() => {
return {
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight,
};
});
expect(clientDimensions).toHaveProperty('width');
expect(typeof clientDimensions.width).toBe('number');
expect(clientDimensions).toHaveProperty('height');
expect(typeof clientDimensions.height).toBe('number');
// Get result of script executed in browser context
// https://playwright.dev/#path=docs%2Fapi.md&q=pageevaluatepagefunction-arg
const scriptResult = await page.evaluate(
numbers => {
const result = numbers.reduce(
(accumulator, currentValue) => accumulator + currentValue
);
return Promise.resolve(result);
},
[1, 2, 3]
);
expect(scriptResult).toBe(6);
// Get result of local function executed in browser context
// https://playwright.dev/#path=docs%2Fapi.md&q=pageevaluatepagefunction-arg
function add(...addends) {
return addends.reduce(
(accumulator, currentValue) => accumulator + currentValue
);
}
const functionResult = await page.evaluate(`
${add.toString()}
const result = add(1, 2, 3);
Promise.resolve(result);
`);
expect(functionResult).toBe(6);
});
test('manual docsify site using playwright methods', async () => {
// Goto URL
// https://playwright.dev/#path=docs%2Fapi.md&q=pagegotourl-options
await page.goto(BLANK_URL);
// Set docsify configuration
// https://playwright.dev/#path=docs%2Fapi.md&q=pageevaluatepagefunction-arg
await page.evaluate(() => {
window.$docsify = {
el: '#app',
basePath: '/docs',
themeColor: 'red',
};
});
// Add docsify target element
// https://playwright.dev/#path=docs%2Fapi.md&q=pageevalselector-pagefunction-arg
await page.$eval('body', elm => {
elm.innerHTML = '<div id="app"></div>';
});
// Inject docsify theme (vue.css)
// https://playwright.dev/#path=docs%2Fapi.md&q=pageaddstyletagoptions
await page.addStyleTag({ url: `${LIB_URL}/themes/vue.css` });
// Inject docsify.js
// https://playwright.dev/#path=docs%2Fapi.md&q=pageaddscripttagoptions
await page.addScriptTag({ url: `${LIB_URL}/docsify.js` });
// Wait for docsify to initialize
// https://playwright.dev/#path=docs%2Fapi.md&q=pagewaitforselectorselector-options
await page.waitForSelector('#main');
// Create handle for JavaScript object in browser
// https://playwright.dev/#path=docs%2Fapi.md&q=pageevaluatepagefunction-arg
const $docsify = await page.evaluate(() => window.$docsify);
// Test object property and value
expect($docsify).toHaveProperty('themeColor', 'red');
});
test('Docsify /docs/ site using docsifyInit()', async () => {
// Load custom docsify
// (See ./helpers/docsifyInit.js for details)
await docsifyInit({
config: {
basePath: '/docs/',
},
// _debug: true,
// _logHTML: true,
});
// Create handle for JavaScript object in browser
// https://playwright.dev/#path=docs%2Fapi.md&q=pageevaluatepagefunction-arg
const $docsify = await page.evaluate(() => window.$docsify);
// Verify config options
expect(typeof $docsify).toEqual('object');
// Verify docsifyInitConfig.markdown content was rendered
await expect(page).toHaveText(
'#main',
'A magical documentation site generator'
);
});
test('custom docsify site using docsifyInit()', async () => {
const docsifyInitConfig = {
config: {
name: 'Docsify Name',
themeColor: 'red',
},
markdown: {
coverpage: `
# Docsify Test
> Testing a magical documentation site generator
[GitHub](https://github.com/docsifyjs/docsify/)
`,
homepage: `
# Hello World
This is the homepage.
`,
navbar: `
- [docsify.js.org](https://docsify.js.org/#/)
`,
sidebar: `
- [Test Page](test)
`,
},
routes: {
'/test.md': `
# Test Page
This is a custom route.
`,
'/data-test-scripturls.js': `
document.body.setAttribute('data-test-scripturls', 'pass');
`,
},
script: `
document.body.setAttribute('data-test-script', 'pass');
`,
scriptURLs: [
// docsifyInit() route
'/data-test-scripturls.js',
// Server route
'/lib/plugins/search.min.js',
],
style: `
body {
background: red !important;
}
`,
styleURLs: ['/lib/themes/vue.css'],
};
await docsifyInit({
...docsifyInitConfig,
// _debug: true,
// _logHTML: true,
});
const $docsify = await page.evaluate(() => window.$docsify);
// Verify config options
expect(typeof $docsify).toEqual('object');
expect($docsify).toHaveProperty('themeColor', 'red');
await expect(page).toHaveText('.app-name', 'Docsify Name');
// Verify docsifyInitConfig.markdown content was rendered
await expect(page).toHaveText('section.cover', 'Docsify Test'); // Coverpage
await expect(page).toHaveText('nav.app-nav', 'docsify.js.org'); // Navbar
await expect(page).toHaveText('aside.sidebar', 'Test Page'); // Sidebar
await expect(page).toHaveText('#main', 'This is the homepage'); // Homepage
// Verify docsifyInitConfig.scriptURLs were added to the DOM
for (const scriptURL of docsifyInitConfig.scriptURLs) {
await expect(page).toHaveSelector(`script[src$="${scriptURL}"]`, {
state: 'attached',
});
}
// Verify docsifyInitConfig.scriptURLs were executed
await expect(page).toHaveSelector('body[data-test-scripturls]');
await expect(page).toHaveSelector('.search input[type="search"]');
// Verify docsifyInitConfig.script was added to the DOM
expect(
await page.evaluate(scriptText => {
return [...document.querySelectorAll('script')].some(
elm => elm.textContent.replace(/\s+/g, '') === scriptText
);
}, docsifyInitConfig.script.replace(/\s+/g, ''))
).toBe(true);
// Verify docsifyInitConfig.script was executed
await expect(page).toHaveSelector('body[data-test-script]');
// Verify docsifyInitConfig.styleURLs were added to the DOM
for (const styleURL of docsifyInitConfig.styleURLs) {
await expect(page).toHaveSelector(
`link[rel*="stylesheet"][href$="${styleURL}"]`,
{
state: 'attached',
}
);
}
// Verify docsifyInitConfig.style was added to the DOM
expect(
await page.evaluate(styleText => {
return [...document.querySelectorAll('style')].some(
elm => elm.textContent.replace(/\s+/g, '') === styleText
);
}, docsifyInitConfig.style.replace(/\s+/g, ''))
).toBe(true);
// Verify docsify navigation and docsifyInitConfig.routes
await page.click('a[href="#/test"]');
expect(page.url()).toMatch(/\/test$/);
await expect(page).toHaveText('#main', 'This is a custom route');
});
test('image snapshots', async () => {
await docsifyInit({
config: {
name: 'Docsify Test',
},
markdown: {
homepage: `
# The Cosmos Awaits
[Carl Sagan](https://en.wikipedia.org/wiki/Carl_Sagan)
Cosmic ocean take root and flourish decipherment hundreds of thousands
dream of the mind's eye courage of our questions. At the edge of forever
network of wormholes ship of the imagination two ghostly white figures
in coveralls and helmets are softly dancing are creatures of the cosmos
the only home we've ever known? How far away emerged into consciousness
bits of moving fluff gathered by gravity with pretty stories for which
there's little good evidence vanquish the impossible.
The ash of stellar alchemy permanence of the stars shores of the cosmic
ocean billions upon billions Drake Equation finite but unbounded.
Hundreds of thousands cosmic ocean hearts of the stars Hypatia invent
the universe hearts of the stars? Realm of the galaxies muse about dream
of the mind's eye hundreds of thousands the only home we've ever known
how far away. Extraordinary claims require extraordinary evidence
citizens of distant epochs invent the universe as a patch of light the
carbon in our apple pies gathered by gravity.
Billions upon billions gathered by gravity white dwarf intelligent
beings vanquish the impossible descended from astronomers. A still more
glorious dawn awaits cosmic ocean star stuff harvesting star light the
sky calls to us kindling the energy hidden in matter rich in heavy
atoms. A mote of dust suspended in a sunbeam across the centuries the
only home we've ever known bits of moving fluff a very small stage in a
vast cosmic arena courage of our questions.
Euclid the only home we've ever known realm of the galaxies trillion
radio telescope Apollonius of Perga. The carbon in our apple pies invent
the universe muse about stirred by starlight great turbulent clouds
emerged into consciousness? Invent the universe vastness is bearable
only through love a still more glorious dawn awaits descended from
astronomers as a patch of light the sky calls to us. Great turbulent
clouds citizens of distant epochs invent the universe two ghostly white
figures in coveralls and helmets are softly dancing courage of our
questions rich in heavy atoms and billions upon billions upon billions
upon billions upon billions upon billions upon billions.
`,
},
styleURLs: [`/lib/themes/vue.css`],
// _debug: true,
// _logHTML: true,
});
// Viewport screenshot
const screenshot1 = await page.screenshot();
expect(screenshot1).toMatchImageSnapshot();
// Full page screenshot
const screenshot2 = await page.screenshot({ fullPage: true });
expect(screenshot2).toMatchImageSnapshot();
// Element screenshot
const elmHandle = await page.$('h1');
const screenshot3 = await elmHandle.screenshot();
expect(screenshot3).toMatchImageSnapshot();
});
});