Skip to content

Commit 7dcfd15

Browse files
Update interface and use @exercism/static-analysis (#31)
- Add `test_code` when available - Fix `output` when there is none (previously `""`, now `null`)
1 parent 4d19dbf commit 7dcfd15

File tree

15 files changed

+341
-68
lines changed

15 files changed

+341
-68
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Changelog
22

3+
## 2.2.0
4+
5+
- Add `test_code` when available
6+
- Fix `output` when there is none (previously `""`, now `null`)
7+
38
## 2.1.0
49

510
- Add `output` per test (user `console.log`)

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ You'll want these `:dev` variants because it will _build_ the required code (it
2929

3030
You can also manually build using `yarn` or `yarn build`, and then run the script directly: `./bin/run.sh arg1 arg2 arg3`.
3131

32-
## Running the Tests
32+
## Running the Solution's Tests
3333

3434
To run a solution's tests, do the following:
3535

@@ -80,7 +80,7 @@ Find the output at:
8080

8181
As you can see, it will be copied to a local directory. It's up to you to clean-up this directory.
8282

83-
## Running the Tests in Docker container
83+
## Running the Solution's Tests in Docker container
8484

8585
_This script is provided for testing purposes_
8686

@@ -93,9 +93,9 @@ To run a solution's test in the Docker container, do the following:
9393

9494
The `package.json` needs to be in-sync with the [`javascript` track `package.json`][git-javascript].
9595

96-
### Known issues
96+
### Testing
9797

98-
- The output format of the tests still does not conform to the [exercism automated tests][git-automated-tests] standard.
98+
Running the tests of the test-runner itself can be achieved by using the `test` script from `package.json`. The tests delegate to the _build output_, which is why `yarn test` first calls `yarn build` before running `jest`. **The tests take over a minute to run on a decent machine**.
9999

100100
[web-exercism]: https://exercism.io
101101
[git-automated-tests]: https://github.com/exercism/automated-tests

bin/run.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,11 @@ else
6868
fi
6969

7070
# Forces a trailing slash
71+
INPUT="${INPUT//\\//}"
7172
INPUT="${INPUT%/}/"
7273

7374
# Forces a trailing slash
75+
OUTPUT="${OUTPUT//\\//}"
7476
OUTPUT="${OUTPUT%/}/"
7577

7678
set -euo pipefail

package.json

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "@exercism/javascript-test-runner",
33
"description": "Automated Test runner for exercism solutions in Javascript.",
44
"author": "Derk-Jan Karrenbeld <derk-jan+github@karrenbeld.info>",
5-
"version": "2.0.0",
5+
"version": "2.2.0",
66
"license": "AGPL-3.0-or-later",
77
"repository": {
88
"type": "git",
@@ -22,7 +22,8 @@
2222
"prebuild": "rimraf ./dist",
2323
"build": "yarn tsc --project ./src/tsconfig.json --outDir ./dist",
2424
"watch": "yarn build -w",
25-
"prepublish": "yarn test",
25+
"prepare": "yarn build",
26+
"prepublishOnly": "yarn test:bare && yarn lint",
2627
"lint": "yarn eslint . --ext ts,js,tsx,jsx,mjs -c .eslintrc",
2728
"test": "yarn build && yarn test:bare",
2829
"test:bare": "jest --roots test --testPathIgnorePatterns=\"fixtures/\""
@@ -33,6 +34,8 @@
3334
"@babel/node": "^7.12.10",
3435
"@babel/preset-env": "^7.12.11",
3536
"@babel/preset-typescript": "^7.12.7",
37+
"@exercism/static-analysis": "^0.4.1",
38+
"@typescript-eslint/typescript-estree": "^4.11.1",
3639
"babel-jest": "^26.6.3",
3740
"chalk": "^4.1.0",
3841
"jest": "^26.6.3",
@@ -43,12 +46,11 @@
4346
},
4447
"devDependencies": {
4548
"@types/jest": "^26.0.19",
46-
"@types/node": "^14.14.16",
47-
"@typescript-eslint/eslint-plugin": "^4.11.0",
48-
"@typescript-eslint/parser": "^4.11.0",
49-
"@typescript-eslint/typescript-estree": "^4.11.0",
49+
"@types/node": "^14.14.19",
50+
"@typescript-eslint/eslint-plugin": "^4.11.1",
51+
"@typescript-eslint/parser": "^4.11.1",
5052
"babel-eslint": "^10.1.0",
51-
"eslint": "^7.16.0",
53+
"eslint": "^7.17.0",
5254
"eslint-config-prettier": "^7.1.0",
5355
"eslint-plugin-import": "^2.22.1",
5456
"eslint-plugin-jest": "^24.1.3",

src/output.ts

Lines changed: 68 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,21 @@
1-
import path from 'path'
2-
import fs from 'fs'
3-
1+
import {
2+
AstParser,
3+
ParsedSource,
4+
} from '@exercism/static-analysis/dist/AstParser'
5+
import {
6+
extractTests,
7+
TestCase,
8+
} from '@exercism/static-analysis/dist/extracts/extract_tests'
9+
import { FileInput } from '@exercism/static-analysis/dist/input/FileInput'
410
import { ConsoleBuffer } from '@jest/console'
511
import {
612
AggregatedResult,
713
AssertionResult,
814
TestResult,
915
} from '@jest/test-result'
1016
import { Config } from '@jest/types'
17+
import fs from 'fs'
18+
import path from 'path'
1119

1220
interface OutputInterface {
1321
status: 'fail' | 'pass' | 'error'
@@ -20,6 +28,7 @@ interface OutputTestInterface {
2028
status: 'fail' | 'pass' | 'error'
2129
message: string
2230
output: string | null
31+
test_code: string
2332
}
2433

2534
export class Output {
@@ -64,11 +73,62 @@ export class Output {
6473
}
6574
}
6675

76+
const parsedSources: Record<
77+
string,
78+
{
79+
program: ParsedSource['program']
80+
source: ParsedSource['source']
81+
tests: Record<string, TestCase>
82+
}
83+
> = {}
84+
85+
// Every tested test file is indexed and parsed
86+
this.results.tests
87+
.map((test) => test.test_code)
88+
.filter((path, index, self) => self.indexOf(path) === index)
89+
.forEach((file) => {
90+
try {
91+
const [{ program, source }] = AstParser.ANALYZER.parseSync(
92+
fs.readFileSync(file).toString()
93+
)
94+
const tests = extractTests(program)
95+
96+
parsedSources[file] = {
97+
program,
98+
source,
99+
tests: tests.reduce((results, item) => {
100+
results[item.name(' > ')] = item
101+
results[item.name(' ' as ' > ')] = item
102+
return results
103+
}, {} as Record<string, TestCase>),
104+
}
105+
} catch (err) {
106+
console.error(
107+
`When trying to parse ${file}, the following error occurred`,
108+
err
109+
)
110+
}
111+
})
112+
113+
// Extract the test code, if possible
114+
const tests = this.results.tests.map((test) => {
115+
const parsedSource = parsedSources[test.test_code]
116+
if (!parsedSource) {
117+
return { ...test, test_code: null }
118+
}
119+
120+
const testCase = parsedSource.tests[test.name]
121+
if (!testCase) {
122+
return { ...test, test_code: null }
123+
}
124+
125+
return { ...test, test_code: testCase.testCode(parsedSource.source) }
126+
})
127+
67128
// Re-order the output so that tests output shows below main output
68-
const { status, message, tests } = this.results
129+
const { status, message } = this.results
69130

70131
const artifact = JSON.stringify({ status, message, tests }, undefined, 2)
71-
72132
fs.writeFileSync(this.outputFile, artifact)
73133
}
74134

@@ -143,8 +203,10 @@ export class Output {
143203
return {
144204
...withoutOutput,
145205
output: isFirstFailure
146-
? [consoleOutputs[''], outputMessage].filter(Boolean).join('\n')
206+
? [consoleOutputs[''], outputMessage].filter(Boolean).join('\n') ||
207+
null
147208
: outputMessage,
209+
test_code: specFilePath,
148210
}
149211
})
150212
)

test/.eslintignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
fixtures/*
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const twoFer = (name = 'you') => `One for ${name}, one for me.`
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
describe('twoFer()', t('another name given', () => {
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)