ic-mops 0.20.2 → 0.21.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli.ts +2 -1
- package/commands/sources.ts +3 -2
- package/commands/{mmf1.ts → test/mmf1.ts} +23 -13
- package/commands/test/reporters/compact-reporter.ts +95 -0
- package/commands/test/reporters/files-reporter.ts +52 -0
- package/commands/test/reporters/reporter.ts +7 -0
- package/commands/test/reporters/verbose-reporter.ts +51 -0
- package/commands/{test.ts → test/test.ts} +63 -79
- package/commands/test/utils.ts +7 -0
- package/dist/cli.js +2 -1
- package/dist/commands/sources.js +3 -2
- package/dist/commands/test/mmf1.d.ts +26 -0
- package/dist/commands/test/mmf1.js +98 -0
- package/dist/commands/test/reporter-files.d.ts +10 -0
- package/dist/commands/test/reporter-files.js +48 -0
- package/dist/commands/test/reporter-verbose.d.ts +10 -0
- package/dist/commands/test/reporter-verbose.js +56 -0
- package/dist/commands/test/reporters/compact-reporter.d.ts +13 -0
- package/dist/commands/test/reporters/compact-reporter.js +92 -0
- package/dist/commands/test/reporters/files-reporter.d.ts +11 -0
- package/dist/commands/test/reporters/files-reporter.js +50 -0
- package/dist/commands/test/reporters/reporter.d.ts +6 -0
- package/dist/commands/test/reporters/reporter.js +1 -0
- package/dist/commands/test/reporters/verbose-reporter.d.ts +11 -0
- package/dist/commands/test/reporters/verbose-reporter.js +56 -0
- package/dist/commands/test/test.d.ts +5 -0
- package/dist/commands/test/test.js +189 -0
- package/dist/commands/test/utils.d.ts +1 -0
- package/dist/commands/test/utils.js +6 -0
- package/dist/commands/test.js +43 -58
- package/dist/package.json +1 -1
- package/package.json +1 -1
package/cli.ts
CHANGED
|
@@ -15,7 +15,7 @@ import {installAll} from './commands/install-all.js';
|
|
|
15
15
|
import {search} from './commands/search.js';
|
|
16
16
|
import {add} from './commands/add.js';
|
|
17
17
|
import {cacheSize, cleanCache} from './cache.js';
|
|
18
|
-
import {test} from './commands/test.js';
|
|
18
|
+
import {test} from './commands/test/test.js';
|
|
19
19
|
import {template} from './commands/template.js';
|
|
20
20
|
import {selfUpdate} from './commands/self-update.js';
|
|
21
21
|
import {remove} from './commands/remove.js';
|
|
@@ -186,6 +186,7 @@ program
|
|
|
186
186
|
program
|
|
187
187
|
.command('test [filter]')
|
|
188
188
|
.description('Run tests')
|
|
189
|
+
.option('-r, --reporter <reporter>', 'Choose reporter: verbose, compact, files')
|
|
189
190
|
.option('-w, --watch', 'Enable watch mode')
|
|
190
191
|
.action(async (filter, options) => {
|
|
191
192
|
await test(filter, options);
|
package/commands/sources.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import fs from 'node:fs';
|
|
3
3
|
import chalk from 'chalk';
|
|
4
|
-
import {checkConfigFile, formatDir, formatGithubDir, parseGithubURL, readConfig} from '../mops.js';
|
|
4
|
+
import {checkConfigFile, formatDir, formatGithubDir, getRootDir, parseGithubURL, readConfig} from '../mops.js';
|
|
5
5
|
import {VesselConfig, readVesselConfig} from '../vessel.js';
|
|
6
6
|
import {Config, Dependency} from '../types.js';
|
|
7
7
|
|
|
@@ -111,10 +111,11 @@ export async function sources({verbose = false} = {}) {
|
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
// sources
|
|
114
|
+
let rootDir = getRootDir();
|
|
114
115
|
return Object.entries(packages).map(([name, pkg]) => {
|
|
115
116
|
let pkgDir;
|
|
116
117
|
if (pkg.path) {
|
|
117
|
-
pkgDir = path.relative(process.cwd(), path.resolve(pkg.path));
|
|
118
|
+
pkgDir = path.relative(process.cwd(), path.resolve(rootDir, pkg.path));
|
|
118
119
|
pkgDir = pkgDir.replaceAll('{MOPS_ENV}', process.env.MOPS_ENV || 'local');
|
|
119
120
|
}
|
|
120
121
|
else if (pkg.repo) {
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
import chalk from 'chalk';
|
|
6
6
|
|
|
7
7
|
type Strategy = 'store' | 'print';
|
|
8
|
+
type TestStatus = 'pass' | 'fail' | 'skip';
|
|
9
|
+
type MessageType = 'pass' | 'fail' | 'skip' | 'suite' | 'stdout';
|
|
8
10
|
|
|
9
11
|
export class MMF1 {
|
|
10
12
|
stack: string[] = [];
|
|
@@ -13,24 +15,32 @@ export class MMF1 {
|
|
|
13
15
|
passed = 0;
|
|
14
16
|
skipped = 0;
|
|
15
17
|
srategy: Strategy;
|
|
16
|
-
output:
|
|
18
|
+
output: {
|
|
19
|
+
type: MessageType;
|
|
20
|
+
message: string;
|
|
21
|
+
}[] = [];
|
|
17
22
|
|
|
18
23
|
constructor(srategy: Strategy) {
|
|
19
24
|
this.srategy = srategy;
|
|
20
25
|
}
|
|
21
26
|
|
|
22
|
-
_log(...args: string[]) {
|
|
27
|
+
_log(type: MessageType, ...args: string[]) {
|
|
23
28
|
if (this.srategy === 'store') {
|
|
24
|
-
this.output.push(
|
|
29
|
+
this.output.push({
|
|
30
|
+
type,
|
|
31
|
+
message: args.join(' ')
|
|
32
|
+
});
|
|
25
33
|
}
|
|
26
34
|
else if (this.srategy === 'print') {
|
|
27
35
|
console.log(...args);
|
|
28
36
|
}
|
|
29
37
|
}
|
|
30
38
|
|
|
31
|
-
flush() {
|
|
39
|
+
flush(messageType?: MessageType) {
|
|
32
40
|
for (let out of this.output) {
|
|
33
|
-
|
|
41
|
+
if (!messageType || out.type === messageType) {
|
|
42
|
+
console.log(out.message);
|
|
43
|
+
}
|
|
34
44
|
}
|
|
35
45
|
this.output = [];
|
|
36
46
|
}
|
|
@@ -46,7 +56,7 @@ export class MMF1 {
|
|
|
46
56
|
this._testSkip(line.split('mops:1:skip ')[1] || '');
|
|
47
57
|
}
|
|
48
58
|
else {
|
|
49
|
-
this._log(' '.repeat(this.stack.length * 2), chalk.gray('stdout'), line);
|
|
59
|
+
this._log('stdout', ' '.repeat(this.stack.length * 2), chalk.gray('stdout'), line);
|
|
50
60
|
}
|
|
51
61
|
}
|
|
52
62
|
|
|
@@ -55,7 +65,7 @@ export class MMF1 {
|
|
|
55
65
|
if (suite) {
|
|
56
66
|
if (this.currSuite !== suite) {
|
|
57
67
|
this.currSuite = suite;
|
|
58
|
-
this._log(' '.repeat((this.stack.length - 1) * 2), (chalk.gray('•')) + '', suite);
|
|
68
|
+
this._log('suite', ' '.repeat((this.stack.length - 1) * 2), (chalk.gray('•')) + '', suite);
|
|
59
69
|
}
|
|
60
70
|
}
|
|
61
71
|
this.stack.push(name);
|
|
@@ -72,29 +82,29 @@ export class MMF1 {
|
|
|
72
82
|
this._status(name, 'skip');
|
|
73
83
|
}
|
|
74
84
|
|
|
75
|
-
_status(name: string, status:
|
|
85
|
+
_status(name: string, status: TestStatus) {
|
|
76
86
|
if (status === 'pass') {
|
|
77
87
|
// do not print suite at the end
|
|
78
88
|
if (name === this.currSuite) {
|
|
79
89
|
return;
|
|
80
90
|
}
|
|
81
91
|
this.passed++;
|
|
82
|
-
this._log(' '.repeat(this.stack.length * 2), chalk.green('✓'), name);
|
|
92
|
+
this._log(status, ' '.repeat(this.stack.length * 2), chalk.green('✓'), name);
|
|
83
93
|
}
|
|
84
94
|
else if (status === 'fail') {
|
|
85
95
|
this.failed++;
|
|
86
|
-
this._log(' '.repeat(this.stack.length * 2), chalk.red('✖'), name);
|
|
96
|
+
this._log(status, ' '.repeat(this.stack.length * 2), chalk.red('✖'), name);
|
|
87
97
|
}
|
|
88
98
|
else if (status === 'skip') {
|
|
89
99
|
this.skipped++;
|
|
90
|
-
this._log(' '.repeat(this.stack.length * 2), chalk.yellow('−'), name);
|
|
100
|
+
this._log(status, ' '.repeat(this.stack.length * 2), chalk.yellow('−'), name);
|
|
91
101
|
}
|
|
92
102
|
}
|
|
93
103
|
|
|
94
104
|
fail(stderr: string) {
|
|
95
105
|
let name = this.stack.pop() || '';
|
|
96
106
|
this._status(name, 'fail');
|
|
97
|
-
this._log(' '.repeat(this.stack.length * 2), chalk.red('FAIL'), stderr);
|
|
107
|
+
this._log('fail', ' '.repeat(this.stack.length * 2), chalk.red('FAIL'), stderr);
|
|
98
108
|
}
|
|
99
109
|
|
|
100
110
|
pass() {
|
|
@@ -102,6 +112,6 @@ export class MMF1 {
|
|
|
102
112
|
if (name) {
|
|
103
113
|
this._status(name, 'pass');
|
|
104
114
|
}
|
|
105
|
-
this._log(' '.repeat(this.stack.length * 2), chalk.green('PASS'));
|
|
115
|
+
this._log('pass', ' '.repeat(this.stack.length * 2), chalk.green('PASS'));
|
|
106
116
|
}
|
|
107
117
|
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import logUpdate from 'log-update';
|
|
3
|
+
import {absToRel} from '../utils.js';
|
|
4
|
+
import {MMF1} from '../mmf1.js';
|
|
5
|
+
import {Reporter} from './reporter.js';
|
|
6
|
+
|
|
7
|
+
export class CompactReporter implements Reporter {
|
|
8
|
+
passed = 0;
|
|
9
|
+
failed = 0;
|
|
10
|
+
skipped = 0;
|
|
11
|
+
passedFiles = 0;
|
|
12
|
+
failedFiles = 0;
|
|
13
|
+
|
|
14
|
+
#allFiles = new Set<string>();
|
|
15
|
+
#runningFiles = new Set<string>();
|
|
16
|
+
#failedFiles = new Set<string>();
|
|
17
|
+
#finishedFiles = new Set<string>();
|
|
18
|
+
#startTime = Date.now();
|
|
19
|
+
|
|
20
|
+
addFiles(files: string[]) {
|
|
21
|
+
this.#allFiles = new Set(files);
|
|
22
|
+
this.#log();
|
|
23
|
+
this.#startTimer();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
addRun(file: string, mmf: MMF1, state: Promise<void>, _wasiMode: boolean) {
|
|
27
|
+
this.#runningFiles.add(file);
|
|
28
|
+
this.#log();
|
|
29
|
+
|
|
30
|
+
state.then(() => {
|
|
31
|
+
this.passed += mmf.passed;
|
|
32
|
+
this.failed += mmf.failed;
|
|
33
|
+
this.skipped += mmf.skipped;
|
|
34
|
+
|
|
35
|
+
this.passedFiles += Number(mmf.failed === 0);
|
|
36
|
+
this.failedFiles += Number(mmf.failed !== 0);
|
|
37
|
+
|
|
38
|
+
if (mmf.failed) {
|
|
39
|
+
this.#failedFiles.add(file);
|
|
40
|
+
logUpdate.clear();
|
|
41
|
+
console.log(chalk.red('✖'), absToRel(file));
|
|
42
|
+
mmf.flush('fail');
|
|
43
|
+
console.log('-'.repeat(50));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
this.#runningFiles.delete(file);
|
|
47
|
+
this.#finishedFiles.add(file);
|
|
48
|
+
this.#log();
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
done(): boolean {
|
|
53
|
+
this.#log();
|
|
54
|
+
logUpdate.done();
|
|
55
|
+
this.#clearTimer();
|
|
56
|
+
return this.failed === 0;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
#timerId: NodeJS.Timer | null = null;
|
|
60
|
+
#startTimer() {
|
|
61
|
+
this.#timerId = setInterval(() => this.#log(), 55);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
#clearTimer() {
|
|
65
|
+
if (this.#timerId) {
|
|
66
|
+
clearInterval(this.#timerId);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
#log() {
|
|
71
|
+
let res: string[] = [];
|
|
72
|
+
let i = 0;
|
|
73
|
+
for (let file of this.#allFiles) {
|
|
74
|
+
if (this.#runningFiles.has(file)) {
|
|
75
|
+
res[Number(i)] = '.';
|
|
76
|
+
}
|
|
77
|
+
else if (this.#finishedFiles.has(file)) {
|
|
78
|
+
res[Number(i)] = this.#failedFiles.has(file) ? chalk.red(':') : ':';
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
res[Number(i)] = ' ';
|
|
82
|
+
}
|
|
83
|
+
i++;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
let output = `[${res.join('')}]\n`
|
|
87
|
+
+ `${chalk.gray(((Date.now() - this.#startTime) / 1000).toFixed(2) + 's')}`
|
|
88
|
+
+ `, total ${this.#allFiles.size} files`
|
|
89
|
+
+ `, passed ${chalk.greenBright(this.passedFiles)} files`
|
|
90
|
+
+ (this.skipped ? `, skipped ${chalk[this.skipped ? 'yellowBright' : 'gray'](this.skipped)} cases` : '')
|
|
91
|
+
+ (this.failed ? `, failed ${chalk[this.failed ? 'redBright' : 'gray'](this.failed)} cases` : '');
|
|
92
|
+
|
|
93
|
+
logUpdate(output);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import {absToRel} from '../utils.js';
|
|
3
|
+
import {MMF1} from '../mmf1.js';
|
|
4
|
+
import {Reporter} from './reporter.js';
|
|
5
|
+
|
|
6
|
+
export class FilesReporter implements Reporter {
|
|
7
|
+
passed = 0;
|
|
8
|
+
failed = 0;
|
|
9
|
+
skipped = 0;
|
|
10
|
+
|
|
11
|
+
#startTime = Date.now();
|
|
12
|
+
|
|
13
|
+
addFiles(files: string[]) {
|
|
14
|
+
console.log(`Test files: ${files.length}`);
|
|
15
|
+
console.log('='.repeat(50));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
addRun(file: string, mmf: MMF1, state: Promise<void>, wasiMode: boolean) {
|
|
19
|
+
state.then(() => {
|
|
20
|
+
this.passed += Number(mmf.failed === 0);
|
|
21
|
+
this.failed += Number(mmf.failed !== 0);
|
|
22
|
+
this.skipped += mmf.skipped;
|
|
23
|
+
|
|
24
|
+
if (mmf.failed) {
|
|
25
|
+
console.log(chalk.red('✖'), absToRel(file));
|
|
26
|
+
mmf.flush('fail');
|
|
27
|
+
console.log('-'.repeat(50));
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
console.log(`${chalk.green('✓')} ${absToRel(file)} ${wasiMode ? chalk.gray('(wasi)') : ''}`);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
done(): boolean {
|
|
36
|
+
console.log('='.repeat(50));
|
|
37
|
+
if (this.failed) {
|
|
38
|
+
console.log(chalk.redBright('Tests failed'));
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
console.log(chalk.greenBright('Tests passed'));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
console.log(`Done in ${chalk.gray(((Date.now() - this.#startTime) / 1000).toFixed(2) + 's')}`
|
|
45
|
+
+ `, passed ${chalk.greenBright(this.passed)} files`
|
|
46
|
+
+ (this.skipped ? `, skipped ${chalk[this.skipped ? 'yellowBright' : 'gray'](this.skipped)} cases` : '')
|
|
47
|
+
+ (this.failed ? `, failed ${chalk[this.failed ? 'redBright' : 'gray'](this.failed)} files` : '')
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
return this.failed === 0;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import {absToRel} from '../utils.js';
|
|
3
|
+
import {MMF1} from '../mmf1.js';
|
|
4
|
+
import {Reporter} from './reporter.js';
|
|
5
|
+
|
|
6
|
+
export class VerboseReporter implements Reporter {
|
|
7
|
+
passed = 0;
|
|
8
|
+
failed = 0;
|
|
9
|
+
skipped = 0;
|
|
10
|
+
|
|
11
|
+
#startTime = Date.now();
|
|
12
|
+
#curFileIndex = 0;
|
|
13
|
+
|
|
14
|
+
addFiles(files: string[]) {
|
|
15
|
+
console.log('Test files:');
|
|
16
|
+
for (let file of files) {
|
|
17
|
+
console.log(chalk.gray(`• ${absToRel(file)}`));
|
|
18
|
+
}
|
|
19
|
+
console.log('='.repeat(50));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
addRun(file: string, mmf: MMF1, state: Promise<void>, wasiMode: boolean) {
|
|
23
|
+
state.then(() => {
|
|
24
|
+
this.passed += mmf.passed;
|
|
25
|
+
this.failed += mmf.failed;
|
|
26
|
+
this.skipped += mmf.skipped;
|
|
27
|
+
|
|
28
|
+
this.#curFileIndex++ && console.log('-'.repeat(50));
|
|
29
|
+
console.log(`Running ${chalk.gray(absToRel(file))} ${wasiMode ? chalk.gray('(wasi)') : ''}`);
|
|
30
|
+
mmf.flush();
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
done(): boolean {
|
|
35
|
+
console.log('='.repeat(50));
|
|
36
|
+
if (this.failed) {
|
|
37
|
+
console.log(chalk.redBright('Tests failed'));
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
console.log(chalk.greenBright('Tests passed'));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
console.log(`Done in ${chalk.gray(((Date.now() - this.#startTime) / 1000).toFixed(2) + 's')}`
|
|
44
|
+
+ `, passed ${chalk.greenBright(this.passed)}`
|
|
45
|
+
+ (this.skipped ? `, skipped ${chalk[this.skipped ? 'yellowBright' : 'gray'](this.skipped)}` : '')
|
|
46
|
+
+ (this.failed ? `, failed ${chalk[this.failed ? 'redBright' : 'gray'](this.failed)}` : '')
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
return this.failed === 0;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -7,10 +7,16 @@ import {globSync} from 'glob';
|
|
|
7
7
|
import chokidar from 'chokidar';
|
|
8
8
|
import debounce from 'debounce';
|
|
9
9
|
|
|
10
|
+
import {sources} from '../sources.js';
|
|
11
|
+
import {getRootDir} from '../../mops.js';
|
|
12
|
+
import {parallel} from '../../parallel.js';
|
|
13
|
+
|
|
10
14
|
import {MMF1} from './mmf1.js';
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
15
|
+
import {absToRel} from './utils.js';
|
|
16
|
+
import {VerboseReporter} from './reporters/verbose-reporter.js';
|
|
17
|
+
import {FilesReporter} from './reporters/files-reporter.js';
|
|
18
|
+
import {CompactReporter} from './reporters/compact-reporter.js';
|
|
19
|
+
import {Reporter} from './reporters/reporter.js';
|
|
14
20
|
|
|
15
21
|
let ignore = [
|
|
16
22
|
'**/node_modules/**',
|
|
@@ -24,7 +30,7 @@ let globConfig = {
|
|
|
24
30
|
ignore: ignore,
|
|
25
31
|
};
|
|
26
32
|
|
|
27
|
-
export async function test(filter = '', {watch = false} = {}) {
|
|
33
|
+
export async function test(filter = '', {watch = false, reporter = 'verbose'} = {}) {
|
|
28
34
|
let rootDir = getRootDir();
|
|
29
35
|
|
|
30
36
|
if (watch) {
|
|
@@ -33,7 +39,7 @@ export async function test(filter = '', {watch = false} = {}) {
|
|
|
33
39
|
let run = debounce(async () => {
|
|
34
40
|
console.clear();
|
|
35
41
|
process.stdout.write('\x1Bc');
|
|
36
|
-
await runAll(filter);
|
|
42
|
+
await runAll(filter, reporter);
|
|
37
43
|
console.log('-'.repeat(50));
|
|
38
44
|
console.log('Waiting for file changes...');
|
|
39
45
|
console.log(chalk.gray((`Press ${chalk.gray('Ctrl+C')} to exit.`)));
|
|
@@ -53,7 +59,7 @@ export async function test(filter = '', {watch = false} = {}) {
|
|
|
53
59
|
run();
|
|
54
60
|
}
|
|
55
61
|
else {
|
|
56
|
-
let passed = await runAll(filter);
|
|
62
|
+
let passed = await runAll(filter, reporter);
|
|
57
63
|
if (!passed) {
|
|
58
64
|
process.exit(1);
|
|
59
65
|
}
|
|
@@ -62,8 +68,18 @@ export async function test(filter = '', {watch = false} = {}) {
|
|
|
62
68
|
|
|
63
69
|
let mocPath = process.env.DFX_MOC_PATH;
|
|
64
70
|
|
|
65
|
-
export async function runAll(filter = '') {
|
|
66
|
-
let
|
|
71
|
+
export async function runAll(filter = '', reporterName = 'verbose') {
|
|
72
|
+
let reporter: Reporter;
|
|
73
|
+
if (reporterName == 'compact') {
|
|
74
|
+
reporter = new CompactReporter;
|
|
75
|
+
}
|
|
76
|
+
else if (reporterName == 'files') {
|
|
77
|
+
reporter = new FilesReporter;
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
reporter = new VerboseReporter;
|
|
81
|
+
}
|
|
82
|
+
|
|
67
83
|
let rootDir = getRootDir();
|
|
68
84
|
let files: string[] = [];
|
|
69
85
|
let libFiles = globSync('**/test?(s)/lib.mo', globConfig);
|
|
@@ -87,15 +103,8 @@ export async function runAll(filter = '') {
|
|
|
87
103
|
return;
|
|
88
104
|
}
|
|
89
105
|
|
|
90
|
-
|
|
91
|
-
for (let file of files) {
|
|
92
|
-
console.log(chalk.gray(`• ${absToRel(file)}`));
|
|
93
|
-
}
|
|
94
|
-
console.log('='.repeat(50));
|
|
106
|
+
reporter.addFiles(files);
|
|
95
107
|
|
|
96
|
-
let failed = 0;
|
|
97
|
-
let passed = 0;
|
|
98
|
-
let skipped = 0;
|
|
99
108
|
let sourcesArr = await sources();
|
|
100
109
|
|
|
101
110
|
if (!mocPath) {
|
|
@@ -105,77 +114,52 @@ export async function runAll(filter = '') {
|
|
|
105
114
|
let wasmDir = `${getRootDir()}/.mops/.test/`;
|
|
106
115
|
fs.mkdirSync(wasmDir, {recursive: true});
|
|
107
116
|
|
|
108
|
-
let i = 0;
|
|
109
|
-
|
|
110
117
|
await parallel(os.cpus().length, files, async (file: string) => {
|
|
111
|
-
if (!mocPath) {
|
|
112
|
-
mocPath = 'moc';
|
|
113
|
-
}
|
|
114
|
-
|
|
115
118
|
let mmf = new MMF1('store');
|
|
116
|
-
|
|
117
119
|
let wasiMode = fs.readFileSync(file, 'utf8').startsWith('// @testmode wasi');
|
|
118
120
|
|
|
119
|
-
let
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
let wasmFile = `${path.join(wasmDir, path.parse(file).name)}.wasm`;
|
|
121
|
+
let promise = new Promise<void>((resolve) => {
|
|
122
|
+
if (!mocPath) {
|
|
123
|
+
mocPath = 'moc';
|
|
124
|
+
}
|
|
124
125
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
//
|
|
132
|
-
let
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
126
|
+
let mocArgs = ['--hide-warnings', '--error-detail=2', ...sourcesArr.join(' ').split(' '), file].filter(x => x);
|
|
127
|
+
|
|
128
|
+
// build and run wasm
|
|
129
|
+
if (wasiMode) {
|
|
130
|
+
let wasmFile = `${path.join(wasmDir, path.parse(file).name)}.wasm`;
|
|
131
|
+
|
|
132
|
+
// build
|
|
133
|
+
let buildProc = spawn(mocPath, [`-o=${wasmFile}`, '-wasi-system-api', ...mocArgs]);
|
|
134
|
+
pipeMMF(buildProc, mmf).then(async () => {
|
|
135
|
+
if (mmf.failed > 0) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
// run
|
|
139
|
+
let proc = spawn('wasmtime', [wasmFile]);
|
|
140
|
+
await pipeMMF(proc, mmf);
|
|
141
|
+
}).finally(() => {
|
|
142
|
+
fs.rmSync(wasmFile, {force: true});
|
|
143
|
+
}).then(resolve);
|
|
144
|
+
}
|
|
145
|
+
// interpret
|
|
146
|
+
else {
|
|
147
|
+
let proc = spawn(mocPath, ['-r', '-ref-system-api', ...mocArgs]);
|
|
148
|
+
pipeMMF(proc, mmf).then(resolve);
|
|
149
|
+
}
|
|
150
|
+
});
|
|
143
151
|
|
|
144
|
-
|
|
145
|
-
failed += mmf.failed;
|
|
146
|
-
skipped += mmf.skipped;
|
|
152
|
+
reporter.addRun(file, mmf, promise, wasiMode);
|
|
147
153
|
|
|
148
|
-
|
|
149
|
-
console.log(`Running ${chalk.gray(path.relative(rootDir, file))} ${wasiMode ? chalk.gray('(wasi)') : ''}`);
|
|
150
|
-
mmf.flush();
|
|
154
|
+
await promise;
|
|
151
155
|
});
|
|
152
156
|
|
|
153
157
|
fs.rmSync(wasmDir, {recursive: true, force: true});
|
|
154
|
-
|
|
155
|
-
console.log('='.repeat(50));
|
|
156
|
-
if (failed) {
|
|
157
|
-
console.log(chalk.redBright('Tests failed'));
|
|
158
|
-
}
|
|
159
|
-
else {
|
|
160
|
-
console.log(chalk.greenBright('Tests passed'));
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
console.log(`Done in ${chalk.gray(((Date.now() - start) / 1000).toFixed(2) + 's')}`
|
|
164
|
-
+ `, passed ${chalk.greenBright(passed)}`
|
|
165
|
-
+ (skipped ? `, skipped ${chalk[skipped ? 'yellowBright' : 'gray'](skipped)}` : '')
|
|
166
|
-
+ (failed ? `, failed ${chalk[failed ? 'redBright' : 'gray'](failed)}` : '')
|
|
167
|
-
);
|
|
168
|
-
|
|
169
|
-
return failed === 0;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
function absToRel(p: string) {
|
|
173
|
-
let rootDir = getRootDir();
|
|
174
|
-
return path.relative(rootDir, path.resolve(p));
|
|
158
|
+
return reporter.done();
|
|
175
159
|
}
|
|
176
160
|
|
|
177
161
|
function pipeMMF(proc: ChildProcessWithoutNullStreams, mmf: MMF1) {
|
|
178
|
-
return new Promise((resolve) => {
|
|
162
|
+
return new Promise<void>((resolve) => {
|
|
179
163
|
// stdout
|
|
180
164
|
proc.stdout.on('data', (data) => {
|
|
181
165
|
for (let line of data.toString().split('\n')) {
|
|
@@ -202,16 +186,16 @@ function pipeMMF(proc: ChildProcessWithoutNullStreams, mmf: MMF1) {
|
|
|
202
186
|
// show failed line
|
|
203
187
|
let content = fs.readFileSync(m1);
|
|
204
188
|
let lines = content.toString().split('\n') || [];
|
|
205
|
-
failedLine += chalk.dim
|
|
189
|
+
failedLine += chalk.dim('\n ...');
|
|
206
190
|
let lineBefore = lines[+m2 - 2];
|
|
207
191
|
if (lineBefore) {
|
|
208
|
-
failedLine += chalk.dim`\n ${+m2 - 1}\t| ${lineBefore.replaceAll('\t', ' ')}
|
|
192
|
+
failedLine += chalk.dim(`\n ${+m2 - 1}\t| ${lineBefore.replaceAll('\t', ' ')}`);
|
|
209
193
|
}
|
|
210
194
|
failedLine += `\n${chalk.redBright`->`} ${m2}\t| ${lines[+m2 - 1]?.replaceAll('\t', ' ')}`;
|
|
211
195
|
if (lines.length > +m2) {
|
|
212
|
-
failedLine += chalk.dim`\n ${+m2 + 1}\t| ${lines[+m2]?.replaceAll('\t', ' ')}
|
|
196
|
+
failedLine += chalk.dim(`\n ${+m2 + 1}\t| ${lines[+m2]?.replaceAll('\t', ' ')}`);
|
|
213
197
|
}
|
|
214
|
-
failedLine += chalk.dim
|
|
198
|
+
failedLine += chalk.dim('\n ...');
|
|
215
199
|
return res;
|
|
216
200
|
});
|
|
217
201
|
if (failedLine) {
|
|
@@ -228,7 +212,7 @@ function pipeMMF(proc: ChildProcessWithoutNullStreams, mmf: MMF1) {
|
|
|
228
212
|
else if (code !== 1) {
|
|
229
213
|
mmf.fail(`unknown exit code: ${code}`);
|
|
230
214
|
}
|
|
231
|
-
resolve(
|
|
215
|
+
resolve();
|
|
232
216
|
});
|
|
233
217
|
});
|
|
234
218
|
}
|
package/dist/cli.js
CHANGED
|
@@ -13,7 +13,7 @@ import { installAll } from './commands/install-all.js';
|
|
|
13
13
|
import { search } from './commands/search.js';
|
|
14
14
|
import { add } from './commands/add.js';
|
|
15
15
|
import { cacheSize, cleanCache } from './cache.js';
|
|
16
|
-
import { test } from './commands/test.js';
|
|
16
|
+
import { test } from './commands/test/test.js';
|
|
17
17
|
import { template } from './commands/template.js';
|
|
18
18
|
import { selfUpdate } from './commands/self-update.js';
|
|
19
19
|
import { remove } from './commands/remove.js';
|
|
@@ -167,6 +167,7 @@ program
|
|
|
167
167
|
program
|
|
168
168
|
.command('test [filter]')
|
|
169
169
|
.description('Run tests')
|
|
170
|
+
.option('-r, --reporter <reporter>', 'Choose reporter: verbose, compact, files')
|
|
170
171
|
.option('-w, --watch', 'Enable watch mode')
|
|
171
172
|
.action(async (filter, options) => {
|
|
172
173
|
await test(filter, options);
|
package/dist/commands/sources.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import fs from 'node:fs';
|
|
3
3
|
import chalk from 'chalk';
|
|
4
|
-
import { checkConfigFile, formatDir, formatGithubDir, parseGithubURL, readConfig } from '../mops.js';
|
|
4
|
+
import { checkConfigFile, formatDir, formatGithubDir, getRootDir, parseGithubURL, readConfig } from '../mops.js';
|
|
5
5
|
import { readVesselConfig } from '../vessel.js';
|
|
6
6
|
// TODO: resolve conflicts
|
|
7
7
|
export async function sources({ verbose = false } = {}) {
|
|
@@ -91,10 +91,11 @@ export async function sources({ verbose = false } = {}) {
|
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
93
|
// sources
|
|
94
|
+
let rootDir = getRootDir();
|
|
94
95
|
return Object.entries(packages).map(([name, pkg]) => {
|
|
95
96
|
let pkgDir;
|
|
96
97
|
if (pkg.path) {
|
|
97
|
-
pkgDir = path.relative(process.cwd(), path.resolve(pkg.path));
|
|
98
|
+
pkgDir = path.relative(process.cwd(), path.resolve(rootDir, pkg.path));
|
|
98
99
|
pkgDir = pkgDir.replaceAll('{MOPS_ENV}', process.env.MOPS_ENV || 'local');
|
|
99
100
|
}
|
|
100
101
|
else if (pkg.repo) {
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
type Strategy = 'store' | 'print';
|
|
2
|
+
type TestStatus = 'pass' | 'fail' | 'skip';
|
|
3
|
+
type MessageType = 'pass' | 'fail' | 'skip' | 'suite' | 'stdout';
|
|
4
|
+
export declare class MMF1 {
|
|
5
|
+
stack: string[];
|
|
6
|
+
currSuite: string;
|
|
7
|
+
failed: number;
|
|
8
|
+
passed: number;
|
|
9
|
+
skipped: number;
|
|
10
|
+
srategy: Strategy;
|
|
11
|
+
output: {
|
|
12
|
+
type: MessageType;
|
|
13
|
+
message: string;
|
|
14
|
+
}[];
|
|
15
|
+
constructor(srategy: Strategy);
|
|
16
|
+
_log(type: MessageType, ...args: string[]): void;
|
|
17
|
+
flush(messageType?: MessageType): void;
|
|
18
|
+
parseLine(line: string): void;
|
|
19
|
+
_testStart(name: string): void;
|
|
20
|
+
_testEnd(name: string): void;
|
|
21
|
+
_testSkip(name: string): void;
|
|
22
|
+
_status(name: string, status: TestStatus): void;
|
|
23
|
+
fail(stderr: string): void;
|
|
24
|
+
pass(): void;
|
|
25
|
+
}
|
|
26
|
+
export {};
|