mirror of
https://github.com/astefanutti/decktape.git
synced 2024-08-17 10:10:30 +03:00
test(e2e): add initial testing harness
This commit is contained in:
parent
8d102c9115
commit
c7f3c97a1c
26
.github/workflows/ci.yml
vendored
Normal file
26
.github/workflows/ci.yml
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push: ~
|
||||
pull_request: ~
|
||||
schedule:
|
||||
- cron: '0 12 * * *'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
timeout-minutes: 60
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
- name: Install Playwright Browsers
|
||||
run: npx playwright install --with-deps
|
||||
- name: Run Playwright tests
|
||||
run: npm test
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: always()
|
||||
with:
|
||||
name: playwright-report
|
||||
path: playwright-report/
|
||||
retention-days: 30
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -11,3 +11,7 @@
|
||||
!package.json
|
||||
!npm-shrinkwrap.json
|
||||
!README.adoc
|
||||
!test/
|
||||
test/output/
|
||||
!playwright.config.ts
|
||||
!.github/
|
922
npm-shrinkwrap.json
generated
922
npm-shrinkwrap.json
generated
File diff suppressed because it is too large
Load Diff
@ -11,7 +11,9 @@
|
||||
"decktape": "decktape.js"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "node decktape.js"
|
||||
"start": "node decktape.js",
|
||||
"test-prepare-pdfs": "node test/run-decktape.js",
|
||||
"test": "playwright test"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -30,5 +32,10 @@
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.20"
|
||||
},
|
||||
"devDependencies": {
|
||||
"koa": "2.14.1",
|
||||
"koa-static": "5.0.0",
|
||||
"@playwright/test": "1.28.1"
|
||||
}
|
||||
}
|
||||
|
55
playwright.config.ts
Normal file
55
playwright.config.ts
Normal file
@ -0,0 +1,55 @@
|
||||
import type { PlaywrightTestConfig } from '@playwright/test';
|
||||
import { devices } from '@playwright/test';
|
||||
|
||||
const port = 3010;
|
||||
|
||||
/**
|
||||
* See https://playwright.dev/docs/test-configuration.
|
||||
*/
|
||||
const config: PlaywrightTestConfig = {
|
||||
testDir: './test',
|
||||
timeout: 30 * 1000,
|
||||
expect: {
|
||||
/**
|
||||
* Maximum time expect() should wait for the condition to be met.
|
||||
* For example in `await expect(locator).toHaveText();`
|
||||
*/
|
||||
timeout: 5000
|
||||
},
|
||||
/* Run tests in files in parallel */
|
||||
fullyParallel: true,
|
||||
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
||||
forbidOnly: !!process.env.CI,
|
||||
/* Retry on CI only */
|
||||
retries: process.env.CI ? 2 : 0,
|
||||
/* Opt out of parallel tests on CI. */
|
||||
workers: process.env.CI ? 1 : undefined,
|
||||
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||
reporter: 'html',
|
||||
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
||||
use: {
|
||||
/* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */
|
||||
actionTimeout: 0,
|
||||
/* Base URL to use in actions like `await page.goto('/')`. */
|
||||
|
||||
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
||||
trace: 'on-first-retry',
|
||||
baseURL: `http://localhost:${port}`
|
||||
},
|
||||
|
||||
/* Configure projects for major browsers */
|
||||
projects: [
|
||||
{
|
||||
name: 'firefox',
|
||||
use: devices['Desktop Firefox'],
|
||||
}
|
||||
],
|
||||
|
||||
/* Run your local dev server before starting the tests */
|
||||
webServer: {
|
||||
command: `node test/run-server.js ${port}`,
|
||||
port,
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
21
test/e2e.spec.ts
Normal file
21
test/e2e.spec.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
import fs from "fs";
|
||||
|
||||
test.describe("e2e", () => {
|
||||
const inputDirectories = fs.readdirSync(new URL("./input", import.meta.url));
|
||||
|
||||
inputDirectories.forEach((input) => {
|
||||
test(`should have no visual regression for ${input}`, async ({ page }) => {
|
||||
await page.goto(`/show-pdf.html?file=${input}`);
|
||||
await page.waitForFunction(() => typeof deck === "object");
|
||||
const theCanvas = page.locator("#the-canvas");
|
||||
const numberOfPages = await page.evaluate(async () => deck.numPages);
|
||||
for (let i = 0; i < numberOfPages; i++) {
|
||||
expect(await theCanvas.screenshot()).toMatchSnapshot({
|
||||
name: `${input}-${i}.png`,
|
||||
});
|
||||
await page.evaluate(() => deck.nextPage());
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
5
test/globals.d.ts
vendored
Normal file
5
test/globals.d.ts
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
interface GlobalDeck {
|
||||
numPages: number;
|
||||
nextPage(): Promise<void>;
|
||||
}
|
||||
declare const deck: GlobalDeck;
|
666
test/input/revealjs-demo/index.html
Normal file
666
test/input/revealjs-demo/index.html
Normal file
@ -0,0 +1,666 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<title>Demo | reveal.js</title>
|
||||
<meta name="description" content="" />
|
||||
<link rel="stylesheet" href="main.css" />
|
||||
<meta itemprop="name" content=" | reveal.js" />
|
||||
<meta itemprop="description" content="" />
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta name="twitter:title" content=" | reveal.js" />
|
||||
<meta name="twitter:description" content="" />
|
||||
<meta name="twitter:site" content="@hakimel" />
|
||||
<meta name="twitter:creator" content="@hakimel" />
|
||||
<meta
|
||||
name="twitter:image:src"
|
||||
content="https://revealjs.com/images/meta/reveal-twitter-card-1024x512.png"
|
||||
/>
|
||||
<meta property="og:title" content=" | reveal.js" />
|
||||
<meta property="og:description" content="" />
|
||||
<meta
|
||||
property="og:image"
|
||||
name="image"
|
||||
content="https://revealjs.com/images/meta/reveal-og-card-1200-630.png"
|
||||
/>
|
||||
<meta property="og:url" content="https://revealjs.com/demo/" />
|
||||
<meta property="og:site_name" content="reveal.js" />
|
||||
<meta property="og:locale" content="en_US" />
|
||||
<meta property="og:type" content="website" />
|
||||
<link rel="icon" type="image/png" href="/images/favicon.svg" />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
</head>
|
||||
<body class="h-screen full-page-demo" data-page="demo">
|
||||
<div class="reveal">
|
||||
<div class="slides">
|
||||
<section>
|
||||
<img
|
||||
src="https://static.slid.es/reveal/logo-v1/reveal-white-text.svg"
|
||||
alt="reveal.js"
|
||||
style="
|
||||
height: 180px;
|
||||
margin: 0 auto 4rem auto;
|
||||
background: transparent;
|
||||
"
|
||||
class="demo-logo"
|
||||
/>
|
||||
<h3>The HTML Presentation Framework</h3>
|
||||
<p>
|
||||
<small
|
||||
>Created by
|
||||
<a href="https://twitter.com/hakimel">Hakim El Hattab</a> and
|
||||
<a href="https://github.com/hakimel/reveal.js/graphs/contributors"
|
||||
>contributors</a
|
||||
></small
|
||||
>
|
||||
</p>
|
||||
<div
|
||||
style="
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 1rem;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
||||
margin-top: 1.5rem;
|
||||
padding-top: 1.5rem;
|
||||
"
|
||||
>
|
||||
<small>Sponsored by</small>
|
||||
<a href="https://codeless.co" rel="dofollow"
|
||||
><img
|
||||
src="https://d1835mevib0k1p.cloudfront.net/reveal-js/sponsors/codeless_light.png"
|
||||
width="227"
|
||||
height="70"
|
||||
/></a>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<h2>Hello There</h2>
|
||||
<p>
|
||||
reveal.js enables you to create beautiful interactive slide decks
|
||||
using HTML. This presentation will show you examples of what it can
|
||||
do.
|
||||
</p>
|
||||
</section>
|
||||
<section>
|
||||
<section>
|
||||
<h2>Vertical Slides</h2>
|
||||
<p>Slides can be nested inside of each other.</p>
|
||||
<p>Use the <em>Space</em> key to navigate through all slides.</p>
|
||||
<br /><a href="#" class="navigate-down"
|
||||
><img
|
||||
class="r-frame"
|
||||
style="background: rgba(255, 255, 255, 0.1)"
|
||||
width="178"
|
||||
height="238"
|
||||
data-src="https://static.slid.es/reveal/arrow.png"
|
||||
alt="Down arrow"
|
||||
/></a>
|
||||
</section>
|
||||
<section>
|
||||
<h2>Basement Level 1</h2>
|
||||
<p>
|
||||
Nested slides are useful for adding additional detail underneath a
|
||||
high level horizontal slide.
|
||||
</p>
|
||||
</section>
|
||||
<section>
|
||||
<h2>Basement Level 2</h2>
|
||||
<p>That's it, time to go back up.</p>
|
||||
<br /><a href="#/2"
|
||||
><img
|
||||
class="r-frame"
|
||||
style="
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
transform: rotate(180deg);
|
||||
"
|
||||
width="178"
|
||||
height="238"
|
||||
data-src="https://static.slid.es/reveal/arrow.png"
|
||||
alt="Up arrow"
|
||||
/></a>
|
||||
</section>
|
||||
</section>
|
||||
<section>
|
||||
<h2>Slides</h2>
|
||||
<p>
|
||||
Not a coder? Not a problem. There's a fully-featured visual editor
|
||||
for authoring these, try it out at
|
||||
<a href="https://slides.com" target="_blank">https://slides.com</a>.
|
||||
</p>
|
||||
</section>
|
||||
<section data-auto-animate>
|
||||
<h2 data-id="code-title">Pretty Code</h2>
|
||||
<pre
|
||||
data-id="code-animation"
|
||||
><code class="hljs" data-trim data-line-numbers>
|
||||
import React, { useState } from 'react';
|
||||
|
||||
function Example() {
|
||||
const [count, setCount] = useState(0);
|
||||
|
||||
return (
|
||||
...
|
||||
);
|
||||
}
|
||||
</code></pre>
|
||||
<p>
|
||||
Code syntax highlighting courtesy of
|
||||
<a href="https://highlightjs.org/usage/">highlight.js</a>.
|
||||
</p>
|
||||
</section>
|
||||
<section data-auto-animate>
|
||||
<h2 data-id="code-title">Even Prettier Animations</h2>
|
||||
<pre
|
||||
data-id="code-animation"
|
||||
><code class="hljs" data-trim data-line-numbers="|4,8-11|17|22-24">
|
||||
import React, { useState } from 'react';
|
||||
|
||||
function Example() {
|
||||
const [count, setCount] = useState(0);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p>You clicked {count} times</p>
|
||||
<button onClick={() => setCount(count + 1)}>
|
||||
Click me
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function SecondExample() {
|
||||
const [count, setCount] = useState(0);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p>You clicked {count} times</p>
|
||||
<button onClick={() => setCount(count + 1)}>
|
||||
Click me
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
</code></pre>
|
||||
</section>
|
||||
<section>
|
||||
<h2>Point of View</h2>
|
||||
<p>Press <strong>ESC</strong> to enter the slide overview.</p>
|
||||
<p>
|
||||
Hold down the <strong>alt</strong> key (<strong>ctrl</strong> in
|
||||
Linux) and click on any element to zoom towards it using
|
||||
<a href="http://lab.hakim.se/zoom-js">zoom.js</a>. Click again to
|
||||
zoom back out.
|
||||
</p>
|
||||
<p>(NOTE: Use ctrl + click in Linux.)</p>
|
||||
</section>
|
||||
<section
|
||||
data-auto-animate
|
||||
data-auto-animate-easing="cubic-bezier(0.770, 0.000, 0.175, 1.000)"
|
||||
>
|
||||
<h2>Auto-Animate</h2>
|
||||
<p>
|
||||
Automatically animate matching elements across slides with
|
||||
<a href="https://revealjs.com/auto-animate/">Auto-Animate</a>.
|
||||
</p>
|
||||
<div class="r-hstack justify-center">
|
||||
<div
|
||||
data-id="box1"
|
||||
style="
|
||||
background: #999;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
margin: 10px;
|
||||
border-radius: 5px;
|
||||
"
|
||||
></div>
|
||||
<div
|
||||
data-id="box2"
|
||||
style="
|
||||
background: #999;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
margin: 10px;
|
||||
border-radius: 5px;
|
||||
"
|
||||
></div>
|
||||
<div
|
||||
data-id="box3"
|
||||
style="
|
||||
background: #999;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
margin: 10px;
|
||||
border-radius: 5px;
|
||||
"
|
||||
></div>
|
||||
</div>
|
||||
</section>
|
||||
<section
|
||||
data-auto-animate
|
||||
data-auto-animate-easing="cubic-bezier(0.770, 0.000, 0.175, 1.000)"
|
||||
>
|
||||
<div class="r-hstack justify-center">
|
||||
<div
|
||||
data-id="box1"
|
||||
data-auto-animate-delay="0"
|
||||
style="
|
||||
background: cyan;
|
||||
width: 150px;
|
||||
height: 100px;
|
||||
margin: 10px;
|
||||
"
|
||||
></div>
|
||||
<div
|
||||
data-id="box2"
|
||||
data-auto-animate-delay="0.1"
|
||||
style="
|
||||
background: magenta;
|
||||
width: 150px;
|
||||
height: 100px;
|
||||
margin: 10px;
|
||||
"
|
||||
></div>
|
||||
<div
|
||||
data-id="box3"
|
||||
data-auto-animate-delay="0.2"
|
||||
style="
|
||||
background: yellow;
|
||||
width: 150px;
|
||||
height: 100px;
|
||||
margin: 10px;
|
||||
"
|
||||
></div>
|
||||
</div>
|
||||
<h2 style="margin-top: 20px">Auto-Animate</h2>
|
||||
</section>
|
||||
<section
|
||||
data-auto-animate
|
||||
data-auto-animate-easing="cubic-bezier(0.770, 0.000, 0.175, 1.000)"
|
||||
>
|
||||
<div class="r-stack">
|
||||
<div
|
||||
data-id="box1"
|
||||
style="
|
||||
background: cyan;
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
border-radius: 200px;
|
||||
"
|
||||
></div>
|
||||
<div
|
||||
data-id="box2"
|
||||
style="
|
||||
background: magenta;
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
border-radius: 200px;
|
||||
"
|
||||
></div>
|
||||
<div
|
||||
data-id="box3"
|
||||
style="
|
||||
background: yellow;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
border-radius: 200px;
|
||||
"
|
||||
></div>
|
||||
</div>
|
||||
<h2 style="margin-top: 20px">Auto-Animate</h2>
|
||||
</section>
|
||||
<section>
|
||||
<h2>Touch Optimized</h2>
|
||||
<p>
|
||||
Presentations look great on touch devices, like mobile phones and
|
||||
tablets. Simply swipe through your slides.
|
||||
</p>
|
||||
</section>
|
||||
<section data-markdown>
|
||||
<script type="text/template">
|
||||
## Markdown support
|
||||
|
||||
Write content using inline or external Markdown.
|
||||
Instructions and more info available in the [readme](https://revealjs.com/markdown/).
|
||||
|
||||
```html []
|
||||
<section data-markdown>
|
||||
## Markdown support
|
||||
|
||||
Write content using inline or external Markdown.
|
||||
Instructions and more info available in the [readme](https://revealjs.com/markdown/).
|
||||
</section>
|
||||
```
|
||||
</script>
|
||||
</section>
|
||||
<section>
|
||||
<p>Add the <code>r-fit-text</code> class to auto-size text</p>
|
||||
<h2 class="r-fit-text">FIT TEXT</h2>
|
||||
</section>
|
||||
<section>
|
||||
<section id="fragments">
|
||||
<h2>Fragments</h2>
|
||||
<p>Hit the next arrow...</p>
|
||||
<p class="fragment">... to step through ...</p>
|
||||
<p>
|
||||
<span class="fragment">... a</span>
|
||||
<span class="fragment">fragmented</span>
|
||||
<span class="fragment">slide.</span>
|
||||
</p>
|
||||
<aside class="notes">
|
||||
This slide has fragments which are also stepped through in the
|
||||
notes window.
|
||||
</aside>
|
||||
</section>
|
||||
<section>
|
||||
<h2>Fragment Styles</h2>
|
||||
<p>There's different types of fragments, like:</p>
|
||||
<p class="fragment grow">grow</p>
|
||||
<p class="fragment shrink">shrink</p>
|
||||
<p class="fragment fade-out">fade-out</p>
|
||||
<p>
|
||||
<span style="display: inline-block" class="fragment fade-right"
|
||||
>fade-right, </span
|
||||
><span style="display: inline-block" class="fragment fade-up"
|
||||
>up, </span
|
||||
><span style="display: inline-block" class="fragment fade-down"
|
||||
>down, </span
|
||||
><span style="display: inline-block" class="fragment fade-left"
|
||||
>left</span
|
||||
>
|
||||
</p>
|
||||
<p class="fragment fade-in-then-out">fade-in-then-out</p>
|
||||
<p class="fragment fade-in-then-semi-out">fade-in-then-semi-out</p>
|
||||
<p>
|
||||
Highlight <span class="fragment highlight-red">red</span>
|
||||
<span class="fragment highlight-blue">blue</span>
|
||||
<span class="fragment highlight-green">green</span>
|
||||
</p>
|
||||
</section>
|
||||
</section>
|
||||
<section id="transitions">
|
||||
<h2>Transition Styles</h2>
|
||||
<p>
|
||||
You can select from different transitions, like:<br /><a
|
||||
href="?transition=none#/transitions"
|
||||
>None</a
|
||||
>
|
||||
- <a href="?transition=fade#/transitions">Fade</a> -
|
||||
<a href="?transition=slide#/transitions">Slide</a> -
|
||||
<a href="?transition=convex#/transitions">Convex</a> -
|
||||
<a href="?transition=concave#/transitions">Concave</a> -
|
||||
<a href="?transition=zoom#/transitions">Zoom</a>
|
||||
</p>
|
||||
</section>
|
||||
<section>
|
||||
<section data-background="#dddddd">
|
||||
<h2>Slide Backgrounds</h2>
|
||||
<p>
|
||||
Set <code>data-background="#dddddd"</code> on a slide to change
|
||||
the background color. All CSS color formats are supported.
|
||||
</p>
|
||||
<a href="#" class="navigate-down"
|
||||
><img
|
||||
class="r-frame"
|
||||
style="background: rgba(255, 255, 255, 0.1)"
|
||||
width="178"
|
||||
height="238"
|
||||
data-src="https://static.slid.es/reveal/arrow.png"
|
||||
alt="Down arrow"
|
||||
/></a>
|
||||
</section>
|
||||
<section
|
||||
data-background="https://static.slid.es/reveal/image-placeholder.png"
|
||||
>
|
||||
<h2>Image Backgrounds</h2>
|
||||
<pre><code class="hljs html"><section data-background="image.png"></code></pre>
|
||||
</section>
|
||||
<section
|
||||
data-background="https://static.slid.es/reveal/image-placeholder.png"
|
||||
data-background-repeat="repeat"
|
||||
data-background-size="100px"
|
||||
>
|
||||
<h2>Tiled Backgrounds</h2>
|
||||
<pre><code class="hljs html" style="word-wrap: break-word;"><section data-background="image.png" data-background-repeat="repeat" data-background-size="100px"></code></pre>
|
||||
</section>
|
||||
<section
|
||||
data-background-video="https://s3.amazonaws.com/static.slid.es/site/homepage/v1/homepage-video-editor.mp4"
|
||||
data-background-color="#000000"
|
||||
>
|
||||
<div
|
||||
style="
|
||||
background-color: rgba(0, 0, 0, 0.9);
|
||||
color: #fff;
|
||||
padding: 20px;
|
||||
"
|
||||
>
|
||||
<h2>Video Backgrounds</h2>
|
||||
<pre><code class="hljs html" style="word-wrap: break-word;"><section data-background-video="video.mp4,video.webm"></code></pre>
|
||||
</div>
|
||||
</section>
|
||||
<section data-background="http://i.giphy.com/90F8aUepslB84.gif">
|
||||
<h2>... and GIFs!</h2>
|
||||
</section>
|
||||
</section>
|
||||
<section
|
||||
data-transition="slide"
|
||||
data-background="#4d7e65"
|
||||
data-background-transition="zoom"
|
||||
>
|
||||
<h2>Background Transitions</h2>
|
||||
<p>
|
||||
Different background transitions are available via the
|
||||
backgroundTransition option. This one's called "zoom".
|
||||
</p>
|
||||
<pre><code class="hljs javascript">Reveal.configure({ backgroundTransition: 'zoom' })</code></pre>
|
||||
</section>
|
||||
<section
|
||||
data-transition="slide"
|
||||
data-background="#b5533c"
|
||||
data-background-transition="zoom"
|
||||
>
|
||||
<h2>Background Transitions</h2>
|
||||
<p>You can override background transitions per-slide.</p>
|
||||
<pre><code class="hljs html" style="word-wrap: break-word;"><section data-background-transition="zoom"></code></pre>
|
||||
</section>
|
||||
<section
|
||||
data-background-iframe="https://hakim.se"
|
||||
data-background-interactive
|
||||
>
|
||||
<div
|
||||
style="
|
||||
position: absolute;
|
||||
width: 40%;
|
||||
right: 0;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.5),
|
||||
0 5px 25px rgba(0, 0, 0, 0.2);
|
||||
background-color: rgba(0, 0, 0, 0.9);
|
||||
color: #fff;
|
||||
padding: 20px;
|
||||
font-size: 20px;
|
||||
text-align: left;
|
||||
"
|
||||
>
|
||||
<h2>Iframe Backgrounds</h2>
|
||||
<p>
|
||||
Since reveal.js runs on the web, you can easily embed other web
|
||||
content. Try interacting with the page in the background.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<h2>Marvelous List</h2>
|
||||
<ul>
|
||||
<li>No order here</li>
|
||||
<li>Or here</li>
|
||||
<li>Or here</li>
|
||||
<li>Or here</li>
|
||||
</ul>
|
||||
</section>
|
||||
<section>
|
||||
<h2>Fantastic Ordered List</h2>
|
||||
<ol>
|
||||
<li>One is smaller than...</li>
|
||||
<li>Two is smaller than...</li>
|
||||
<li>Three!</li>
|
||||
</ol>
|
||||
</section>
|
||||
<section>
|
||||
<h2>Tabular Tables</h2>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Item</th>
|
||||
<th>Value</th>
|
||||
<th>Quantity</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Apples</td>
|
||||
<td>$1</td>
|
||||
<td>7</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Lemonade</td>
|
||||
<td>$2</td>
|
||||
<td>18</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Bread</td>
|
||||
<td>$3</td>
|
||||
<td>2</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
<section>
|
||||
<h2>Clever Quotes</h2>
|
||||
<p>
|
||||
These guys come in two forms, inline:
|
||||
<q
|
||||
cite="http://searchservervirtualization.techtarget.com/definition/Our-Favorite-Technology-Quotations"
|
||||
>The nice thing about standards is that there are so many to
|
||||
choose from</q
|
||||
>
|
||||
and block:
|
||||
</p>
|
||||
<blockquote
|
||||
cite="http://searchservervirtualization.techtarget.com/definition/Our-Favorite-Technology-Quotations"
|
||||
>
|
||||
“For years there has been a theory that millions of monkeys
|
||||
typing at random on millions of typewriters would reproduce the
|
||||
entire works of Shakespeare. The Internet has proven this theory to
|
||||
be untrue.”
|
||||
</blockquote>
|
||||
</section>
|
||||
<section>
|
||||
<h2>Intergalactic Interconnections</h2>
|
||||
<p>
|
||||
You can link between slides internally,
|
||||
<a href="#/2/3">like this</a>.
|
||||
</p>
|
||||
</section>
|
||||
<section>
|
||||
<h2>Speaker View</h2>
|
||||
<p>
|
||||
There's a
|
||||
<a href="https://revealjs.com/speaker-view/">speaker view</a>. It
|
||||
includes a timer, preview of the upcoming slide as well as your
|
||||
speaker notes.
|
||||
</p>
|
||||
<p>Press the <em>S</em> key to try it out.</p>
|
||||
<aside class="notes">
|
||||
Oh hey, these are some notes. They'll be hidden in your
|
||||
presentation, but you can see them if you open the speaker notes
|
||||
window (hit 's' on your keyboard).
|
||||
</aside>
|
||||
</section>
|
||||
<section>
|
||||
<h2>Export to PDF</h2>
|
||||
<p>
|
||||
Presentations can be
|
||||
<a href="https://revealjs.com/pdf-export/">exported to PDF</a>,
|
||||
here's an example:
|
||||
</p>
|
||||
<iframe
|
||||
data-src="https://www.slideshare.net/slideshow/embed_code/42840540"
|
||||
width="445"
|
||||
height="355"
|
||||
frameborder="0"
|
||||
marginwidth="0"
|
||||
marginheight="0"
|
||||
scrolling="no"
|
||||
style="border: 3px solid #666; margin-bottom: 5px; max-width: 100%"
|
||||
allowfullscreen
|
||||
></iframe>
|
||||
</section>
|
||||
<section>
|
||||
<h2>Global State</h2>
|
||||
<p>
|
||||
Set <code>data-state="something"</code> on a slide and
|
||||
<code>"something"</code> will be added as a class to the document
|
||||
element when the slide is open. This lets you apply broader style
|
||||
changes, like switching the page background.
|
||||
</p>
|
||||
</section>
|
||||
<section data-state="customevent">
|
||||
<h2>State Events</h2>
|
||||
<p>
|
||||
Additionally custom events can be triggered on a per slide basis by
|
||||
binding to the <code>data-state</code> name.
|
||||
</p>
|
||||
<pre><code class="javascript" data-trim contenteditable style="font-size: 18px;">
|
||||
Reveal.on( 'customevent', function() {
|
||||
console.log( '"customevent" has fired' );
|
||||
} );
|
||||
</code></pre>
|
||||
</section>
|
||||
<section>
|
||||
<h2>Take a Moment</h2>
|
||||
<p>
|
||||
Press B or . on your keyboard to pause the presentation. This is
|
||||
helpful when you're on stage and want to take distracting slides off
|
||||
the screen.
|
||||
</p>
|
||||
</section>
|
||||
<section>
|
||||
<h2>Much more</h2>
|
||||
<ul>
|
||||
<li>Right-to-left support</li>
|
||||
<li>
|
||||
<a href="https://revealjs.com/api/">Extensive JavaScript API</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://revealjs.com/auto-slide/">Auto-progression</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://revealjs.com/backgrounds/#parallax-background"
|
||||
>Parallax backgrounds</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://revealjs.com/keyboard/"
|
||||
>Custom keyboard bindings</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
<section style="text-align: left">
|
||||
<h1>THE END</h1>
|
||||
<p>
|
||||
- <a href="https://slides.com">Try the online editor</a><br />-
|
||||
<a href="https://github.com/hakimel/reveal.js"
|
||||
>Source code & documentation</a
|
||||
>
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
<script src="main.js" defer="defer"></script>
|
||||
</body>
|
||||
</html>
|
202154
test/input/revealjs-demo/main.css
Normal file
202154
test/input/revealjs-demo/main.css
Normal file
File diff suppressed because it is too large
Load Diff
630
test/input/revealjs-demo/main.js
Normal file
630
test/input/revealjs-demo/main.js
Normal file
File diff suppressed because one or more lines are too long
46
test/run-decktape.js
Normal file
46
test/run-decktape.js
Normal file
@ -0,0 +1,46 @@
|
||||
import childProcess from "child_process";
|
||||
import fs from "fs";
|
||||
import { fileURLToPath } from "url";
|
||||
import path from "path";
|
||||
import { server } from "./server.js";
|
||||
|
||||
const decktapeExecutable = fileURLToPath(
|
||||
new URL("../decktape.js", import.meta.url)
|
||||
);
|
||||
const outputDirectory = fileURLToPath(new URL("./output", import.meta.url));
|
||||
const inputDirectories = fs.readdirSync(new URL("./input", import.meta.url));
|
||||
|
||||
const [, mainFileName] = process.argv;
|
||||
if (mainFileName === fileURLToPath(import.meta.url)) {
|
||||
try {
|
||||
const port = server.listen();
|
||||
await Promise.all(
|
||||
inputDirectories.map(async (inputDir) => {
|
||||
await runDecktape(port, inputDir);
|
||||
})
|
||||
);
|
||||
} finally {
|
||||
await server.close();
|
||||
}
|
||||
}
|
||||
|
||||
function runDecktape(port, inputDir) {
|
||||
return new Promise((res, rej) => {
|
||||
console.log(`Generating ${inputDir}`);
|
||||
const outputFile = path.resolve(outputDirectory, `${inputDir}.pdf`);
|
||||
const process = childProcess.exec(
|
||||
`node ${decktapeExecutable} http://localhost:${port}/input/${inputDir}/ ${outputFile}`,
|
||||
{ stdio: "inherit" },
|
||||
(err) => {
|
||||
if (err) {
|
||||
rej(err);
|
||||
} else {
|
||||
console.log(`✅ ${inputDir}.pdf`);
|
||||
res();
|
||||
}
|
||||
}
|
||||
);
|
||||
process.stdout.on('data', data => console.log(data.toString()));
|
||||
process.stderr.on('data', data => console.error(data.toString()));
|
||||
});
|
||||
}
|
17
test/run-server.js
Normal file
17
test/run-server.js
Normal file
@ -0,0 +1,17 @@
|
||||
import { server } from "./server.js";
|
||||
import { fileURLToPath } from "url";
|
||||
import path from "path";
|
||||
|
||||
const [, mainFileName, port] = process.argv;
|
||||
if (mainFileName === fileURLToPath(import.meta.url)) {
|
||||
if (!port) {
|
||||
const fileName = path.basename(fileURLToPath(import.meta.url));
|
||||
console.error(`
|
||||
Error: Missing port number to run on
|
||||
Usage: node ${fileName} <port>
|
||||
|
||||
Example: node ${fileName} 3000`);
|
||||
} else {
|
||||
server.listen(parseInt(port));
|
||||
}
|
||||
}
|
28
test/server.js
Normal file
28
test/server.js
Normal file
@ -0,0 +1,28 @@
|
||||
import Koa from "koa";
|
||||
import serve from "koa-static";
|
||||
import { fileURLToPath } from "url";
|
||||
import { promisify } from "util";
|
||||
import path from "path";
|
||||
|
||||
const app = new Koa();
|
||||
|
||||
const dir = path.dirname(fileURLToPath(import.meta.url));
|
||||
app.use(serve(dir));
|
||||
|
||||
export const server = {
|
||||
listen(portHint = undefined) {
|
||||
this.innerServer = app.listen(portHint);
|
||||
const { port } = this.innerServer.address();
|
||||
console.log(
|
||||
`listening from http://localhost:${port}/ (serving static files from ${dir})`
|
||||
);
|
||||
return port;
|
||||
},
|
||||
|
||||
async close() {
|
||||
if (this.innerServer && this.innerServer.close) {
|
||||
const closeAsync = promisify(this.innerServer.close).bind(this.innerServer);
|
||||
await closeAsync();
|
||||
}
|
||||
},
|
||||
};
|
50
test/show-pdf.html
Normal file
50
test/show-pdf.html
Normal file
@ -0,0 +1,50 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Document</title>
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="the-canvas"></canvas>
|
||||
<script src="https://cdn.jsdelivr.net/npm/pdfjs-dist@3.1.81/build/pdf.min.js"></script>
|
||||
<script type="module">
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const pdf = await pdfjsLib.getDocument(`./output/${params.get('file')}.pdf`).promise;
|
||||
let currentPage = 1;
|
||||
await renderPage();
|
||||
|
||||
window.deck = {
|
||||
nextPage: async () => {
|
||||
if (currentPage < pdf.numPages) {
|
||||
currentPage++;
|
||||
await renderPage();
|
||||
}
|
||||
},
|
||||
numPages: pdf.numPages,
|
||||
};
|
||||
async function renderPage() {
|
||||
const page = await pdf.getPage(currentPage);
|
||||
const scale = 1.5;
|
||||
const viewport = page.getViewport({ scale: scale });
|
||||
// Support HiDPI-screens.
|
||||
const outputScale = window.devicePixelRatio || 1;
|
||||
|
||||
const canvas = document.getElementById("the-canvas");
|
||||
const canvasContext = canvas.getContext("2d");
|
||||
canvas.width = Math.floor(viewport.width * outputScale);
|
||||
canvas.height = Math.floor(viewport.height * outputScale);
|
||||
canvas.style.width = Math.floor(viewport.width) + "px";
|
||||
canvas.style.height = Math.floor(viewport.height) + "px";
|
||||
const transform =
|
||||
outputScale !== 1
|
||||
? [outputScale, 0, 0, outputScale, 0, 0]
|
||||
: null;
|
||||
|
||||
await page.render({ canvasContext, transform, viewport })
|
||||
.promise;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user