ic-mops 0.25.3 → 0.26.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.
- package/README.md +16 -3
- package/cli.ts +4 -7
- package/commands/publish.ts +39 -15
- package/commands/test/mmf1.ts +10 -1
- package/commands/test/reporters/compact-reporter.ts +4 -0
- package/commands/test/reporters/silent-reporter.ts +42 -0
- package/commands/test/reporters/verbose-reporter.ts +4 -0
- package/commands/test/test.ts +17 -8
- package/declarations/main/main.did +12 -0
- package/declarations/main/main.did.d.ts +7 -0
- package/declarations/main/main.did.js +10 -0
- package/dist/cli.js +4 -7
- package/dist/commands/publish.d.ts +3 -2
- package/dist/commands/publish.js +36 -15
- package/dist/commands/test/mmf1.d.ts +4 -1
- package/dist/commands/test/mmf1.js +9 -1
- package/dist/commands/test/reporters/compact-reporter.js +3 -0
- package/dist/commands/test/reporters/silent-reporter.d.ts +13 -0
- package/dist/commands/test/reporters/silent-reporter.js +35 -0
- package/dist/commands/test/reporters/verbose-reporter.js +3 -0
- package/dist/commands/test/test.d.ts +5 -2
- package/dist/commands/test/test.js +14 -6
- package/dist/declarations/main/main.did +12 -0
- package/dist/declarations/main/main.did.d.ts +7 -0
- package/dist/declarations/main/main.did.js +10 -0
- package/dist/package.json +1 -1
- package/dist/templates/src/lib.mo +1 -1
- package/package.json +1 -1
- package/templates/src/lib.mo +1 -1
package/README.md
CHANGED
|
@@ -17,7 +17,20 @@ npm i -g ic-mops
|
|
|
17
17
|
|
|
18
18
|
## Install Packages
|
|
19
19
|
|
|
20
|
-
### 1.
|
|
20
|
+
### 1. Configure dfx.json
|
|
21
|
+
Add `mops` as a packtool to your `dfx.json`
|
|
22
|
+
|
|
23
|
+
```json
|
|
24
|
+
{
|
|
25
|
+
"defaults": {
|
|
26
|
+
"build": {
|
|
27
|
+
"packtool": "mops sources"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### 2. Initialize
|
|
21
34
|
Run this command in the root directory of your project (where is `dfx.json` placed)
|
|
22
35
|
|
|
23
36
|
If there are Vessel config files, mops will migrate packages from `vessel.dhall` to `mops.toml`
|
|
@@ -26,7 +39,7 @@ If there are Vessel config files, mops will migrate packages from `vessel.dhall`
|
|
|
26
39
|
mops init
|
|
27
40
|
```
|
|
28
41
|
|
|
29
|
-
###
|
|
42
|
+
### 3. Install Motoko Packages
|
|
30
43
|
Use `mops add <package_name>` to install a specific package and save it to `mops.toml`
|
|
31
44
|
|
|
32
45
|
```
|
|
@@ -53,7 +66,7 @@ Use `mops install` to install all packages specified in `mops.toml`
|
|
|
53
66
|
mops install
|
|
54
67
|
```
|
|
55
68
|
|
|
56
|
-
###
|
|
69
|
+
### 4. Import Package
|
|
57
70
|
Now you can import installed packages in your Motoko code
|
|
58
71
|
|
|
59
72
|
```motoko
|
package/cli.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import fs from 'node:fs';
|
|
4
|
-
import {program, Argument} from 'commander';
|
|
4
|
+
import {program, Argument, Option} from 'commander';
|
|
5
5
|
import chalk from 'chalk';
|
|
6
6
|
import {Principal} from '@dfinity/principal';
|
|
7
7
|
|
|
@@ -100,6 +100,7 @@ program
|
|
|
100
100
|
.command('publish')
|
|
101
101
|
.description('Publish package to the mops registry')
|
|
102
102
|
.option('--no-docs', 'Do not generate docs')
|
|
103
|
+
.option('--no-test', 'Do not run tests')
|
|
103
104
|
.action(async (options) => {
|
|
104
105
|
if (!checkConfigFile()) {
|
|
105
106
|
process.exit(1);
|
|
@@ -188,14 +189,10 @@ program
|
|
|
188
189
|
program
|
|
189
190
|
.command('test [filter]')
|
|
190
191
|
.description('Run tests')
|
|
191
|
-
.
|
|
192
|
+
.addOption(new Option('-r, --reporter <reporter>', 'Test reporter').choices(['verbose', 'compact', 'files', 'silent']).default('verbose'))
|
|
193
|
+
.addOption(new Option('--mode <mode>', 'Test mode').choices(['interpreter', 'wasi']).default('interpreter'))
|
|
192
194
|
.option('-w, --watch', 'Enable watch mode')
|
|
193
|
-
.option('--mode <mode>', 'Test mode: \'interpreter\' or \'wasi\' (default \'interpreter\'')
|
|
194
195
|
.action(async (filter, options) => {
|
|
195
|
-
if (options.mode && !['interpreter', 'wasi'].includes(options.mode)) {
|
|
196
|
-
console.log(`Unknown --mode value '${options.mode}'. Allowed: interpreter, wasi`);
|
|
197
|
-
process.exit(1);
|
|
198
|
-
}
|
|
199
196
|
await test(filter, options);
|
|
200
197
|
});
|
|
201
198
|
|
package/commands/publish.ts
CHANGED
|
@@ -10,8 +10,10 @@ import {parallel} from '../parallel.js';
|
|
|
10
10
|
import {docs} from './docs.js';
|
|
11
11
|
import {DependencyV2, PackageConfigV2} from '../declarations/main/main.did.js';
|
|
12
12
|
import {Dependency} from '../types.js';
|
|
13
|
+
import {testWithReporter} from './test/test.js';
|
|
14
|
+
import {SilentReporter} from './test/reporters/silent-reporter.js';
|
|
13
15
|
|
|
14
|
-
export async function publish({
|
|
16
|
+
export async function publish(options: {docs?: boolean, test?: boolean} = {}) {
|
|
15
17
|
if (!checkConfigFile()) {
|
|
16
18
|
return;
|
|
17
19
|
}
|
|
@@ -19,24 +21,26 @@ export async function publish({noDocs = false} = {}) {
|
|
|
19
21
|
let rootDir = getRootDir();
|
|
20
22
|
let config = readConfig();
|
|
21
23
|
|
|
24
|
+
console.log(`Publishing ${config.package?.name}@${config.package?.version}`);
|
|
25
|
+
|
|
22
26
|
// validate
|
|
23
27
|
for (let key of Object.keys(config)) {
|
|
24
28
|
if (!['package', 'dependencies', 'dev-dependencies', 'scripts'].includes(key)) {
|
|
25
29
|
console.log(chalk.red('Error: ') + `Unknown config section [${key}]`);
|
|
26
|
-
|
|
30
|
+
process.exit(1);
|
|
27
31
|
}
|
|
28
32
|
}
|
|
29
33
|
|
|
30
34
|
// required fields
|
|
31
35
|
if (!config.package) {
|
|
32
36
|
console.log(chalk.red('Error: ') + 'Please specify [package] section in your mops.toml');
|
|
33
|
-
|
|
37
|
+
process.exit(1);
|
|
34
38
|
}
|
|
35
39
|
for (let key of ['name', 'version']) {
|
|
36
40
|
// @ts-ignore
|
|
37
41
|
if (!config.package[key]) {
|
|
38
42
|
console.log(chalk.red('Error: ') + `Please specify "${key}" in [config] section in your mops.toml`);
|
|
39
|
-
|
|
43
|
+
process.exit(1);
|
|
40
44
|
}
|
|
41
45
|
}
|
|
42
46
|
|
|
@@ -74,7 +78,7 @@ export async function publish({noDocs = false} = {}) {
|
|
|
74
78
|
for (let key of Object.keys(config.package)) {
|
|
75
79
|
if (!packageKeys.includes(key)) {
|
|
76
80
|
console.log(chalk.red('Error: ') + `Unknown config key 'package.${key}'`);
|
|
77
|
-
|
|
81
|
+
process.exit(1);
|
|
78
82
|
}
|
|
79
83
|
}
|
|
80
84
|
|
|
@@ -100,20 +104,20 @@ export async function publish({noDocs = false} = {}) {
|
|
|
100
104
|
// @ts-ignore
|
|
101
105
|
if (config.package[key] && config.package[key].length > max) {
|
|
102
106
|
console.log(chalk.red('Error: ') + `package.${key} value max length is ${max}`);
|
|
103
|
-
|
|
107
|
+
process.exit(1);
|
|
104
108
|
}
|
|
105
109
|
}
|
|
106
110
|
|
|
107
111
|
if (config.dependencies) {
|
|
108
112
|
if (Object.keys(config.dependencies).length > 100) {
|
|
109
113
|
console.log(chalk.red('Error: ') + 'max dependencies is 100');
|
|
110
|
-
|
|
114
|
+
process.exit(1);
|
|
111
115
|
}
|
|
112
116
|
|
|
113
117
|
for (let dep of Object.values(config.dependencies)) {
|
|
114
118
|
if (dep.path) {
|
|
115
119
|
console.log(chalk.red('Error: ') + 'you can\'t publish packages with local dependencies');
|
|
116
|
-
|
|
120
|
+
process.exit(1);
|
|
117
121
|
}
|
|
118
122
|
delete dep.path;
|
|
119
123
|
}
|
|
@@ -122,13 +126,13 @@ export async function publish({noDocs = false} = {}) {
|
|
|
122
126
|
if (config['dev-dependencies']) {
|
|
123
127
|
if (Object.keys(config['dev-dependencies']).length > 100) {
|
|
124
128
|
console.log(chalk.red('Error: ') + 'max dev-dependencies is 100');
|
|
125
|
-
|
|
129
|
+
process.exit(1);
|
|
126
130
|
}
|
|
127
131
|
|
|
128
132
|
for (let dep of Object.values(config['dev-dependencies'])) {
|
|
129
133
|
if (dep.path) {
|
|
130
134
|
console.log(chalk.red('Error: ') + 'you can\'t publish packages with local dev-dependencies');
|
|
131
|
-
|
|
135
|
+
process.exit(1);
|
|
132
136
|
}
|
|
133
137
|
delete dep.path;
|
|
134
138
|
}
|
|
@@ -197,7 +201,8 @@ export async function publish({noDocs = false} = {}) {
|
|
|
197
201
|
|
|
198
202
|
// generate docs
|
|
199
203
|
let docsFile = path.join(rootDir, '.mops/.docs/docs.tgz');
|
|
200
|
-
if (
|
|
204
|
+
if (options.docs) {
|
|
205
|
+
console.log('Generating documentation...');
|
|
201
206
|
await docs({silent: true});
|
|
202
207
|
if (fs.existsSync(docsFile)) {
|
|
203
208
|
files.unshift(docsFile);
|
|
@@ -207,18 +212,29 @@ export async function publish({noDocs = false} = {}) {
|
|
|
207
212
|
// check required files
|
|
208
213
|
if (!files.includes('mops.toml')) {
|
|
209
214
|
console.log(chalk.red('Error: ') + ' please add mops.toml file');
|
|
210
|
-
|
|
215
|
+
process.exit(1);
|
|
211
216
|
}
|
|
212
217
|
if (!files.includes('README.md')) {
|
|
213
218
|
console.log(chalk.red('Error: ') + ' please add README.md file');
|
|
214
|
-
|
|
219
|
+
process.exit(1);
|
|
215
220
|
}
|
|
216
221
|
|
|
217
222
|
// check allowed exts
|
|
218
223
|
for (let file of files) {
|
|
219
224
|
if (!minimatch(file, '**/*.{mo,did,md,toml}') && !file.toLowerCase().endsWith('license') && !file.toLowerCase().endsWith('notice') && file !== docsFile) {
|
|
220
225
|
console.log(chalk.red('Error: ') + `file ${file} has unsupported extension. Allowed: .mo, .did, .md, .toml`);
|
|
221
|
-
|
|
226
|
+
process.exit(1);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// test
|
|
231
|
+
let reporter = new SilentReporter;
|
|
232
|
+
if (options.test) {
|
|
233
|
+
console.log('Running tests...');
|
|
234
|
+
await testWithReporter(reporter);
|
|
235
|
+
if (reporter.failed > 0) {
|
|
236
|
+
console.log(chalk.red('Error: ') + 'tests failed');
|
|
237
|
+
process.exit(1);
|
|
222
238
|
}
|
|
223
239
|
}
|
|
224
240
|
|
|
@@ -227,7 +243,7 @@ export async function publish({noDocs = false} = {}) {
|
|
|
227
243
|
let step = 0;
|
|
228
244
|
function progress() {
|
|
229
245
|
step++;
|
|
230
|
-
logUpdate(`
|
|
246
|
+
logUpdate(`Uploading files ${progressBar(step, total)}`);
|
|
231
247
|
}
|
|
232
248
|
|
|
233
249
|
// upload config
|
|
@@ -241,6 +257,14 @@ export async function publish({noDocs = false} = {}) {
|
|
|
241
257
|
}
|
|
242
258
|
let puiblishingId = publishing.ok;
|
|
243
259
|
|
|
260
|
+
// upload test stats
|
|
261
|
+
if (options.test) {
|
|
262
|
+
await actor.uploadTestStats(puiblishingId, {
|
|
263
|
+
passed: BigInt(reporter.passed),
|
|
264
|
+
passedNames: reporter.passedNamesFlat,
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
|
|
244
268
|
// upload files
|
|
245
269
|
await parallel(8, files, async (file: string) => {
|
|
246
270
|
progress();
|
package/commands/test/mmf1.ts
CHANGED
|
@@ -9,6 +9,7 @@ type TestStatus = 'pass' | 'fail' | 'skip';
|
|
|
9
9
|
type MessageType = 'pass' | 'fail' | 'skip' | 'suite' | 'stdout';
|
|
10
10
|
|
|
11
11
|
export class MMF1 {
|
|
12
|
+
file: string;
|
|
12
13
|
stack: string[] = [];
|
|
13
14
|
currSuite: string = '';
|
|
14
15
|
failed = 0;
|
|
@@ -19,9 +20,16 @@ export class MMF1 {
|
|
|
19
20
|
type: MessageType;
|
|
20
21
|
message: string;
|
|
21
22
|
}[] = [];
|
|
23
|
+
nestingSymbol = ' › ';
|
|
24
|
+
// or <file>
|
|
25
|
+
// or <file> › <test>
|
|
26
|
+
// or <file> › <suite> › <test>
|
|
27
|
+
// or <file> › <suite> › <test> › <nested-test>...
|
|
28
|
+
passedNamesFlat: string[] = [];
|
|
22
29
|
|
|
23
|
-
constructor(srategy: Strategy) {
|
|
30
|
+
constructor(srategy: Strategy, file: string) {
|
|
24
31
|
this.srategy = srategy;
|
|
32
|
+
this.file = file;
|
|
25
33
|
}
|
|
26
34
|
|
|
27
35
|
_log(type: MessageType, ...args: string[]) {
|
|
@@ -90,6 +98,7 @@ export class MMF1 {
|
|
|
90
98
|
}
|
|
91
99
|
this.passed++;
|
|
92
100
|
this._log(status, ' '.repeat(this.stack.length * 2), chalk.green('✓'), name);
|
|
101
|
+
this.passedNamesFlat.push([this.file, ...this.stack, name].join(this.nestingSymbol));
|
|
93
102
|
}
|
|
94
103
|
else if (status === 'fail') {
|
|
95
104
|
this.failed++;
|
|
@@ -32,6 +32,10 @@ export class CompactReporter implements Reporter {
|
|
|
32
32
|
this.failed += mmf.failed;
|
|
33
33
|
this.skipped += mmf.skipped;
|
|
34
34
|
|
|
35
|
+
if (mmf.passed === 0 && mmf.failed === 0) {
|
|
36
|
+
this.passed++;
|
|
37
|
+
}
|
|
38
|
+
|
|
35
39
|
this.passedFiles += Number(mmf.failed === 0);
|
|
36
40
|
this.failedFiles += Number(mmf.failed !== 0);
|
|
37
41
|
|
|
@@ -0,0 +1,42 @@
|
|
|
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 SilentReporter implements Reporter {
|
|
7
|
+
passed = 0;
|
|
8
|
+
failed = 0;
|
|
9
|
+
skipped = 0;
|
|
10
|
+
passedFiles = 0;
|
|
11
|
+
failedFiles = 0;
|
|
12
|
+
passedNamesFlat: string[] = [];
|
|
13
|
+
|
|
14
|
+
addFiles(_files: string[]) {}
|
|
15
|
+
|
|
16
|
+
addRun(file: string, mmf: MMF1, state: Promise<void>, _wasiMode: boolean) {
|
|
17
|
+
state.then(() => {
|
|
18
|
+
this.passed += mmf.passed;
|
|
19
|
+
this.failed += mmf.failed;
|
|
20
|
+
this.skipped += mmf.skipped;
|
|
21
|
+
this.passedNamesFlat = [...this.passedNamesFlat, ...mmf.passedNamesFlat];
|
|
22
|
+
|
|
23
|
+
if (mmf.passed === 0 && mmf.failed === 0) {
|
|
24
|
+
this.passed++;
|
|
25
|
+
this.passedNamesFlat.push(absToRel(file));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
this.passedFiles += Number(mmf.failed === 0);
|
|
29
|
+
this.failedFiles += Number(mmf.failed !== 0);
|
|
30
|
+
|
|
31
|
+
if (mmf.failed) {
|
|
32
|
+
console.log(chalk.red('✖'), absToRel(file));
|
|
33
|
+
mmf.flush('fail');
|
|
34
|
+
console.log('-'.repeat(50));
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
done(): boolean {
|
|
40
|
+
return this.failed === 0;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -25,6 +25,10 @@ export class VerboseReporter implements Reporter {
|
|
|
25
25
|
this.failed += mmf.failed;
|
|
26
26
|
this.skipped += mmf.skipped;
|
|
27
27
|
|
|
28
|
+
if (mmf.passed === 0 && mmf.failed === 0) {
|
|
29
|
+
this.passed++;
|
|
30
|
+
}
|
|
31
|
+
|
|
28
32
|
this.#curFileIndex++ && console.log('-'.repeat(50));
|
|
29
33
|
console.log(`Running ${chalk.gray(absToRel(file))} ${wasiMode ? chalk.gray('(wasi)') : ''}`);
|
|
30
34
|
mmf.flush();
|
package/commands/test/test.ts
CHANGED
|
@@ -13,10 +13,11 @@ import {parallel} from '../../parallel.js';
|
|
|
13
13
|
|
|
14
14
|
import {MMF1} from './mmf1.js';
|
|
15
15
|
import {absToRel} from './utils.js';
|
|
16
|
+
import {Reporter} from './reporters/reporter.js';
|
|
16
17
|
import {VerboseReporter} from './reporters/verbose-reporter.js';
|
|
17
18
|
import {FilesReporter} from './reporters/files-reporter.js';
|
|
18
19
|
import {CompactReporter} from './reporters/compact-reporter.js';
|
|
19
|
-
import {
|
|
20
|
+
import {SilentReporter} from './reporters/silent-reporter.js';
|
|
20
21
|
|
|
21
22
|
let ignore = [
|
|
22
23
|
'**/node_modules/**',
|
|
@@ -30,9 +31,10 @@ let globConfig = {
|
|
|
30
31
|
ignore: ignore,
|
|
31
32
|
};
|
|
32
33
|
|
|
34
|
+
type ReporterName = 'verbose' | 'files' | 'compact' | 'silent';
|
|
33
35
|
type TestMode = 'interpreter' | 'wasi';
|
|
34
36
|
|
|
35
|
-
export async function test(filter = '', {watch = false, reporter = 'verbose', mode = 'interpreter' as TestMode} = {}) {
|
|
37
|
+
export async function test(filter = '', {watch = false, reporter = 'verbose' as ReporterName, mode = 'interpreter' as TestMode} = {}) {
|
|
36
38
|
let rootDir = getRootDir();
|
|
37
39
|
|
|
38
40
|
if (watch) {
|
|
@@ -41,7 +43,7 @@ export async function test(filter = '', {watch = false, reporter = 'verbose', mo
|
|
|
41
43
|
let run = debounce(async () => {
|
|
42
44
|
console.clear();
|
|
43
45
|
process.stdout.write('\x1Bc');
|
|
44
|
-
await runAll(filter,
|
|
46
|
+
await runAll(reporter, filter, mode);
|
|
45
47
|
console.log('-'.repeat(50));
|
|
46
48
|
console.log('Waiting for file changes...');
|
|
47
49
|
console.log(chalk.gray((`Press ${chalk.gray('Ctrl+C')} to exit.`)));
|
|
@@ -61,7 +63,7 @@ export async function test(filter = '', {watch = false, reporter = 'verbose', mo
|
|
|
61
63
|
run();
|
|
62
64
|
}
|
|
63
65
|
else {
|
|
64
|
-
let passed = await runAll(
|
|
66
|
+
let passed = await runAll(reporter, filter, mode);
|
|
65
67
|
if (!passed) {
|
|
66
68
|
process.exit(1);
|
|
67
69
|
}
|
|
@@ -70,7 +72,7 @@ export async function test(filter = '', {watch = false, reporter = 'verbose', mo
|
|
|
70
72
|
|
|
71
73
|
let mocPath = process.env.DFX_MOC_PATH;
|
|
72
74
|
|
|
73
|
-
export async function runAll(
|
|
75
|
+
export async function runAll(reporterName: ReporterName = 'verbose', filter = '', mode: TestMode = 'interpreter'): Promise<boolean> {
|
|
74
76
|
let reporter: Reporter;
|
|
75
77
|
if (reporterName == 'compact') {
|
|
76
78
|
reporter = new CompactReporter;
|
|
@@ -78,10 +80,17 @@ export async function runAll(filter = '', reporterName = 'verbose', mode: TestMo
|
|
|
78
80
|
else if (reporterName == 'files') {
|
|
79
81
|
reporter = new FilesReporter;
|
|
80
82
|
}
|
|
83
|
+
else if (reporterName == 'silent') {
|
|
84
|
+
reporter = new SilentReporter;
|
|
85
|
+
}
|
|
81
86
|
else {
|
|
82
87
|
reporter = new VerboseReporter;
|
|
83
88
|
}
|
|
89
|
+
let done = await testWithReporter(reporter, filter, mode);
|
|
90
|
+
return done;
|
|
91
|
+
}
|
|
84
92
|
|
|
93
|
+
export async function testWithReporter(reporter: Reporter, filter = '', mode: TestMode = 'interpreter'): Promise<boolean> {
|
|
85
94
|
let rootDir = getRootDir();
|
|
86
95
|
let files: string[] = [];
|
|
87
96
|
let libFiles = globSync('**/test?(s)/lib.mo', globConfig);
|
|
@@ -98,11 +107,11 @@ export async function runAll(filter = '', reporterName = 'verbose', mode: TestMo
|
|
|
98
107
|
if (!files.length) {
|
|
99
108
|
if (filter) {
|
|
100
109
|
console.log(`No test files found for filter '${filter}'`);
|
|
101
|
-
return;
|
|
110
|
+
return false;
|
|
102
111
|
}
|
|
103
112
|
console.log('No test files found');
|
|
104
113
|
console.log('Put your tests in \'test\' directory in *.test.mo files');
|
|
105
|
-
return;
|
|
114
|
+
return false;
|
|
106
115
|
}
|
|
107
116
|
|
|
108
117
|
reporter.addFiles(files);
|
|
@@ -117,7 +126,7 @@ export async function runAll(filter = '', reporterName = 'verbose', mode: TestMo
|
|
|
117
126
|
fs.mkdirSync(wasmDir, {recursive: true});
|
|
118
127
|
|
|
119
128
|
await parallel(os.cpus().length, files, async (file: string) => {
|
|
120
|
-
let mmf = new MMF1('store');
|
|
129
|
+
let mmf = new MMF1('store', absToRel(file));
|
|
121
130
|
let wasiMode = mode === 'wasi' || fs.readFileSync(file, 'utf8').startsWith('// @testmode wasi');
|
|
122
131
|
|
|
123
132
|
let promise = new Promise<void>((resolve) => {
|
|
@@ -26,6 +26,16 @@ type User =
|
|
|
26
26
|
};
|
|
27
27
|
type Time = int;
|
|
28
28
|
type Text = text;
|
|
29
|
+
type TestStats__1 =
|
|
30
|
+
record {
|
|
31
|
+
passed: nat;
|
|
32
|
+
passedNames: vec text;
|
|
33
|
+
};
|
|
34
|
+
type TestStats =
|
|
35
|
+
record {
|
|
36
|
+
passed: nat;
|
|
37
|
+
passedNames: vec text;
|
|
38
|
+
};
|
|
29
39
|
type StorageStats =
|
|
30
40
|
record {
|
|
31
41
|
cyclesBalance: nat;
|
|
@@ -133,6 +143,7 @@ type PackageDetails =
|
|
|
133
143
|
owner: principal;
|
|
134
144
|
ownerInfo: User;
|
|
135
145
|
publication: PackagePublication;
|
|
146
|
+
testStats: TestStats__1;
|
|
136
147
|
versionHistory: vec PackageSummary__1;
|
|
137
148
|
};
|
|
138
149
|
type PackageConfigV2__1 =
|
|
@@ -242,4 +253,5 @@ service : {
|
|
|
242
253
|
startPublish: (PackageConfigV2) -> (Result_1);
|
|
243
254
|
takeAirdropSnapshot: () -> () oneway;
|
|
244
255
|
uploadFileChunk: (PublishingId, FileId, nat, blob) -> (Result);
|
|
256
|
+
uploadTestStats: (PublishingId, TestStats) -> (Result);
|
|
245
257
|
}
|
|
@@ -58,6 +58,7 @@ export interface PackageDetails {
|
|
|
58
58
|
'ownerInfo' : User,
|
|
59
59
|
'owner' : Principal,
|
|
60
60
|
'deps' : Array<PackageSummary__1>,
|
|
61
|
+
'testStats' : TestStats__1,
|
|
61
62
|
'downloadsTotal' : bigint,
|
|
62
63
|
'downloadsInLast30Days' : bigint,
|
|
63
64
|
'downloadTrend' : Array<DownloadsSnapshot>,
|
|
@@ -124,6 +125,11 @@ export interface StorageStats {
|
|
|
124
125
|
'cyclesBalance' : bigint,
|
|
125
126
|
'memorySize' : bigint,
|
|
126
127
|
}
|
|
128
|
+
export interface TestStats { 'passedNames' : Array<string>, 'passed' : bigint }
|
|
129
|
+
export interface TestStats__1 {
|
|
130
|
+
'passedNames' : Array<string>,
|
|
131
|
+
'passed' : bigint,
|
|
132
|
+
}
|
|
127
133
|
export type Text = string;
|
|
128
134
|
export type Time = bigint;
|
|
129
135
|
export interface User {
|
|
@@ -206,4 +212,5 @@ export interface _SERVICE {
|
|
|
206
212
|
[PublishingId, FileId, bigint, Uint8Array | number[]],
|
|
207
213
|
Result
|
|
208
214
|
>,
|
|
215
|
+
'uploadTestStats' : ActorMethod<[PublishingId, TestStats], Result>,
|
|
209
216
|
}
|
|
@@ -84,6 +84,10 @@ export const idlFactory = ({ IDL }) => {
|
|
|
84
84
|
'config' : PackageConfigV2__1,
|
|
85
85
|
'publication' : PackagePublication,
|
|
86
86
|
});
|
|
87
|
+
const TestStats__1 = IDL.Record({
|
|
88
|
+
'passedNames' : IDL.Vec(IDL.Text),
|
|
89
|
+
'passed' : IDL.Nat,
|
|
90
|
+
});
|
|
87
91
|
const DownloadsSnapshot = IDL.Record({
|
|
88
92
|
'startTime' : Time,
|
|
89
93
|
'endTime' : Time,
|
|
@@ -93,6 +97,7 @@ export const idlFactory = ({ IDL }) => {
|
|
|
93
97
|
'ownerInfo' : User,
|
|
94
98
|
'owner' : IDL.Principal,
|
|
95
99
|
'deps' : IDL.Vec(PackageSummary__1),
|
|
100
|
+
'testStats' : TestStats__1,
|
|
96
101
|
'downloadsTotal' : IDL.Nat,
|
|
97
102
|
'downloadsInLast30Days' : IDL.Nat,
|
|
98
103
|
'downloadTrend' : IDL.Vec(DownloadsSnapshot),
|
|
@@ -145,6 +150,10 @@ export const idlFactory = ({ IDL }) => {
|
|
|
145
150
|
});
|
|
146
151
|
const PublishingErr = IDL.Text;
|
|
147
152
|
const Result_1 = IDL.Variant({ 'ok' : PublishingId, 'err' : PublishingErr });
|
|
153
|
+
const TestStats = IDL.Record({
|
|
154
|
+
'passedNames' : IDL.Vec(IDL.Text),
|
|
155
|
+
'passed' : IDL.Nat,
|
|
156
|
+
});
|
|
148
157
|
return IDL.Service({
|
|
149
158
|
'backup' : IDL.Func([], [], []),
|
|
150
159
|
'claimAirdrop' : IDL.Func([IDL.Principal], [IDL.Text], []),
|
|
@@ -237,6 +246,7 @@ export const idlFactory = ({ IDL }) => {
|
|
|
237
246
|
[Result],
|
|
238
247
|
[],
|
|
239
248
|
),
|
|
249
|
+
'uploadTestStats' : IDL.Func([PublishingId, TestStats], [Result], []),
|
|
240
250
|
});
|
|
241
251
|
};
|
|
242
252
|
export const init = ({ IDL }) => { return []; };
|
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import fs from 'node:fs';
|
|
3
|
-
import { program, Argument } from 'commander';
|
|
3
|
+
import { program, Argument, Option } from 'commander';
|
|
4
4
|
import chalk from 'chalk';
|
|
5
5
|
import { Principal } from '@dfinity/principal';
|
|
6
6
|
import { init } from './commands/init.js';
|
|
@@ -89,6 +89,7 @@ program
|
|
|
89
89
|
.command('publish')
|
|
90
90
|
.description('Publish package to the mops registry')
|
|
91
91
|
.option('--no-docs', 'Do not generate docs')
|
|
92
|
+
.option('--no-test', 'Do not run tests')
|
|
92
93
|
.action(async (options) => {
|
|
93
94
|
if (!checkConfigFile()) {
|
|
94
95
|
process.exit(1);
|
|
@@ -169,14 +170,10 @@ program
|
|
|
169
170
|
program
|
|
170
171
|
.command('test [filter]')
|
|
171
172
|
.description('Run tests')
|
|
172
|
-
.
|
|
173
|
+
.addOption(new Option('-r, --reporter <reporter>', 'Test reporter').choices(['verbose', 'compact', 'files', 'silent']).default('verbose'))
|
|
174
|
+
.addOption(new Option('--mode <mode>', 'Test mode').choices(['interpreter', 'wasi']).default('interpreter'))
|
|
173
175
|
.option('-w, --watch', 'Enable watch mode')
|
|
174
|
-
.option('--mode <mode>', 'Test mode: \'interpreter\' or \'wasi\' (default \'interpreter\'')
|
|
175
176
|
.action(async (filter, options) => {
|
|
176
|
-
if (options.mode && !['interpreter', 'wasi'].includes(options.mode)) {
|
|
177
|
-
console.log(`Unknown --mode value '${options.mode}'. Allowed: interpreter, wasi`);
|
|
178
|
-
process.exit(1);
|
|
179
|
-
}
|
|
180
177
|
await test(filter, options);
|
|
181
178
|
});
|
|
182
179
|
// template
|
package/dist/commands/publish.js
CHANGED
|
@@ -8,29 +8,32 @@ import prompts from 'prompts';
|
|
|
8
8
|
import { checkConfigFile, getRootDir, mainActor, progressBar, readConfig } from '../mops.js';
|
|
9
9
|
import { parallel } from '../parallel.js';
|
|
10
10
|
import { docs } from './docs.js';
|
|
11
|
-
|
|
11
|
+
import { testWithReporter } from './test/test.js';
|
|
12
|
+
import { SilentReporter } from './test/reporters/silent-reporter.js';
|
|
13
|
+
export async function publish(options = {}) {
|
|
12
14
|
if (!checkConfigFile()) {
|
|
13
15
|
return;
|
|
14
16
|
}
|
|
15
17
|
let rootDir = getRootDir();
|
|
16
18
|
let config = readConfig();
|
|
19
|
+
console.log(`Publishing ${config.package?.name}@${config.package?.version}`);
|
|
17
20
|
// validate
|
|
18
21
|
for (let key of Object.keys(config)) {
|
|
19
22
|
if (!['package', 'dependencies', 'dev-dependencies', 'scripts'].includes(key)) {
|
|
20
23
|
console.log(chalk.red('Error: ') + `Unknown config section [${key}]`);
|
|
21
|
-
|
|
24
|
+
process.exit(1);
|
|
22
25
|
}
|
|
23
26
|
}
|
|
24
27
|
// required fields
|
|
25
28
|
if (!config.package) {
|
|
26
29
|
console.log(chalk.red('Error: ') + 'Please specify [package] section in your mops.toml');
|
|
27
|
-
|
|
30
|
+
process.exit(1);
|
|
28
31
|
}
|
|
29
32
|
for (let key of ['name', 'version']) {
|
|
30
33
|
// @ts-ignore
|
|
31
34
|
if (!config.package[key]) {
|
|
32
35
|
console.log(chalk.red('Error: ') + `Please specify "${key}" in [config] section in your mops.toml`);
|
|
33
|
-
|
|
36
|
+
process.exit(1);
|
|
34
37
|
}
|
|
35
38
|
}
|
|
36
39
|
// desired fields
|
|
@@ -66,7 +69,7 @@ export async function publish({ noDocs = false } = {}) {
|
|
|
66
69
|
for (let key of Object.keys(config.package)) {
|
|
67
70
|
if (!packageKeys.includes(key)) {
|
|
68
71
|
console.log(chalk.red('Error: ') + `Unknown config key 'package.${key}'`);
|
|
69
|
-
|
|
72
|
+
process.exit(1);
|
|
70
73
|
}
|
|
71
74
|
}
|
|
72
75
|
// check lengths
|
|
@@ -90,18 +93,18 @@ export async function publish({ noDocs = false } = {}) {
|
|
|
90
93
|
// @ts-ignore
|
|
91
94
|
if (config.package[key] && config.package[key].length > max) {
|
|
92
95
|
console.log(chalk.red('Error: ') + `package.${key} value max length is ${max}`);
|
|
93
|
-
|
|
96
|
+
process.exit(1);
|
|
94
97
|
}
|
|
95
98
|
}
|
|
96
99
|
if (config.dependencies) {
|
|
97
100
|
if (Object.keys(config.dependencies).length > 100) {
|
|
98
101
|
console.log(chalk.red('Error: ') + 'max dependencies is 100');
|
|
99
|
-
|
|
102
|
+
process.exit(1);
|
|
100
103
|
}
|
|
101
104
|
for (let dep of Object.values(config.dependencies)) {
|
|
102
105
|
if (dep.path) {
|
|
103
106
|
console.log(chalk.red('Error: ') + 'you can\'t publish packages with local dependencies');
|
|
104
|
-
|
|
107
|
+
process.exit(1);
|
|
105
108
|
}
|
|
106
109
|
delete dep.path;
|
|
107
110
|
}
|
|
@@ -109,12 +112,12 @@ export async function publish({ noDocs = false } = {}) {
|
|
|
109
112
|
if (config['dev-dependencies']) {
|
|
110
113
|
if (Object.keys(config['dev-dependencies']).length > 100) {
|
|
111
114
|
console.log(chalk.red('Error: ') + 'max dev-dependencies is 100');
|
|
112
|
-
|
|
115
|
+
process.exit(1);
|
|
113
116
|
}
|
|
114
117
|
for (let dep of Object.values(config['dev-dependencies'])) {
|
|
115
118
|
if (dep.path) {
|
|
116
119
|
console.log(chalk.red('Error: ') + 'you can\'t publish packages with local dev-dependencies');
|
|
117
|
-
|
|
120
|
+
process.exit(1);
|
|
118
121
|
}
|
|
119
122
|
delete dep.path;
|
|
120
123
|
}
|
|
@@ -177,7 +180,8 @@ export async function publish({ noDocs = false } = {}) {
|
|
|
177
180
|
files = globbySync([...files, ...defaultFiles]);
|
|
178
181
|
// generate docs
|
|
179
182
|
let docsFile = path.join(rootDir, '.mops/.docs/docs.tgz');
|
|
180
|
-
if (
|
|
183
|
+
if (options.docs) {
|
|
184
|
+
console.log('Generating documentation...');
|
|
181
185
|
await docs({ silent: true });
|
|
182
186
|
if (fs.existsSync(docsFile)) {
|
|
183
187
|
files.unshift(docsFile);
|
|
@@ -186,17 +190,27 @@ export async function publish({ noDocs = false } = {}) {
|
|
|
186
190
|
// check required files
|
|
187
191
|
if (!files.includes('mops.toml')) {
|
|
188
192
|
console.log(chalk.red('Error: ') + ' please add mops.toml file');
|
|
189
|
-
|
|
193
|
+
process.exit(1);
|
|
190
194
|
}
|
|
191
195
|
if (!files.includes('README.md')) {
|
|
192
196
|
console.log(chalk.red('Error: ') + ' please add README.md file');
|
|
193
|
-
|
|
197
|
+
process.exit(1);
|
|
194
198
|
}
|
|
195
199
|
// check allowed exts
|
|
196
200
|
for (let file of files) {
|
|
197
201
|
if (!minimatch(file, '**/*.{mo,did,md,toml}') && !file.toLowerCase().endsWith('license') && !file.toLowerCase().endsWith('notice') && file !== docsFile) {
|
|
198
202
|
console.log(chalk.red('Error: ') + `file ${file} has unsupported extension. Allowed: .mo, .did, .md, .toml`);
|
|
199
|
-
|
|
203
|
+
process.exit(1);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
// test
|
|
207
|
+
let reporter = new SilentReporter;
|
|
208
|
+
if (options.test) {
|
|
209
|
+
console.log('Running tests...');
|
|
210
|
+
await testWithReporter(reporter);
|
|
211
|
+
if (reporter.failed > 0) {
|
|
212
|
+
console.log(chalk.red('Error: ') + 'tests failed');
|
|
213
|
+
process.exit(1);
|
|
200
214
|
}
|
|
201
215
|
}
|
|
202
216
|
// progress
|
|
@@ -204,7 +218,7 @@ export async function publish({ noDocs = false } = {}) {
|
|
|
204
218
|
let step = 0;
|
|
205
219
|
function progress() {
|
|
206
220
|
step++;
|
|
207
|
-
logUpdate(`
|
|
221
|
+
logUpdate(`Uploading files ${progressBar(step, total)}`);
|
|
208
222
|
}
|
|
209
223
|
// upload config
|
|
210
224
|
let actor = await mainActor(true);
|
|
@@ -215,6 +229,13 @@ export async function publish({ noDocs = false } = {}) {
|
|
|
215
229
|
return;
|
|
216
230
|
}
|
|
217
231
|
let puiblishingId = publishing.ok;
|
|
232
|
+
// upload test stats
|
|
233
|
+
if (options.test) {
|
|
234
|
+
await actor.uploadTestStats(puiblishingId, {
|
|
235
|
+
passed: BigInt(reporter.passed),
|
|
236
|
+
passedNames: reporter.passedNamesFlat,
|
|
237
|
+
});
|
|
238
|
+
}
|
|
218
239
|
// upload files
|
|
219
240
|
await parallel(8, files, async (file) => {
|
|
220
241
|
progress();
|
|
@@ -2,6 +2,7 @@ type Strategy = 'store' | 'print';
|
|
|
2
2
|
type TestStatus = 'pass' | 'fail' | 'skip';
|
|
3
3
|
type MessageType = 'pass' | 'fail' | 'skip' | 'suite' | 'stdout';
|
|
4
4
|
export declare class MMF1 {
|
|
5
|
+
file: string;
|
|
5
6
|
stack: string[];
|
|
6
7
|
currSuite: string;
|
|
7
8
|
failed: number;
|
|
@@ -12,7 +13,9 @@ export declare class MMF1 {
|
|
|
12
13
|
type: MessageType;
|
|
13
14
|
message: string;
|
|
14
15
|
}[];
|
|
15
|
-
|
|
16
|
+
nestingSymbol: string;
|
|
17
|
+
passedNamesFlat: string[];
|
|
18
|
+
constructor(srategy: Strategy, file: string);
|
|
16
19
|
_log(type: MessageType, ...args: string[]): void;
|
|
17
20
|
flush(messageType?: MessageType): void;
|
|
18
21
|
parseLine(line: string): void;
|
|
@@ -4,14 +4,21 @@
|
|
|
4
4
|
// mops:1:skip
|
|
5
5
|
import chalk from 'chalk';
|
|
6
6
|
export class MMF1 {
|
|
7
|
-
constructor(srategy) {
|
|
7
|
+
constructor(srategy, file) {
|
|
8
8
|
this.stack = [];
|
|
9
9
|
this.currSuite = '';
|
|
10
10
|
this.failed = 0;
|
|
11
11
|
this.passed = 0;
|
|
12
12
|
this.skipped = 0;
|
|
13
13
|
this.output = [];
|
|
14
|
+
this.nestingSymbol = ' › ';
|
|
15
|
+
// or <file>
|
|
16
|
+
// or <file> › <test>
|
|
17
|
+
// or <file> › <suite> › <test>
|
|
18
|
+
// or <file> › <suite> › <test> › <nested-test>...
|
|
19
|
+
this.passedNamesFlat = [];
|
|
14
20
|
this.srategy = srategy;
|
|
21
|
+
this.file = file;
|
|
15
22
|
}
|
|
16
23
|
_log(type, ...args) {
|
|
17
24
|
if (this.srategy === 'store') {
|
|
@@ -73,6 +80,7 @@ export class MMF1 {
|
|
|
73
80
|
}
|
|
74
81
|
this.passed++;
|
|
75
82
|
this._log(status, ' '.repeat(this.stack.length * 2), chalk.green('✓'), name);
|
|
83
|
+
this.passedNamesFlat.push([this.file, ...this.stack, name].join(this.nestingSymbol));
|
|
76
84
|
}
|
|
77
85
|
else if (status === 'fail') {
|
|
78
86
|
this.failed++;
|
|
@@ -40,6 +40,9 @@ export class CompactReporter {
|
|
|
40
40
|
this.passed += mmf.passed;
|
|
41
41
|
this.failed += mmf.failed;
|
|
42
42
|
this.skipped += mmf.skipped;
|
|
43
|
+
if (mmf.passed === 0 && mmf.failed === 0) {
|
|
44
|
+
this.passed++;
|
|
45
|
+
}
|
|
43
46
|
this.passedFiles += Number(mmf.failed === 0);
|
|
44
47
|
this.failedFiles += Number(mmf.failed !== 0);
|
|
45
48
|
if (mmf.failed) {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { MMF1 } from '../mmf1.js';
|
|
2
|
+
import { Reporter } from './reporter.js';
|
|
3
|
+
export declare class SilentReporter implements Reporter {
|
|
4
|
+
passed: number;
|
|
5
|
+
failed: number;
|
|
6
|
+
skipped: number;
|
|
7
|
+
passedFiles: number;
|
|
8
|
+
failedFiles: number;
|
|
9
|
+
passedNamesFlat: string[];
|
|
10
|
+
addFiles(_files: string[]): void;
|
|
11
|
+
addRun(file: string, mmf: MMF1, state: Promise<void>, _wasiMode: boolean): void;
|
|
12
|
+
done(): boolean;
|
|
13
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { absToRel } from '../utils.js';
|
|
3
|
+
export class SilentReporter {
|
|
4
|
+
constructor() {
|
|
5
|
+
this.passed = 0;
|
|
6
|
+
this.failed = 0;
|
|
7
|
+
this.skipped = 0;
|
|
8
|
+
this.passedFiles = 0;
|
|
9
|
+
this.failedFiles = 0;
|
|
10
|
+
this.passedNamesFlat = [];
|
|
11
|
+
}
|
|
12
|
+
addFiles(_files) { }
|
|
13
|
+
addRun(file, mmf, state, _wasiMode) {
|
|
14
|
+
state.then(() => {
|
|
15
|
+
this.passed += mmf.passed;
|
|
16
|
+
this.failed += mmf.failed;
|
|
17
|
+
this.skipped += mmf.skipped;
|
|
18
|
+
this.passedNamesFlat = [...this.passedNamesFlat, ...mmf.passedNamesFlat];
|
|
19
|
+
if (mmf.passed === 0 && mmf.failed === 0) {
|
|
20
|
+
this.passed++;
|
|
21
|
+
this.passedNamesFlat.push(absToRel(file));
|
|
22
|
+
}
|
|
23
|
+
this.passedFiles += Number(mmf.failed === 0);
|
|
24
|
+
this.failedFiles += Number(mmf.failed !== 0);
|
|
25
|
+
if (mmf.failed) {
|
|
26
|
+
console.log(chalk.red('✖'), absToRel(file));
|
|
27
|
+
mmf.flush('fail');
|
|
28
|
+
console.log('-'.repeat(50));
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
done() {
|
|
33
|
+
return this.failed === 0;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -33,6 +33,9 @@ export class VerboseReporter {
|
|
|
33
33
|
this.passed += mmf.passed;
|
|
34
34
|
this.failed += mmf.failed;
|
|
35
35
|
this.skipped += mmf.skipped;
|
|
36
|
+
if (mmf.passed === 0 && mmf.failed === 0) {
|
|
37
|
+
this.passed++;
|
|
38
|
+
}
|
|
36
39
|
(__classPrivateFieldSet(this, _VerboseReporter_curFileIndex, (_b = __classPrivateFieldGet(this, _VerboseReporter_curFileIndex, "f"), _a = _b++, _b), "f"), _a) && console.log('-'.repeat(50));
|
|
37
40
|
console.log(`Running ${chalk.gray(absToRel(file))} ${wasiMode ? chalk.gray('(wasi)') : ''}`);
|
|
38
41
|
mmf.flush();
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
import { Reporter } from './reporters/reporter.js';
|
|
2
|
+
type ReporterName = 'verbose' | 'files' | 'compact' | 'silent';
|
|
1
3
|
type TestMode = 'interpreter' | 'wasi';
|
|
2
4
|
export declare function test(filter?: string, { watch, reporter, mode }?: {
|
|
3
5
|
watch?: boolean | undefined;
|
|
4
|
-
reporter?:
|
|
6
|
+
reporter?: ReporterName | undefined;
|
|
5
7
|
mode?: TestMode | undefined;
|
|
6
8
|
}): Promise<void>;
|
|
7
|
-
export declare function runAll(
|
|
9
|
+
export declare function runAll(reporterName?: ReporterName, filter?: string, mode?: TestMode): Promise<boolean>;
|
|
10
|
+
export declare function testWithReporter(reporter: Reporter, filter?: string, mode?: TestMode): Promise<boolean>;
|
|
8
11
|
export {};
|
|
@@ -14,6 +14,7 @@ import { absToRel } from './utils.js';
|
|
|
14
14
|
import { VerboseReporter } from './reporters/verbose-reporter.js';
|
|
15
15
|
import { FilesReporter } from './reporters/files-reporter.js';
|
|
16
16
|
import { CompactReporter } from './reporters/compact-reporter.js';
|
|
17
|
+
import { SilentReporter } from './reporters/silent-reporter.js';
|
|
17
18
|
let ignore = [
|
|
18
19
|
'**/node_modules/**',
|
|
19
20
|
'**/.mops/**',
|
|
@@ -32,7 +33,7 @@ export async function test(filter = '', { watch = false, reporter = 'verbose', m
|
|
|
32
33
|
let run = debounce(async () => {
|
|
33
34
|
console.clear();
|
|
34
35
|
process.stdout.write('\x1Bc');
|
|
35
|
-
await runAll(filter,
|
|
36
|
+
await runAll(reporter, filter, mode);
|
|
36
37
|
console.log('-'.repeat(50));
|
|
37
38
|
console.log('Waiting for file changes...');
|
|
38
39
|
console.log(chalk.gray((`Press ${chalk.gray('Ctrl+C')} to exit.`)));
|
|
@@ -50,14 +51,14 @@ export async function test(filter = '', { watch = false, reporter = 'verbose', m
|
|
|
50
51
|
run();
|
|
51
52
|
}
|
|
52
53
|
else {
|
|
53
|
-
let passed = await runAll(
|
|
54
|
+
let passed = await runAll(reporter, filter, mode);
|
|
54
55
|
if (!passed) {
|
|
55
56
|
process.exit(1);
|
|
56
57
|
}
|
|
57
58
|
}
|
|
58
59
|
}
|
|
59
60
|
let mocPath = process.env.DFX_MOC_PATH;
|
|
60
|
-
export async function runAll(
|
|
61
|
+
export async function runAll(reporterName = 'verbose', filter = '', mode = 'interpreter') {
|
|
61
62
|
let reporter;
|
|
62
63
|
if (reporterName == 'compact') {
|
|
63
64
|
reporter = new CompactReporter;
|
|
@@ -65,9 +66,16 @@ export async function runAll(filter = '', reporterName = 'verbose', mode = 'inte
|
|
|
65
66
|
else if (reporterName == 'files') {
|
|
66
67
|
reporter = new FilesReporter;
|
|
67
68
|
}
|
|
69
|
+
else if (reporterName == 'silent') {
|
|
70
|
+
reporter = new SilentReporter;
|
|
71
|
+
}
|
|
68
72
|
else {
|
|
69
73
|
reporter = new VerboseReporter;
|
|
70
74
|
}
|
|
75
|
+
let done = await testWithReporter(reporter, filter, mode);
|
|
76
|
+
return done;
|
|
77
|
+
}
|
|
78
|
+
export async function testWithReporter(reporter, filter = '', mode = 'interpreter') {
|
|
71
79
|
let rootDir = getRootDir();
|
|
72
80
|
let files = [];
|
|
73
81
|
let libFiles = globSync('**/test?(s)/lib.mo', globConfig);
|
|
@@ -84,11 +92,11 @@ export async function runAll(filter = '', reporterName = 'verbose', mode = 'inte
|
|
|
84
92
|
if (!files.length) {
|
|
85
93
|
if (filter) {
|
|
86
94
|
console.log(`No test files found for filter '${filter}'`);
|
|
87
|
-
return;
|
|
95
|
+
return false;
|
|
88
96
|
}
|
|
89
97
|
console.log('No test files found');
|
|
90
98
|
console.log('Put your tests in \'test\' directory in *.test.mo files');
|
|
91
|
-
return;
|
|
99
|
+
return false;
|
|
92
100
|
}
|
|
93
101
|
reporter.addFiles(files);
|
|
94
102
|
let sourcesArr = await sources();
|
|
@@ -98,7 +106,7 @@ export async function runAll(filter = '', reporterName = 'verbose', mode = 'inte
|
|
|
98
106
|
let wasmDir = `${getRootDir()}/.mops/.test/`;
|
|
99
107
|
fs.mkdirSync(wasmDir, { recursive: true });
|
|
100
108
|
await parallel(os.cpus().length, files, async (file) => {
|
|
101
|
-
let mmf = new MMF1('store');
|
|
109
|
+
let mmf = new MMF1('store', absToRel(file));
|
|
102
110
|
let wasiMode = mode === 'wasi' || fs.readFileSync(file, 'utf8').startsWith('// @testmode wasi');
|
|
103
111
|
let promise = new Promise((resolve) => {
|
|
104
112
|
if (!mocPath) {
|
|
@@ -26,6 +26,16 @@ type User =
|
|
|
26
26
|
};
|
|
27
27
|
type Time = int;
|
|
28
28
|
type Text = text;
|
|
29
|
+
type TestStats__1 =
|
|
30
|
+
record {
|
|
31
|
+
passed: nat;
|
|
32
|
+
passedNames: vec text;
|
|
33
|
+
};
|
|
34
|
+
type TestStats =
|
|
35
|
+
record {
|
|
36
|
+
passed: nat;
|
|
37
|
+
passedNames: vec text;
|
|
38
|
+
};
|
|
29
39
|
type StorageStats =
|
|
30
40
|
record {
|
|
31
41
|
cyclesBalance: nat;
|
|
@@ -133,6 +143,7 @@ type PackageDetails =
|
|
|
133
143
|
owner: principal;
|
|
134
144
|
ownerInfo: User;
|
|
135
145
|
publication: PackagePublication;
|
|
146
|
+
testStats: TestStats__1;
|
|
136
147
|
versionHistory: vec PackageSummary__1;
|
|
137
148
|
};
|
|
138
149
|
type PackageConfigV2__1 =
|
|
@@ -242,4 +253,5 @@ service : {
|
|
|
242
253
|
startPublish: (PackageConfigV2) -> (Result_1);
|
|
243
254
|
takeAirdropSnapshot: () -> () oneway;
|
|
244
255
|
uploadFileChunk: (PublishingId, FileId, nat, blob) -> (Result);
|
|
256
|
+
uploadTestStats: (PublishingId, TestStats) -> (Result);
|
|
245
257
|
}
|
|
@@ -58,6 +58,7 @@ export interface PackageDetails {
|
|
|
58
58
|
'ownerInfo' : User,
|
|
59
59
|
'owner' : Principal,
|
|
60
60
|
'deps' : Array<PackageSummary__1>,
|
|
61
|
+
'testStats' : TestStats__1,
|
|
61
62
|
'downloadsTotal' : bigint,
|
|
62
63
|
'downloadsInLast30Days' : bigint,
|
|
63
64
|
'downloadTrend' : Array<DownloadsSnapshot>,
|
|
@@ -124,6 +125,11 @@ export interface StorageStats {
|
|
|
124
125
|
'cyclesBalance' : bigint,
|
|
125
126
|
'memorySize' : bigint,
|
|
126
127
|
}
|
|
128
|
+
export interface TestStats { 'passedNames' : Array<string>, 'passed' : bigint }
|
|
129
|
+
export interface TestStats__1 {
|
|
130
|
+
'passedNames' : Array<string>,
|
|
131
|
+
'passed' : bigint,
|
|
132
|
+
}
|
|
127
133
|
export type Text = string;
|
|
128
134
|
export type Time = bigint;
|
|
129
135
|
export interface User {
|
|
@@ -206,4 +212,5 @@ export interface _SERVICE {
|
|
|
206
212
|
[PublishingId, FileId, bigint, Uint8Array | number[]],
|
|
207
213
|
Result
|
|
208
214
|
>,
|
|
215
|
+
'uploadTestStats' : ActorMethod<[PublishingId, TestStats], Result>,
|
|
209
216
|
}
|
|
@@ -84,6 +84,10 @@ export const idlFactory = ({ IDL }) => {
|
|
|
84
84
|
'config' : PackageConfigV2__1,
|
|
85
85
|
'publication' : PackagePublication,
|
|
86
86
|
});
|
|
87
|
+
const TestStats__1 = IDL.Record({
|
|
88
|
+
'passedNames' : IDL.Vec(IDL.Text),
|
|
89
|
+
'passed' : IDL.Nat,
|
|
90
|
+
});
|
|
87
91
|
const DownloadsSnapshot = IDL.Record({
|
|
88
92
|
'startTime' : Time,
|
|
89
93
|
'endTime' : Time,
|
|
@@ -93,6 +97,7 @@ export const idlFactory = ({ IDL }) => {
|
|
|
93
97
|
'ownerInfo' : User,
|
|
94
98
|
'owner' : IDL.Principal,
|
|
95
99
|
'deps' : IDL.Vec(PackageSummary__1),
|
|
100
|
+
'testStats' : TestStats__1,
|
|
96
101
|
'downloadsTotal' : IDL.Nat,
|
|
97
102
|
'downloadsInLast30Days' : IDL.Nat,
|
|
98
103
|
'downloadTrend' : IDL.Vec(DownloadsSnapshot),
|
|
@@ -145,6 +150,10 @@ export const idlFactory = ({ IDL }) => {
|
|
|
145
150
|
});
|
|
146
151
|
const PublishingErr = IDL.Text;
|
|
147
152
|
const Result_1 = IDL.Variant({ 'ok' : PublishingId, 'err' : PublishingErr });
|
|
153
|
+
const TestStats = IDL.Record({
|
|
154
|
+
'passedNames' : IDL.Vec(IDL.Text),
|
|
155
|
+
'passed' : IDL.Nat,
|
|
156
|
+
});
|
|
148
157
|
return IDL.Service({
|
|
149
158
|
'backup' : IDL.Func([], [], []),
|
|
150
159
|
'claimAirdrop' : IDL.Func([IDL.Principal], [IDL.Text], []),
|
|
@@ -237,6 +246,7 @@ export const idlFactory = ({ IDL }) => {
|
|
|
237
246
|
[Result],
|
|
238
247
|
[],
|
|
239
248
|
),
|
|
249
|
+
'uploadTestStats' : IDL.Func([PublishingId, TestStats], [Result], []),
|
|
240
250
|
});
|
|
241
251
|
};
|
|
242
252
|
export const init = ({ IDL }) => { return []; };
|
package/dist/package.json
CHANGED
package/package.json
CHANGED