Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgrade runner for type-tests #73

Merged
merged 12 commits into from
Jul 31, 2024
487 changes: 392 additions & 95 deletions bin/run.sh

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"prepublish": "corepack yarn test:bare && corepack yarn lint",
"lint": "corepack yarn eslint src -c eslint.config.mjs && corepack yarn eslint test -c eslint.config.mjs",
"test": "corepack yarn build && corepack yarn test:bare",
"test:bare": "corepack yarn node test/smoke.test.mjs && corepack yarn node test/skip.test.mjs && corepack yarn node test/import.test.mjs"
"test:bare": "corepack yarn node test/smoke.test.mjs && corepack yarn node test/skip.test.mjs && corepack yarn node test/import.test.mjs && corepack yarn node test/types.test.mjs"
},
"dependencies": {
"@exercism/babel-preset-typescript": "^0.5.0",
Expand All @@ -40,6 +40,7 @@
"core-js": "^3.37.1",
"jest": "^29.7.0",
"shelljs": "^0.8.5",
"tstyche": "^2.1.1",
"typescript": "~5.5.4"
},
"devDependencies": {
Expand Down
26 changes: 22 additions & 4 deletions src/output.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ type ExerciseConfig = {
'flag.tests.task-per-describe': boolean
'flag.tests.may-run-long': boolean
'flag.tests.includes-optional': boolean
'flag.tests.jest'?: boolean
'flag.tests.tstyche'?: boolean
}
}

Expand Down Expand Up @@ -66,11 +68,9 @@ export class Output {
this.results.status =
aggregatedResults.numRuntimeErrorTestSuites === 0 &&
aggregatedResults.numFailedTestSuites === 0 &&
// Pending tests are skipped tests. test.skip tests are fine in our
// reporter and should not be forced to have ran here. So the next
// line is commented out.
// Pending tests are skipped tests. test.skip tests are fine if the
// exercise reports that there are optional tests.
//
// aggregatedResults.numPendingTests === 0 &&
aggregatedResults.numFailedTests === 0
? 'pass'
: 'fail'
Expand All @@ -88,6 +88,24 @@ export class Output {
'an issue if the problem persists.'
)
}

if (
this.results.status === 'pass' &&
(aggregatedResults.numPendingTests !== 0 ||
aggregatedResults.numPendingTestSuites !== 0)
) {
if (
!this.configFlag('flag.tests.includes-optional') &&
this.config?.custom
) {
this.results.status = 'fail'
this.error(
'Expected to see 0 skipped tests and 0 skipped test suites. ' +
'None of the tests in this exercise are optional. The skipped ' +
'tests will not show up, but were found during the last run.'
)
}
}
}

const parsedSources: Record<
Expand Down
File renamed without changes.
14 changes: 14 additions & 0 deletions test/fixtures/tstyche/documentation/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"authors": [
"SleeplessByte"
],
"files": {
"solution": [
],
"test": [
"__typetests__/docs.tst.ts"
],
"example": [
]
}
}
19 changes: 19 additions & 0 deletions test/fixtures/tstyche/documentation/__typetests__/docs.tst.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { expect, test } from "tstyche";

function firstItem<T>(target: Array<T>): T | undefined {
return target[0];
}

test("first item requires a parameter", () => {
expect(firstItem(["a", "b", "c"])).type.toBe<string | undefined>();

expect(firstItem()).type.toRaiseError("Expected 1 argument");
});

function secondItem<T>(target: Array<T>): T | undefined {
return target[1];
}

test("handles numbers", () => {
expect(secondItem([1, 2, 3])).type.toBe<number | undefined>();
});
10 changes: 10 additions & 0 deletions test/fixtures/tstyche/documentation/__typetests__/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"noEmit": true,
"strict": true,
"types": []
},
"include": ["./"],
"exclude": []
}
1 change: 1 addition & 0 deletions test/fixtures/tstyche/documentation/empty.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {}
10 changes: 10 additions & 0 deletions test/fixtures/tstyche/documentation/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"compilerOptions": {
"target": "es2016",
"module": "commonjs",
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true
},
"exclude": ["./__typetests__"]
}
15 changes: 15 additions & 0 deletions test/fixtures/tstyche/fire/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"authors": [
"SleeplessByte"
],
"files": {
"solution": [
"fire.ts"
],
"test": [
"__typetests__/fire.tst.ts"
],
"example": [
]
}
}
14 changes: 14 additions & 0 deletions test/fixtures/tstyche/fire/__typetests__/fire.tst.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { test, expect, describe } from 'tstyche'
import type { MethodLikeKeys } from '../fire.js'

interface Sample {
description: string
getLength: () => number
getWidth?: () => number
}

describe('fire', () => {
test('all method keys are found', () => {
expect<MethodLikeKeys<Sample>>().type.toBe<'getLength' | 'getWidth'>()
})
})
10 changes: 10 additions & 0 deletions test/fixtures/tstyche/fire/__typetests__/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"noEmit": true,
"strict": true,
"types": []
},
"include": ["./"],
"exclude": []
}
5 changes: 5 additions & 0 deletions test/fixtures/tstyche/fire/fire.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
type MethodLike = (...args: any) => any;

export type MethodLikeKeys<T> = keyof {
[K in keyof T as T[K] extends MethodLike ? K : never]: T[K];
};
10 changes: 10 additions & 0 deletions test/fixtures/tstyche/fire/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"compilerOptions": {
"target": "es2016",
"module": "commonjs",
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true
},
"exclude": ["./__typetests__"]
}
15 changes: 15 additions & 0 deletions test/fixtures/tstyche/firefought/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"authors": [
"SleeplessByte"
],
"files": {
"solution": [
"fire.ts"
],
"test": [
"__typetests__/fire.tst.ts"
],
"example": [
]
}
}
14 changes: 14 additions & 0 deletions test/fixtures/tstyche/firefought/__typetests__/fire.tst.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { describe, test, expect } from "tstyche";
import type { MethodLikeKeys } from "../fire.js";

interface Sample {
description: string;
getLength: () => number;
getWidth?: () => number;
}

describe('fire', () => {
test('all method keys are found', () => {
expect<MethodLikeKeys<Sample>>().type.toBe<"getLength" | "getWidth">();
})
})
10 changes: 10 additions & 0 deletions test/fixtures/tstyche/firefought/__typetests__/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"noEmit": true,
"strict": true,
"types": []
},
"include": ["./"],
"exclude": []
}
5 changes: 5 additions & 0 deletions test/fixtures/tstyche/firefought/fire.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
type MethodLike = (...args: any) => any;

export type MethodLikeKeys<T> = keyof {
[K in keyof T as Required<T>[K] extends MethodLike ? K : never]: T[K];
};
10 changes: 10 additions & 0 deletions test/fixtures/tstyche/firefought/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"compilerOptions": {
"target": "es2016",
"module": "commonjs",
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true
},
"exclude": ["./__typetests__"]
}
2 changes: 1 addition & 1 deletion test/fixtures/two-fer/error/syntax/expected_results.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{ "version": 1, "status": "error", "message": "The submitted code didn't compile. We have collected the errors encountered during compilation. At this moment the error messages are not very read-friendly, but it's a start. We are working on a more helpful output.\n-------------------------------\ntwo-fer.ts(1,14): error TS1389: 'const' is not allowed as a variable declaration name.\ntwo-fer.ts(1,20): error TS1134: Variable declaration expected.\ntwo-fer.ts(1,29): error TS1109: Expression expected.\ntwo-fer.ts(2,1): error TS1005: ')' expected." }
{ "version": 1, "status": "error", "message": "The submitted code didn't compile. We have collected the errors encountered during compilation. At this moment the error messages are not very read-friendly, but it's a start. We are working on a more helpful output.\n-------------------------------\ntwo-fer.ts(1,14): error TS1389: 'const' is not allowed as a variable declaration name.\ntwo-fer.ts(1,20): error TS1134: Variable declaration expected.\ntwo-fer.ts(1,29): error TS1109: Expression expected.\ntwo-fer.ts(2,1): error TS1005: ')' expected.\n" }
4 changes: 2 additions & 2 deletions test/smoke.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ assertPass(
)

shelljs.echo(
'typescript-test-runner > passing solution > with output directory'
'typescript-test-runner > failing solution > with output directory'
)
assertPass('clock', join(fixtures, 'clock', 'pass'))
assertError('clock', join(fixtures, 'clock', 'fail'))

/** Test failures */
const failures = ['tests', 'empty']
Expand Down
13 changes: 13 additions & 0 deletions test/types.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { join } from 'node:path'
import shelljs from 'shelljs'
import { assertError, assertPass } from './asserts.mjs'
import { fixtures } from './paths.mjs'

shelljs.echo('type tests (only) > documentation solution (smoke test)')
assertPass('tstyche', join(fixtures, 'tstyche', 'documentation'))

shelljs.echo('type tests (only) > failing solution')
assertError('tstyche', join(fixtures, 'tstyche', 'fire'))

shelljs.echo('type tests (only) > passing solution')
assertPass('tstyche', join(fixtures, 'tstyche', 'firefought'))
31 changes: 31 additions & 0 deletions tsconfig.solutions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"extends": "@tsconfig/node20/tsconfig.json",
"compilerOptions": {
// Allows you to use the newest syntax, and have access to console.log
// https://www.typescriptlang.org/tsconfig#lib
"lib": ["ES2020", "dom"],
// Make sure typescript is configured to output ESM
// https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c#how-can-i-make-my-typescript-project-output-esm
"module": "Node16",
// Since this project is using babel, TypeScript may target something very
// high, and babel will make sure it runs on your local Node version.
// https://babeljs.io/docs/en/
"target": "ES2020", // ESLint doesn't support this yet: "es2022",

"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,

// Because jest-resolve isn't like node resolve, the absolute path must be .ts
"allowImportingTsExtensions": true,
"noEmit": true,

// Because we'll be using babel: ensure that Babel can safely transpile
// files in the TypeScript project.
//
// https://babeljs.io/docs/en/babel-plugin-transform-typescript/#caveats
"isolatedModules": true
},
"exclude": [".meta/*", "__typetests__/*", "*.test.ts", "*.tst.ts"]
}
15 changes: 15 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1546,6 +1546,7 @@ __metadata:
prettier: "npm:^3.3.3"
rimraf: "npm:^6.0.1"
shelljs: "npm:^0.8.5"
tstyche: "npm:^2.1.1"
typescript: "npm:~5.5.4"
bin:
typescript-test-runner: bin/run.sh
Expand Down Expand Up @@ -6274,6 +6275,20 @@ __metadata:
languageName: node
linkType: hard

"tstyche@npm:^2.1.1":
version: 2.1.1
resolution: "tstyche@npm:2.1.1"
peerDependencies:
typescript: 4.x || 5.x
peerDependenciesMeta:
typescript:
optional: true
bin:
tstyche: ./build/bin.js
checksum: 10/f30e7d782e51c262528ededf383c9daf39af8dea063d483667e3ff9f4800434891589c294c4b4f69802dd06daf8fb1d2a10553316d2f4631ba1413d3e48dab81
languageName: node
linkType: hard

"type-check@npm:^0.4.0, type-check@npm:~0.4.0":
version: 0.4.0
resolution: "type-check@npm:0.4.0"
Expand Down
Loading