ic-mops 0.20.2 → 0.21.0

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.
Files changed (30) hide show
  1. package/cli.ts +2 -1
  2. package/commands/{mmf1.ts → test/mmf1.ts} +23 -13
  3. package/commands/test/reporters/compact-reporter.ts +95 -0
  4. package/commands/test/reporters/files-reporter.ts +52 -0
  5. package/commands/test/reporters/reporter.ts +7 -0
  6. package/commands/test/reporters/verbose-reporter.ts +51 -0
  7. package/commands/{test.ts → test/test.ts} +63 -79
  8. package/commands/test/utils.ts +7 -0
  9. package/dist/cli.js +2 -1
  10. package/dist/commands/test/mmf1.d.ts +26 -0
  11. package/dist/commands/test/mmf1.js +98 -0
  12. package/dist/commands/test/reporter-files.d.ts +10 -0
  13. package/dist/commands/test/reporter-files.js +48 -0
  14. package/dist/commands/test/reporter-verbose.d.ts +10 -0
  15. package/dist/commands/test/reporter-verbose.js +56 -0
  16. package/dist/commands/test/reporters/compact-reporter.d.ts +13 -0
  17. package/dist/commands/test/reporters/compact-reporter.js +92 -0
  18. package/dist/commands/test/reporters/files-reporter.d.ts +11 -0
  19. package/dist/commands/test/reporters/files-reporter.js +50 -0
  20. package/dist/commands/test/reporters/reporter.d.ts +6 -0
  21. package/dist/commands/test/reporters/reporter.js +1 -0
  22. package/dist/commands/test/reporters/verbose-reporter.d.ts +11 -0
  23. package/dist/commands/test/reporters/verbose-reporter.js +56 -0
  24. package/dist/commands/test/test.d.ts +5 -0
  25. package/dist/commands/test/test.js +189 -0
  26. package/dist/commands/test/utils.d.ts +1 -0
  27. package/dist/commands/test/utils.js +6 -0
  28. package/dist/commands/test.js +43 -58
  29. package/dist/package.json +1 -1
  30. 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);
@@ -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: string[] = [];
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(args.join(' '));
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
- console.log(out);
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: string) {
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,7 @@
1
+ import {MMF1} from '../mmf1.js';
2
+
3
+ export interface Reporter {
4
+ addFiles(files: string[]): void;
5
+ addRun(file: string, mmf: MMF1, state: Promise<void>, wasiMode: boolean): void;
6
+ done(): boolean;
7
+ }
@@ -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 {sources} from './sources.js';
12
- import {getRootDir} from '../mops.js';
13
- import {parallel} from '../parallel.js';
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 start = Date.now();
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
- console.log('Test files:');
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 mocArgs = ['--hide-warnings', '--error-detail=2', ...sourcesArr.join(' ').split(' '), file].filter(x => x);
120
-
121
- // build and run wasm
122
- if (wasiMode) {
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
- // build
126
- let buildProc = spawn(mocPath, [`-o=${wasmFile}`, '-wasi-system-api', ...mocArgs]);
127
- await pipeMMF(buildProc, mmf).then(async () => {
128
- if (mmf.failed > 0) {
129
- return;
130
- }
131
- // run
132
- let proc = spawn('wasmtime', [wasmFile]);
133
- await pipeMMF(proc, mmf);
134
- }).finally(() => {
135
- fs.rmSync(wasmFile, {force: true});
136
- });
137
- }
138
- // interpret
139
- else {
140
- let proc = spawn(mocPath, ['-r', '-ref-system-api', ...mocArgs]);
141
- await pipeMMF(proc, mmf);
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
- passed += mmf.passed;
145
- failed += mmf.failed;
146
- skipped += mmf.skipped;
152
+ reporter.addRun(file, mmf, promise, wasiMode);
147
153
 
148
- i++ && console.log('-'.repeat(50));
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`\n ...`;
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`\n ...`;
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(mmf);
215
+ resolve();
232
216
  });
233
217
  });
234
218
  }
@@ -0,0 +1,7 @@
1
+ import path from 'node:path';
2
+ import {getRootDir} from '../../mops.js';
3
+
4
+ export function absToRel(p: string) {
5
+ let rootDir = getRootDir();
6
+ return path.relative(rootDir, path.resolve(p));
7
+ }
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);
@@ -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 {};