Implemented Gemini screenshot testing base

This commit is contained in:
squidfunk 2017-01-28 20:25:30 +01:00
parent 8f46cc6f09
commit 3783eceb09
22 changed files with 521 additions and 151 deletions

View File

@ -18,10 +18,11 @@
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
# Build files
# Files generated by build
/build
/material
/site
# Gemini reports
# Files generated by visual tests
/gemini-report
/tests/visual/data

6
.gitignore vendored
View File

@ -25,14 +25,16 @@
/node_modules
/npm-debug.log
# Build files
# Files generated by build
/build
/manifest.json
/MANIFEST
/site
# Gemini reports
# Files generated by visual tests
/gemini-report
/tests/visual/baseline/local
/tests/visual/data
# Distribution files
/dist

View File

@ -37,7 +37,7 @@ const config = {
},
lib: "lib", /* Libraries and tasks */
tests: {
visual: "tests/visual" /* Visual regression tests */
visual: "tests/visual" /* Base directory for visual tests */
},
views: {
src: "src", /* Source directory for views */
@ -95,7 +95,10 @@ gulp.src = (...glob) => {
* Helper function to load a task
*/
const load = task => {
return require(`./${config.lib}/tasks/${task}`)(gulp, config, args)
return done => {
return require(`./${config.lib}/tasks/${task}`)
.call(gulp, gulp, config, args)(done)
}
}
/* ----------------------------------------------------------------------------
@ -269,22 +272,37 @@ gulp.task("mkdocs:serve",
load("mkdocs/serve"))
/* ----------------------------------------------------------------------------
* Tests
* Visual tests
* ------------------------------------------------------------------------- */
/*
* Generate visual tests
*/
gulp.task("tests:visual:generate", (args.clean ? [
// "assets:build", // TODO: only commented out for dev
// "views:build",
"tests:visual:clean"
] : []),
load("tests/visual/generate"))
/*
* Run visual tests
*/
gulp.task("tests:visual:run", [
// "assets:build",
// "views:build"
"tests:visual:generate"
], load("tests/visual/run"))
/*
* Start karma test runner
* Update reference images for visual tests
*/
gulp.task("tests:unit:watch",
() => {})
gulp.task("tests:visual:update",
load("tests/visual/update"))
/*
* Clean files generated by visual tests
*/
gulp.task("tests:visual:clean",
load("tests/visual/clean"))
/* ----------------------------------------------------------------------------
* Interface

50
lib/servers/ecstatic.js Normal file
View File

@ -0,0 +1,50 @@
/*
* Copyright (c) 2016-2017 Martin Donath <martin.donath@squidfunk.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
import ecstatic from "ecstatic"
import * as http from "http"
/* ----------------------------------------------------------------------------
* Locals
* ------------------------------------------------------------------------- */
/* Static file server */
let server = null
/* ----------------------------------------------------------------------------
* Functions
* ------------------------------------------------------------------------- */
export const start = (directory, port, done) => {
server = http.createServer(ecstatic({
root: directory
}))
/* Listen and register signal handlers */
server.listen(port, "127.0.0.1", done)
for (const signal of ["SIGTERM", "SIGINT", "exit"])
process.on(signal, stop)
}
export const stop = done => {
server.close(done)
}

View File

@ -35,15 +35,24 @@ let server = null
export const start = done => {
selenium.start({}, (err, proc) => {
/* Register signal handlers */
for (const signal of ["SIGTERM", "SIGINT", "exit"])
process.on(signal, stop)
if (err) {
/* Install selenium, if not present */
if (/^Missing(.*)chromedriver$/.test(err.message)) {
selenium.install(done)
new Promise(resolve => {
selenium.install({}, resolve)
})
/* Start selenium again */
.then(() => {
selenium.start({}, (err_, proc_) => {
server = proc_
done()
})
})
/* Otherwise, throw error */
@ -53,19 +62,18 @@ export const start = done => {
}
/* Remember process handle */
server = server || proc
server = proc
done()
})
}
export const stop = () => {
if (server)
export const stop = done => {
if (server) {
if (typeof done === "function")
server.on("exit", done)
server.kill()
/* Unset, so we don't try to kill the server twice */
server = null
}
}
/* ----------------------------------------------------------------------------
* Signals
* ------------------------------------------------------------------------- */
for (const signal of ["SIGTERM", "SIGINT", "exit"])
process.on(signal, stop)

View File

@ -0,0 +1,38 @@
/*
* Copyright (c) 2016-2017 Martin Donath <martin.donath@squidfunk.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
import clean from "del"
import vinyl from "vinyl-paths"
/* ----------------------------------------------------------------------------
* Task: clean files generated by visual tests
* ------------------------------------------------------------------------- */
export default (gulp, config) => {
return () => {
return gulp.src([
`${config.tests.visual}/data`,
"./gemini-report"
])
.pipe(vinyl(clean))
}
}

View File

@ -0,0 +1,63 @@
/*
* Copyright (c) 2016-2017 Martin Donath <martin.donath@squidfunk.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
import child from "child_process"
import path from "path"
import through from "through2"
import util from "gulp-util"
/* ----------------------------------------------------------------------------
* Task: generate visual tests
* ------------------------------------------------------------------------- */
export default (gulp, config) => {
const theme = path.resolve(process.cwd(), config.views.build)
return () => {
return gulp.src(`${config.tests.visual}/suites/**/mkdocs.yml`)
.pipe(
through.obj(function(file, enc, done) {
if (file.isNull() || file.isStream())
return done()
/* Resolve test name and destination */
const name = path.relative(`${config.tests.visual}/suites`,
path.dirname(file.path))
const site = path.resolve(process.cwd(),
`${config.tests.visual}/data`, name, "_")
/* Generate test fixtures with freshly built theme */
const proc = child.spawnSync("mkdocs", [
"build", "--site-dir", site, "--theme-dir", theme
], {
cwd: path.dirname(file.path)
})
/* Emit error, if any */
if (proc.status)
this.emit("error", new util.PluginError("mkdocs",
`Terminated with errors: ${proc.stderr.toString()}`))
/* Terminate */
done()
}))
}
}

View File

@ -20,62 +20,91 @@
* IN THE SOFTWARE.
*/
import child from "child_process"
import path from "path"
import * as ecstatic from "~/lib/servers/ecstatic"
import * as selenium from "~/lib/servers/selenium"
import Gemini from "gemini"
/* ----------------------------------------------------------------------------
* Task: start test runner
* Test runner: Selenium
* ------------------------------------------------------------------------- */
/* MkDocs server */
let server = null
class SeleniumTestRunner {
/**
* Start Selenium
*
* @param {Function} done - Resolve callback
*/
start(done) {
selenium.start(done)
}
/**
* Stop Selenium
*
* @param {Function} done - Resolve callback
*/
stop(done) {
selenium.stop(done)
}
}
/* ----------------------------------------------------------------------------
* Task: start test runner
* Task: run visual tests
* ------------------------------------------------------------------------- */
export default (gulp, config) => {
return () => {
export default (gulp, config, args) => {
return done => {
/* Start MkDocs server */
return new Promise(resolve => {
server = child.spawn("mkdocs", [
"serve", "--dev-addr", "127.0.0.1:8000"
], {
cwd: config.tests.visual,
stdio: [process.stdin, process.stdout, "pipe"]
})
/* Start static file server */
new Promise(resolve => {
ecstatic.start(`${config.tests.visual}/data`, 8000, resolve)
/* Wait for MkDocs server and resolve promise */
server.stderr.on("data", data => {
if (data.toString().match("Serving")) {
server.stderr.removeAllListeners("data")
resolve()
}
})
/* Start Selenium */
/* Create and start test runner */
}).then(() => {
return new Promise(resolve => {
selenium.start(() => resolve())
/* Start Gemini test runner depending on environment */
}).then(() => {
const gemini = require(path.join(
process.cwd(), `${config.tests.visual}/.gemini-local.json`))
return new Gemini(gemini).test("tests/visual/suites", {
reporters: ["html"]
return new Promise((resolve, reject) => {
const runner = new SeleniumTestRunner()
runner.start(err => {
return err ? reject(err) : resolve(runner)
})
})
/* Setup and run Gemini */
.then(runner => {
const gemini = require(
path.join(process.cwd(), `${config.tests.visual}/config`,
process.env.CI || process.env.SAUCE
? "gemini-sauce.json"
: "gemini-local.json"))
/* Start Gemini and return runner upon finish */
return new Gemini(gemini).test(`${config.tests.visual}/suites`, {
reporters: [process.env.CI ? "flat" : "html"],
grep: args.grep ? new RegExp(args.grep, "i") : null,
browsers: args.browsers ? [].concat(args.browsers) : null
})
/* Return runner for graceful stop */
.then(() => {
return runner
})
})
/* Stop test runner */
.then(runner => {
return new Promise(resolve => {
runner.stop(resolve)
})
})
/* Stop static file server */
})
.then(() => {
selenium.stop()
})
})
.then(() => {
server.kill()
ecstatic.stop(done)
}, err => {
return done(err)
})
}
}

View File

@ -0,0 +1,72 @@
/*
* Copyright (c) 2016-2017 Martin Donath <martin.donath@squidfunk.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
import fs from "fs"
import path from "path"
import through from "through2"
/* ----------------------------------------------------------------------------
* Task: update reference images for visual tests
* ------------------------------------------------------------------------- */
export default (gulp, config) => {
return () => {
const base = path.join(
process.cwd(), `${config.tests.visual}/config`)
/* Read Gemini configs and map browsers to screenshot directories */
const mapping = fs.readdirSync(base)
.reduce((result, filename) => {
return Object.assign(result, (gemini => {
return Object.keys(gemini.browsers)
.reduce((browsers, name) => {
browsers[name] = gemini.screenshotsDir
return browsers
}, {})
})(require(path.join(base, filename))))
}, {})
/* Prepare filenames */
const dest = path.join(process.cwd(), `${config.tests.visual}/baseline`)
return gulp.src("gemini-report/images/**/*~current.png")
.pipe(
through.obj(function(file, enc, done) {
if (file.isNull() || file.isStream())
return done()
/* Remove the state from the filename */
file.path = file.path.replace("~current", "")
/* Retrieve the folder for the environment of the baseline */
const folder = path.relative(dest,
mapping[path.basename(file.path, ".png")])
file.path = file.path.replace("images", `images/${folder}`)
/* Push file to next stage */
this.push(file)
done()
}))
/* Update reference images */
.pipe(gulp.dest(dest))
}
}

View File

@ -45,7 +45,7 @@
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
{% endblock %}
{% block styles %}
<link rel="stylesheet" href="{{ base_url }}/assets/stylesheets/application-bc099a55ca.css">
<link rel="stylesheet" href="{{ base_url }}/assets/stylesheets/application-e21055ee85.css">
{% if config.extra.palette %}
<link rel="stylesheet" href="{{ base_url }}/assets/stylesheets/application-02ce7adcc2.palette.css">
{% endif %}

View File

@ -27,7 +27,8 @@
"clean": "scripts/clean",
"lint": "scripts/lint",
"start": "scripts/start",
"test": "scripts/test"
"test:visual:run": "scripts/test/visual/run",
"test:visual:update": "scripts/test/visual/update"
},
"dependencies": {},
"devDependencies": {
@ -45,6 +46,7 @@
"css-mqpacker": "^5.0.1",
"custom-event-polyfill": "^0.3.0",
"del": "^2.2.2",
"ecstatic": "^2.1.0",
"eslint": "^3.14.0",
"eslint-plugin-mocha": "^4.8.0",
"fastclick": "^1.0.6",

View File

@ -28,4 +28,4 @@ if [[ ! -d `npm bin` ]]; then
fi
# Run command
`npm bin`/gulp build --clean --optimize --revision
`npm bin`/gulp build --clean --optimize --revision $@

View File

@ -28,4 +28,4 @@ if [[ ! -d `npm bin` ]]; then
fi
# Run command
`npm bin`/gulp clean
`npm bin`/gulp clean $@

View File

@ -28,4 +28,4 @@ if [[ ! -d `npm bin` ]]; then
fi
# Run command
`npm bin`/gulp watch --no-lint
`npm bin`/gulp watch --no-lint $@

31
scripts/test/visual/run Executable file
View File

@ -0,0 +1,31 @@
#!/bin/bash
# Copyright (c) 2016-2017 Martin Donath <martin.donath@squidfunk.com>
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
# Check if "npm install" was executed
if [[ ! -d `npm bin` ]]; then
echo "\"node_modules\" not found:"
echo "npm install"
exit 1
fi
# Run command
`npm bin`/gulp tests:visual:run --clean --optimize --revision $@

View File

@ -28,4 +28,4 @@ if [[ ! -d `npm bin` ]]; then
fi
# Run command
`npm bin`/gulp test
`npm bin`/gulp tests:visual:update $@

View File

@ -1,6 +1,6 @@
{
"rootUrl": "http://localhost:8000",
"screenshotsDir": "./tests/visual/baseline",
"screenshotsDir": "./tests/visual/baseline/local",
"browsers": {
"local-chrome": {
"desiredCapabilities": {
@ -10,6 +10,6 @@
},
"system": {
"projectRoot": "./",
"sourceRoot": "src/assets/stylesheets"
"sourceRoot": "./src/assets/stylesheets"
}
}

View File

@ -1,75 +0,0 @@
# Copyright (c) 2016-2017 Martin Donath <martin.donath@squidfunk.com>
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
# Project information
site_name: Material for MkDocs Tests
site_description: A Material Design theme for MkDocs
site_author: Martin Donath
site_url: http://squidfunk.github.io/mkdocs-material/
# Repository
repo_name: squidfunk/mkdocs-material
repo_url: https://github.com/squidfunk/mkdocs-material
# Copyright
copyright: 'Copyright &copy; 2016 Martin Donath'
# Documentation and theme directories
docs_dir: fixtures
theme_dir: ../../material
# Options
extra:
palette:
primary: indigo
accent: indigo
social:
- type: github-alt
link: https://github.com/squidfunk
- type: twitter
link: https://twitter.com/squidfunk
- type: linkedin
link: https://de.linkedin.com/in/martin-donath-20a95039
# Extensions
markdown_extensions:
- markdown.extensions.admonition
- markdown.extensions.codehilite(guess_lang=false)
- markdown.extensions.footnotes
- markdown.extensions.meta
- markdown.extensions.toc(permalink=true)
- pymdownx.arithmatex
- pymdownx.betterem(smart_enable=all)
- pymdownx.caret
- pymdownx.critic
- pymdownx.emoji:
emoji_generator: !!python/name:pymdownx.emoji.to_svg
- pymdownx.inlinehilite
- pymdownx.magiclink
- pymdownx.mark
- pymdownx.smartsymbols
- pymdownx.superfences
- pymdownx.tasklist(custom_checkbox=true)
- pymdownx.tilde
# Page tree
pages:
- Extensions:
- Admonition: extensions/admonition.md

View File

@ -0,0 +1,107 @@
# Admonition Tests
<style>
.md-header {
display: none;
}
</style>
## Default
!!! note
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla et euismod
nulla. Curabitur feugiat, tortor non consequat finibus, justo purus auctor
massa, nec semper lorem quam in massa.
## Format
### Custom title
!!! note "Phasellus posuere in sem ut cursus"
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla et euismod
nulla. Curabitur feugiat, tortor non consequat finibus, justo purus auctor
massa, nec semper lorem quam in massa.
### Long title
!!! note "Phasellus posuere in sem ut cursus. Nullam sit amet tincidunt ipsum, sit amet elementum turpis. Etiam ipsum quam, mattis in purus vitae, lacinia fermentum enim."
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla et euismod
nulla. Curabitur feugiat, tortor non consequat finibus, justo purus auctor
massa, nec semper lorem quam in massa.
### Empty title
!!! note ""
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla et euismod
nulla. Curabitur feugiat, tortor non consequat finibus, justo purus auctor
massa, nec semper lorem quam in massa.
## Types
### Note
!!! note
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla et euismod
nulla. Curabitur feugiat, tortor non consequat finibus, justo purus auctor
massa, nec semper lorem quam in massa.
### Summary
!!! summary
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla et euismod
nulla. Curabitur feugiat, tortor non consequat finibus, justo purus auctor
massa, nec semper lorem quam in massa.
### Tip
!!! tip
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla et euismod
nulla. Curabitur feugiat, tortor non consequat finibus, justo purus auctor
massa, nec semper lorem quam in massa.
### Success
!!! success
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla et euismod
nulla. Curabitur feugiat, tortor non consequat finibus, justo purus auctor
massa, nec semper lorem quam in massa.
### Warning
!!! warning
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla et euismod
nulla. Curabitur feugiat, tortor non consequat finibus, justo purus auctor
massa, nec semper lorem quam in massa.
### Failure
!!! failure
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla et euismod
nulla. Curabitur feugiat, tortor non consequat finibus, justo purus auctor
massa, nec semper lorem quam in massa.
### Danger
!!! danger
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla et euismod
nulla. Curabitur feugiat, tortor non consequat finibus, justo purus auctor
massa, nec semper lorem quam in massa.
### Bug
!!! bug
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla et euismod
nulla. Curabitur feugiat, tortor non consequat finibus, justo purus auctor
massa, nec semper lorem quam in massa.

View File

@ -0,0 +1,24 @@
# Copyright (c) 2016-2017 Martin Donath <martin.donath@squidfunk.com>
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
# Test suite
site_name: Admonition Test
markdown_extensions:
- markdown.extensions.admonition

View File

@ -20,7 +20,7 @@
* IN THE SOFTWARE.
*/
import generate from "../../generate.js"
import generate from "../../../generate.js"
/* ----------------------------------------------------------------------------
* Tests
@ -32,10 +32,10 @@ generate({
* Admonition block
*
* The admonition block looks the same on everything above tablet
* portrait, so we can save a few testcases.
* portrait, so we can save a few test cases.
*/
".admonition": {
"url": "/extensions/admonition",
"admonition": {
"url": "/extensions/admonition/_",
"capture": "#default + .admonition",
"break": "-@tablet-portrait",
"suite": {