Skip to content

Commit 70ef42c

Browse files
marioneblgaearon
authored andcommitted
Add screencast task (facebook#3816)
* Automate screencast recordings * **screencast.js**: Automate screencast.sh, asciinema, svg-term-cli. Removes progress-bar, npm tree data from cast * **screencast.sh**: Simulate user input, trigger demoed commands * **screencast-start.js**: Start a shell command and end the process log patterns have been observed
1 parent 72c3d4e commit 70ef42c

File tree

4 files changed

+173
-2
lines changed

4 files changed

+173
-2
lines changed

package.json

+6-2
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,23 @@
1212
"postinstall": "cd packages/react-error-overlay/ && yarn build:prod",
1313
"publish": "tasks/publish.sh",
1414
"start": "cd packages/react-scripts && node bin/react-scripts.js start",
15-
"screencast": "svg-term --cast hItN7sl5yfCPTHxvFg5glhhfp --out screencast.svg --window",
15+
"screencast": "node ./tasks/screencast.js",
1616
"test": "cd packages/react-scripts && node bin/react-scripts.js test --env=jsdom",
1717
"format": "prettier --trailing-comma es5 --single-quote --write 'packages/*/*.js' 'packages/*/!(node_modules)/**/*.js'",
1818
"precommit": "lint-staged"
1919
},
2020
"devDependencies": {
2121
"eslint": "4.15.0",
22+
"execa": "^0.9.0",
2223
"husky": "^0.13.2",
2324
"lerna": "2.6.0",
2425
"lerna-changelog": "^0.6.0",
2526
"lint-staged": "^3.3.1",
27+
"meow": "^4.0.0",
28+
"multimatch": "^2.1.0",
2629
"prettier": "1.6.1",
27-
"svg-term-cli": "^2.0.3"
30+
"svg-term-cli": "^2.0.3",
31+
"tempy": "^0.2.1"
2832
},
2933
"lint-staged": {
3034
"*.js": [

tasks/screencast-start.js

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
#!/usr/bin/env node
2+
3+
/**
4+
* Copyright (c) 2015-present, Facebook, Inc.
5+
*
6+
* This source code is licensed under the MIT license found in the
7+
* LICENSE file in the root directory of this source tree.
8+
*/
9+
10+
'use strict';
11+
12+
const execa = require('execa');
13+
const meow = require('meow');
14+
const multimatch = require('multimatch');
15+
16+
main(meow());
17+
18+
function main(cli) {
19+
let count = 0;
20+
21+
const start = Date.now();
22+
const duration = parseInt(cli.flags.timeout, 10) * 1000;
23+
const cp = execa.shell(cli.flags.command);
24+
25+
const target = parseInt(cli.flags.patternCount || '1', 10);
26+
27+
cp.stdout.on('data', data => {
28+
process.stdout.write(data);
29+
const matches = multimatch([String(data)], cli.flags.pattern);
30+
const errMatches = multimatch([String(data)], cli.flags.errorPattern);
31+
32+
if (matches.length > 0) {
33+
count++;
34+
}
35+
36+
if (errMatches.length > 0) {
37+
process.exit(1);
38+
}
39+
40+
if (count >= target) {
41+
setTimeout(() => {
42+
process.exit(0);
43+
}, duration);
44+
}
45+
});
46+
47+
cp.on('exit', e => {
48+
const elapsed = Date.now() - start;
49+
50+
if (elapsed >= duration) {
51+
return;
52+
}
53+
54+
setTimeout(() => {
55+
process.exit(e.code);
56+
}, duration - elapsed);
57+
});
58+
}

tasks/screencast.js

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#!/usr/bin/env node
2+
3+
/**
4+
* Copyright (c) 2015-present, Facebook, Inc.
5+
*
6+
* This source code is licensed under the MIT license found in the
7+
* LICENSE file in the root directory of this source tree.
8+
*/
9+
10+
'use strict';
11+
12+
const fs = require('fs');
13+
const path = require('path');
14+
const execa = require('execa');
15+
const tempy = require('tempy');
16+
17+
main();
18+
19+
function main() {
20+
const previous = process.cwd();
21+
const cwd = tempy.directory();
22+
23+
const cast = path.join(cwd, 'screencast.json');
24+
const script = path.join(__dirname, 'screencast.sh');
25+
const out = path.join(previous, 'screencast.svg');
26+
27+
const resolveLine = l => l.indexOf('🔍 Resolving packages...') > -1;
28+
const fetchLine = l => l.indexOf('🚚 Fetching packages...') > -1;
29+
const countLine = l => l.match(/Saved [0-9]+ new dependencies/);
30+
const doneLine = l => l.indexOf('✨ Done in') > -1;
31+
32+
try {
33+
process.chdir(cwd);
34+
console.log(`Recording screencast ...`);
35+
execa.sync('asciinema', ['rec', '--command', `sh ${script}`, cast], {
36+
cwd,
37+
stdio: 'inherit',
38+
});
39+
40+
console.log('Cleaning data ...');
41+
const data = require(cast);
42+
43+
cut(data.stdout, { start: resolveLine, end: fetchLine });
44+
cut(data.stdout, { start: countLine, end: doneLine });
45+
replace(data.stdout, [{ in: cwd, out: '~' }]);
46+
47+
fs.writeFileSync(cast, JSON.stringify(data, null, ' '));
48+
49+
console.log('Rendering SVG ...');
50+
execa.sync('svg-term', ['--window', '--in', cast, '--out', out]);
51+
52+
console.log(`Recorded screencast to ${cast}`);
53+
console.log(`Rendered SVG to ${out}`);
54+
} catch (err) {
55+
throw err;
56+
} finally {
57+
process.chdir(previous);
58+
}
59+
}
60+
61+
function cut(frames, { start, end }) {
62+
const si = frames.findIndex(([, l]) => start(l));
63+
const ei = frames.findIndex(([, l]) => end(l));
64+
65+
if (si === -1 || ei === -1) {
66+
return;
67+
}
68+
69+
frames.splice(si + 1, ei - si - 1);
70+
}
71+
72+
function replace(frames, replacements) {
73+
frames.forEach(frame => {
74+
replacements.forEach(r => (frame[1] = frame[1].split(r.in).join(r.out)));
75+
});
76+
}

tasks/screencast.sh

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#!/bin/zsh
2+
# Copyright (c) 2015-present, Facebook, Inc.
3+
#
4+
# This source code is licensed under the MIT license found in the
5+
# LICENSE file in the root directory of this source tree.
6+
7+
# ******************************************************************************
8+
# This is an end-to-end test intended to be run via screencast.js
9+
# Dependencies: asciinema, pv, core-utils
10+
# ******************************************************************************
11+
set -e
12+
13+
printf '\e[32m%s\e[m' "λ "
14+
echo "npx create-react-app my-app" | pv -qL $[10+(-2 + RANDOM%5)]
15+
npx create-react-app my-app
16+
17+
printf '\e[32m%s\e[m' "λ "
18+
sleep 1
19+
echo "cd my-app" | pv -qL $[10+(-2 + RANDOM%5)]
20+
cd my-app
21+
22+
printf '\e[32m%s\e[m' "λ "
23+
sleep 1
24+
echo "npm start" | pv -qL $[10+(-2 + RANDOM%5)]
25+
26+
BROWSER="none" node "$(dirname $0)/screencast-start.js" \
27+
--command "npm start" \
28+
--pattern="Compiled successfully*" \
29+
--pattern-count 2 \
30+
--error-pattern="*already running on port" \
31+
--timeout 10
32+
33+
echo ""

0 commit comments

Comments
 (0)