Skip to content

Commit fc1e9fb

Browse files
authored
Merge pull request #51 from mkantor/examples-with-snapshots
Add some example programs
2 parents 2cf8321 + a4c80cd commit fc1e9fb

File tree

7 files changed

+244
-1
lines changed

7 files changed

+244
-1
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ npm run build
1212
echo '{@runtime context => :context.program.start_time}' | ./please --output-format=json
1313
```
1414

15+
There are more example programs in [`./examples`](./examples).
16+
1517
## What This Repository Is
1618

1719
**This implementation of Please is a proof of concept**. There are bugs and

examples/.snapshot

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
exports[`examples > fibonacci.plz --input="not a number" > stderr 1`] = `
2+
3+
`;
4+
5+
exports[`examples > fibonacci.plz --input="not a number" > stdout 1`] = `
6+
"input must be a natural number"
7+
8+
`;
9+
10+
exports[`examples > fibonacci.plz --input=-1 > stderr 1`] = `
11+
12+
`;
13+
14+
exports[`examples > fibonacci.plz --input=-1 > stdout 1`] = `
15+
"input must be a natural number"
16+
17+
`;
18+
19+
exports[`examples > fibonacci.plz --input=0 > stderr 1`] = `
20+
21+
`;
22+
23+
exports[`examples > fibonacci.plz --input=0 > stdout 1`] = `
24+
0
25+
26+
`;
27+
28+
exports[`examples > fibonacci.plz --input=1 > stderr 1`] = `
29+
30+
`;
31+
32+
exports[`examples > fibonacci.plz --input=1 > stdout 1`] = `
33+
1
34+
35+
`;
36+
37+
exports[`examples > fibonacci.plz --input=10 > stderr 1`] = `
38+
39+
`;
40+
41+
exports[`examples > fibonacci.plz --input=10 > stdout 1`] = `
42+
55
43+
44+
`;
45+
46+
exports[`examples > fibonacci.plz --input=2 > stderr 1`] = `
47+
48+
`;
49+
50+
exports[`examples > fibonacci.plz --input=2 > stdout 1`] = `
51+
1
52+
53+
`;
54+
55+
exports[`examples > fibonacci.plz > stderr 1`] = `
56+
57+
`;
58+
59+
exports[`examples > fibonacci.plz > stdout 1`] = `
60+
"missing input argument"
61+
62+
`;
63+
64+
exports[`examples > kitchen-sink.plz > stderr 1`] = `
65+
"this goes to stderr"
66+
67+
`;
68+
69+
exports[`examples > kitchen-sink.plz > stdout 1`] = `
70+
{
71+
foo: bar
72+
bar: bar
73+
sky_is_blue: true
74+
colors: {
75+
red
76+
green
77+
blue
78+
}
79+
two: 2
80+
add_one: :integer.add(1)
81+
three: 3
82+
function: x => {
83+
value: :x
84+
}
85+
conditional_value: {
86+
value: 2
87+
}
88+
side_effect: "this goes to stderr"
89+
}
90+
91+
`;
92+
93+
exports[`examples > lookup-environment-variable.plz > stderr 1`] = `
94+
95+
`;
96+
97+
exports[`examples > lookup-environment-variable.plz > stdout 1`] = `
98+
{}
99+
100+
`;

examples/fibonacci.plz

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
fibonacci: n => {
3+
@if :integer.less_than(2)(:n)
4+
then: :n
5+
else: :integer.add(
6+
:fibonacci(:integer.subtract(2)(:n))
7+
)(
8+
:fibonacci(:integer.subtract(1)(:n))
9+
)
10+
}
11+
12+
input: { @runtime context => :context.arguments.lookup(input) }
13+
14+
output: :apply(:input)(
15+
:match({
16+
none: _ => "missing input argument"
17+
some: input => {
18+
@if :natural_number.is(:input)
19+
then: :fibonacci(:input)
20+
else: "input must be a natural number"
21+
}
22+
})
23+
)
24+
}.output

examples/kitchen-sink.plz

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
// a comment
3+
foo: bar
4+
bar: :foo
5+
sky_is_blue: :boolean.not(false)
6+
colors: { red green blue }
7+
two: :integer.add(1)(1)
8+
add_one: :integer.add(1)
9+
three: :add_one(:two)
10+
function: x => { value: :x }
11+
conditional_value: :function({ @if :sky_is_blue :two :three })
12+
side_effect: { @runtime context => :context.log("this goes to stderr") }
13+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{@runtime context =>
2+
:flow({
3+
:context.arguments.lookup
4+
:match({
5+
none: {}
6+
some: :flow({
7+
:context.environment.lookup
8+
:match({
9+
none: {}
10+
some: :identity
11+
})
12+
})
13+
})
14+
})(variable)
15+
}

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
"build": "tsc --build tsconfig.app.json --force",
1313
"build:tests": "tsc --project tsconfig.app.json --outDir dist-test --declarationDir dist && tsc --build tsconfig.test.json",
1414
"clean": "rm -rf dist* *.tsbuildinfo",
15-
"test": "npm run build:tests && node --test"
15+
"test": "npm run build:tests && node --test --experimental-test-snapshots",
16+
"test-update-snapshots": "npm run build:tests && node --test --test-update-snapshots --experimental-test-snapshots"
1617
},
1718
"devDependencies": {
1819
"@types/node": "^22.5.5",

src/examples.test.ts

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import { exec } from 'node:child_process'
2+
import { promises as fs } from 'node:fs'
3+
import path from 'node:path'
4+
import test, { snapshot, suite } from 'node:test'
5+
6+
// All examples will be tested with no additional command-line arguments. To
7+
// also test with specific sets of arguments, add them here:
8+
const additionalCommandLineArguments: Readonly<
9+
Record<string, readonly string[]>
10+
> = {
11+
'fibonacci.plz': [
12+
'--input=0',
13+
'--input=1',
14+
'--input=2',
15+
'--input=10',
16+
'--input="not a number"',
17+
'--input=-1',
18+
],
19+
}
20+
21+
snapshot.setResolveSnapshotPath(_ =>
22+
path.join(import.meta.dirname, '..', 'examples', '.snapshot'),
23+
)
24+
25+
const exampleDirectoryPath = path.join(import.meta.dirname, '..', 'examples')
26+
const pleasePath = path.join(
27+
import.meta.dirname,
28+
'language',
29+
'cli',
30+
'please.js',
31+
)
32+
33+
suite('examples', async () => {
34+
const exampleFileNames = (await fs.readdir(exampleDirectoryPath)).filter(
35+
fileName => fileName.endsWith('.plz'),
36+
)
37+
for (const exampleFileName of exampleFileNames) {
38+
const setsOfCommandLineArguments = [
39+
'', // Always test with no arguments.
40+
...(additionalCommandLineArguments[exampleFileName] ?? []),
41+
] as const
42+
43+
for (const commandLineArguments of setsOfCommandLineArguments) {
44+
const exampleFilePath = path.join(exampleDirectoryPath, exampleFileName)
45+
test(
46+
exampleFileName.concat(
47+
commandLineArguments === '' ? '' : ` ${commandLineArguments}`,
48+
),
49+
_ =>
50+
new Promise((resolve, reject) => {
51+
const _childProcess = exec(
52+
`cat "${exampleFilePath}" | node "${pleasePath}" --no-color --output-format=plz ${commandLineArguments}`,
53+
(error, stdout, stderr) => {
54+
// `error` is an `ExecException` when exit status is nonzero.
55+
if (error !== null) {
56+
reject(error)
57+
} else {
58+
Promise.all([
59+
test('stdout', t =>
60+
t.assert.snapshot(stdout, snapshotOptions)),
61+
test('stderr', t =>
62+
t.assert.snapshot(stderr, snapshotOptions)),
63+
])
64+
.then(_ => resolve(undefined))
65+
.catch(reject)
66+
}
67+
},
68+
)
69+
}),
70+
)
71+
}
72+
}
73+
})
74+
75+
// Expect snapshots to already be strings.
76+
const snapshotOptions = {
77+
serializers: [
78+
(value: unknown) => {
79+
if (typeof value !== 'string') {
80+
throw new Error(
81+
`snapshot was not a string (was \`${JSON.stringify(value)}\`)`,
82+
)
83+
} else {
84+
return value
85+
}
86+
},
87+
],
88+
}

0 commit comments

Comments
 (0)