@milaboratories/pl-middle-layer 1.19.0 → 1.19.2
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@milaboratories/pl-middle-layer",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.2",
|
|
4
4
|
"description": "Pl Middle Layer",
|
|
5
5
|
"types": "./dist/index.d.ts",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -28,19 +28,19 @@
|
|
|
28
28
|
"utility-types": "^3.11.0",
|
|
29
29
|
"yaml": "^2.6.1",
|
|
30
30
|
"zod": "^3.23.8",
|
|
31
|
-
"@milaboratories/computable": "^2.3.2",
|
|
32
31
|
"@milaboratories/resolve-helper": "^1.0.2",
|
|
33
|
-
"@milaboratories/
|
|
34
|
-
"@
|
|
35
|
-
"@
|
|
36
|
-
"@platforma-sdk/model": "^1.14.1",
|
|
37
|
-
"@platforma-sdk/workflow-tengo": "2.5.0",
|
|
32
|
+
"@milaboratories/computable": "^2.3.3",
|
|
33
|
+
"@platforma-sdk/block-tools": "^2.4.1",
|
|
34
|
+
"@milaboratories/pl-client": "^2.6.3",
|
|
38
35
|
"@milaboratories/pl-model-middle-layer": "^1.6.0",
|
|
39
|
-
"@milaboratories/pl-config": "^1.3.1",
|
|
40
|
-
"@milaboratories/pl-local": "^1.8.0",
|
|
41
|
-
"@milaboratories/ts-helpers": "^1.1.1",
|
|
42
36
|
"@milaboratories/pl-model-common": "^1.8.0",
|
|
43
|
-
"@milaboratories/pl-tree": "^1.4.
|
|
37
|
+
"@milaboratories/pl-tree": "^1.4.18",
|
|
38
|
+
"@platforma-sdk/model": "^1.14.1",
|
|
39
|
+
"@milaboratories/ts-helpers": "^1.1.2",
|
|
40
|
+
"@milaboratories/pl-drivers": "^1.4.0",
|
|
41
|
+
"@milaboratories/pl-config": "^1.3.2",
|
|
42
|
+
"@platforma-sdk/workflow-tengo": "2.6.0",
|
|
43
|
+
"@milaboratories/pl-local": "^1.8.1"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"typescript": "~5.5.4",
|
|
@@ -1,7 +1,58 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { awaitBlockDone, withMl } from './middle_layer.test';
|
|
3
|
-
import { getQuickJS, Scope, shouldInterruptAfterDeadline } from 'quickjs-emscripten';
|
|
1
|
+
import { expect, test } from '@jest/globals';
|
|
4
2
|
import * as tp from 'node:timers/promises';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { getQuickJS, Scope, shouldInterruptAfterDeadline } from 'quickjs-emscripten';
|
|
5
|
+
import { randomUUID } from 'node:crypto';
|
|
6
|
+
import { MiddleLayer } from './middle_layer';
|
|
7
|
+
import { PlClient, TestHelpers } from '@milaboratories/pl-client';
|
|
8
|
+
import { Project } from './project';
|
|
9
|
+
|
|
10
|
+
export async function withMl(
|
|
11
|
+
cb: (ml: MiddleLayer, workFolder: string) => Promise<void>
|
|
12
|
+
): Promise<void> {
|
|
13
|
+
const workFolder = path.resolve(`work/${randomUUID()}`);
|
|
14
|
+
|
|
15
|
+
await TestHelpers.withTempRoot(async (pl: PlClient) => {
|
|
16
|
+
const ml = await MiddleLayer.init(pl, workFolder, {
|
|
17
|
+
defaultTreeOptions: { pollingInterval: 250, stopPollingDelay: 500 },
|
|
18
|
+
devBlockUpdateRecheckInterval: 300,
|
|
19
|
+
localSecret: MiddleLayer.generateLocalSecret(),
|
|
20
|
+
localProjections: [], // TODO must be different with local pl
|
|
21
|
+
openFileDialogCallback: () => {
|
|
22
|
+
throw new Error('Not implemented.');
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
try {
|
|
26
|
+
await cb(ml, workFolder);
|
|
27
|
+
} finally {
|
|
28
|
+
await ml.close();
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export async function awaitBlockDone(prj: Project, blockId: string, timeout: number = 2000) {
|
|
34
|
+
const abortSignal = AbortSignal.timeout(timeout);
|
|
35
|
+
const overview = prj.overview;
|
|
36
|
+
const state = prj.getBlockState(blockId);
|
|
37
|
+
// const stateAndOverview = Computable.make(() => ({ overview, state: undefined }));
|
|
38
|
+
while (true) {
|
|
39
|
+
// const {
|
|
40
|
+
// overview: overviewSnapshot,
|
|
41
|
+
// state: stateSnapshot
|
|
42
|
+
// } = await stateAndOverview.getValue();
|
|
43
|
+
const overviewSnapshot = (await overview.getValue())!;
|
|
44
|
+
const blockOverview = overviewSnapshot.blocks.find((b) => b.id == blockId);
|
|
45
|
+
if (blockOverview === undefined) throw new Error(`Blocks not found: ${blockId}`);
|
|
46
|
+
if (blockOverview.outputErrors) return;
|
|
47
|
+
if (blockOverview.calculationStatus === 'Done') return;
|
|
48
|
+
try {
|
|
49
|
+
await overview.awaitChange(abortSignal);
|
|
50
|
+
} catch (e: any) {
|
|
51
|
+
console.dir(await state.getValue(), { depth: 5 });
|
|
52
|
+
throw new Error('Aborted.', { cause: e });
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
5
56
|
|
|
6
57
|
test('test JS render enter numbers', async () => {
|
|
7
58
|
await withMl(async (ml) => {
|
|
@@ -1,726 +0,0 @@
|
|
|
1
|
-
import { test, expect } from '@jest/globals';
|
|
2
|
-
import { TestHelpers } from '@milaboratories/pl-client';
|
|
3
|
-
import { MiddleLayer } from './middle_layer';
|
|
4
|
-
import { outputRef } from '../model/args';
|
|
5
|
-
import { randomUUID } from 'node:crypto';
|
|
6
|
-
import path from 'node:path';
|
|
7
|
-
import fs from 'node:fs';
|
|
8
|
-
import { BlockPackRegistry, CentralBlockRegistry, getDevV1PacketMtime } from '../block_registry';
|
|
9
|
-
import { LocalBlobHandleAndSize, RemoteBlobHandleAndSize } from '@milaboratories/pl-model-common';
|
|
10
|
-
import { Project } from './project';
|
|
11
|
-
import { LegacyDevBlockPackConfig } from '../dev_env';
|
|
12
|
-
import { V2RegistryProvider } from '../block_registry/registry-v2-provider';
|
|
13
|
-
import { Agent } from 'undici';
|
|
14
|
-
import { BlockPackSpec } from '@milaboratories/pl-model-middle-layer';
|
|
15
|
-
|
|
16
|
-
const registry = new BlockPackRegistry(new V2RegistryProvider(new Agent()), [
|
|
17
|
-
{ id: 'central', spec: CentralBlockRegistry },
|
|
18
|
-
{
|
|
19
|
-
id: 'dev',
|
|
20
|
-
title: 'Local dev registry',
|
|
21
|
-
spec: {
|
|
22
|
-
type: 'local-dev',
|
|
23
|
-
path: path.resolve('./integration')
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
]);
|
|
27
|
-
|
|
28
|
-
async function getStandardBlockSpecs() {
|
|
29
|
-
return {
|
|
30
|
-
enterNumbersSpecFromRemote: {
|
|
31
|
-
type: 'from-registry-v1',
|
|
32
|
-
registryUrl: 'https://block.registry.platforma.bio/releases',
|
|
33
|
-
id: { organization: 'milaboratory', name: 'enter-numbers', version: '1.1.1' }
|
|
34
|
-
} satisfies BlockPackSpec,
|
|
35
|
-
enterNumbersSpecFromDev: {
|
|
36
|
-
type: 'dev-v1',
|
|
37
|
-
folder: path.resolve('./integration/block-beta-enter-numbers'),
|
|
38
|
-
mtime: '1727099575979300176'
|
|
39
|
-
} satisfies BlockPackSpec,
|
|
40
|
-
sumNumbersSpecFromRemote: {
|
|
41
|
-
type: 'from-registry-v1',
|
|
42
|
-
registryUrl: 'https://block.registry.platforma.bio/releases',
|
|
43
|
-
id: { organization: 'milaboratory', name: 'sum-numbers', version: '1.0.1' }
|
|
44
|
-
} satisfies BlockPackSpec,
|
|
45
|
-
sumNumbersSpecFromDev: {
|
|
46
|
-
type: 'dev-v1',
|
|
47
|
-
folder: path.resolve('./integration/block-beta-sum-numbers'),
|
|
48
|
-
mtime: '1727099575981045820'
|
|
49
|
-
} satisfies BlockPackSpec,
|
|
50
|
-
downloadFileSpecFromRemote: {
|
|
51
|
-
type: 'from-registry-v1',
|
|
52
|
-
registryUrl: 'https://block.registry.platforma.bio/releases',
|
|
53
|
-
id: { organization: 'milaboratory', name: 'download-file', version: '1.2.0' }
|
|
54
|
-
} satisfies BlockPackSpec,
|
|
55
|
-
uploadFileSpecFromRemote: {
|
|
56
|
-
type: 'from-registry-v1',
|
|
57
|
-
registryUrl: 'https://block.registry.platforma.bio/releases',
|
|
58
|
-
id: { organization: 'milaboratory', name: 'upload-file', version: '1.0.7' }
|
|
59
|
-
} satisfies BlockPackSpec,
|
|
60
|
-
readLogsSpecFromRemote: {
|
|
61
|
-
type: 'from-registry-v1',
|
|
62
|
-
registryUrl: 'https://block.registry.platforma.bio/releases',
|
|
63
|
-
id: { organization: 'milaboratory', name: 'read-logs', version: '1.0.7' }
|
|
64
|
-
} satisfies BlockPackSpec
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export async function withMl(
|
|
69
|
-
cb: (ml: MiddleLayer, workFolder: string) => Promise<void>
|
|
70
|
-
): Promise<void> {
|
|
71
|
-
const workFolder = path.resolve(`work/${randomUUID()}`);
|
|
72
|
-
|
|
73
|
-
await TestHelpers.withTempRoot(async (pl) => {
|
|
74
|
-
const ml = await MiddleLayer.init(pl, workFolder, {
|
|
75
|
-
defaultTreeOptions: { pollingInterval: 250, stopPollingDelay: 500 },
|
|
76
|
-
devBlockUpdateRecheckInterval: 300,
|
|
77
|
-
localSecret: MiddleLayer.generateLocalSecret(),
|
|
78
|
-
localProjections: [], // TODO must be different with local pl
|
|
79
|
-
openFileDialogCallback: () => {
|
|
80
|
-
throw new Error('Not implemented.');
|
|
81
|
-
}
|
|
82
|
-
});
|
|
83
|
-
try {
|
|
84
|
-
await cb(ml, workFolder);
|
|
85
|
-
} finally {
|
|
86
|
-
await ml.close();
|
|
87
|
-
}
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
export async function awaitBlockDone(prj: Project, blockId: string, timeout: number = 2000) {
|
|
92
|
-
const abortSignal = AbortSignal.timeout(timeout);
|
|
93
|
-
const overview = prj.overview;
|
|
94
|
-
const state = prj.getBlockState(blockId);
|
|
95
|
-
// const stateAndOverview = Computable.make(() => ({ overview, state: undefined }));
|
|
96
|
-
while (true) {
|
|
97
|
-
// const {
|
|
98
|
-
// overview: overviewSnapshot,
|
|
99
|
-
// state: stateSnapshot
|
|
100
|
-
// } = await stateAndOverview.getValue();
|
|
101
|
-
const overviewSnapshot = (await overview.getValue())!;
|
|
102
|
-
const blockOverview = overviewSnapshot.blocks.find((b) => b.id == blockId);
|
|
103
|
-
if (blockOverview === undefined) throw new Error(`Blocks not found: ${blockId}`);
|
|
104
|
-
if (blockOverview.outputErrors) return;
|
|
105
|
-
if (blockOverview.calculationStatus === 'Done') return;
|
|
106
|
-
try {
|
|
107
|
-
await overview.awaitChange(abortSignal);
|
|
108
|
-
} catch (e: any) {
|
|
109
|
-
console.dir(await state.getValue(), { depth: 5 });
|
|
110
|
-
throw new Error('Aborted.', { cause: e });
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
test('project list manipulations test', async () => {
|
|
116
|
-
await withMl(async (ml) => {
|
|
117
|
-
const projectList = ml.projectList;
|
|
118
|
-
|
|
119
|
-
expect(await projectList.awaitStableValue()).toEqual([]);
|
|
120
|
-
|
|
121
|
-
const pRid1 = await ml.createProject({ label: 'Project 1' }, 'id1');
|
|
122
|
-
|
|
123
|
-
expect(await projectList.getValue()).toMatchObject([
|
|
124
|
-
{
|
|
125
|
-
id: 'id1',
|
|
126
|
-
rid: pRid1,
|
|
127
|
-
meta: { label: 'Project 1' },
|
|
128
|
-
opened: false
|
|
129
|
-
}
|
|
130
|
-
]);
|
|
131
|
-
|
|
132
|
-
await ml.setProjectMeta(pRid1, { label: 'Project 1A' });
|
|
133
|
-
|
|
134
|
-
const listSnapshot1 = await projectList.getValue();
|
|
135
|
-
expect(listSnapshot1).toMatchObject([
|
|
136
|
-
{
|
|
137
|
-
id: 'id1',
|
|
138
|
-
rid: pRid1,
|
|
139
|
-
meta: { label: 'Project 1A' },
|
|
140
|
-
opened: false
|
|
141
|
-
}
|
|
142
|
-
]);
|
|
143
|
-
expect(listSnapshot1![0].lastModified.valueOf()).toBeGreaterThan(
|
|
144
|
-
listSnapshot1![0].created.valueOf()
|
|
145
|
-
);
|
|
146
|
-
|
|
147
|
-
await ml.openProject(pRid1);
|
|
148
|
-
|
|
149
|
-
expect(await projectList.getValue()).toMatchObject([
|
|
150
|
-
{
|
|
151
|
-
id: 'id1',
|
|
152
|
-
rid: pRid1,
|
|
153
|
-
meta: { label: 'Project 1A' },
|
|
154
|
-
opened: true
|
|
155
|
-
}
|
|
156
|
-
]);
|
|
157
|
-
|
|
158
|
-
await ml.closeProject(pRid1);
|
|
159
|
-
|
|
160
|
-
expect(await projectList.getValue()).toMatchObject([
|
|
161
|
-
{
|
|
162
|
-
id: 'id1',
|
|
163
|
-
rid: pRid1,
|
|
164
|
-
meta: { label: 'Project 1A' },
|
|
165
|
-
opened: false
|
|
166
|
-
}
|
|
167
|
-
]);
|
|
168
|
-
|
|
169
|
-
await ml.deleteProject('id1');
|
|
170
|
-
|
|
171
|
-
expect(await projectList.awaitStableValue()).toEqual([]);
|
|
172
|
-
});
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
test('simple project manipulations test', async () => {
|
|
176
|
-
await withMl(async (ml) => {
|
|
177
|
-
const projectList = ml.projectList;
|
|
178
|
-
expect(await projectList.awaitStableValue()).toEqual([]);
|
|
179
|
-
const pRid1 = await ml.createProject({ label: 'Project 1' }, 'id1');
|
|
180
|
-
const projectListValue1 = await projectList.getValue();
|
|
181
|
-
expect(projectListValue1).toMatchObject([
|
|
182
|
-
{
|
|
183
|
-
id: 'id1',
|
|
184
|
-
rid: pRid1,
|
|
185
|
-
meta: { label: 'Project 1' },
|
|
186
|
-
opened: false
|
|
187
|
-
}
|
|
188
|
-
]);
|
|
189
|
-
|
|
190
|
-
const lastModInitial = projectListValue1![0].lastModified.valueOf();
|
|
191
|
-
|
|
192
|
-
await ml.openProject(pRid1);
|
|
193
|
-
const prj = ml.getOpenedProject(pRid1);
|
|
194
|
-
|
|
195
|
-
expect(await prj.overview.awaitStableValue()).toMatchObject({
|
|
196
|
-
meta: { label: 'Project 1' },
|
|
197
|
-
authorMarker: undefined,
|
|
198
|
-
blocks: []
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
await ml.setProjectMeta(
|
|
202
|
-
pRid1,
|
|
203
|
-
{ label: 'New Project Label' },
|
|
204
|
-
{ authorId: 'test_author', localVersion: 1 }
|
|
205
|
-
);
|
|
206
|
-
await prj.overview.refreshState();
|
|
207
|
-
expect(await prj.overview.awaitStableValue()).toMatchObject({
|
|
208
|
-
meta: { label: 'New Project Label' },
|
|
209
|
-
authorMarker: { authorId: 'test_author', localVersion: 1 },
|
|
210
|
-
blocks: []
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
const {
|
|
214
|
-
enterNumbersSpecFromRemote,
|
|
215
|
-
sumNumbersSpecFromRemote,
|
|
216
|
-
enterNumbersSpecFromDev,
|
|
217
|
-
sumNumbersSpecFromDev
|
|
218
|
-
} = await getStandardBlockSpecs();
|
|
219
|
-
const block1Id = await prj.addBlock('Block 1', enterNumbersSpecFromRemote);
|
|
220
|
-
const block2Id = await prj.addBlock('Block 2', enterNumbersSpecFromDev);
|
|
221
|
-
const block3Id = await prj.addBlock('Block 3', sumNumbersSpecFromRemote);
|
|
222
|
-
|
|
223
|
-
expect(await prj.overview.awaitStableValue()).toMatchObject({
|
|
224
|
-
meta: { label: 'New Project Label' },
|
|
225
|
-
authorMarker: undefined
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
const overviewSnapshot0 = await prj.overview.awaitStableValue();
|
|
229
|
-
|
|
230
|
-
overviewSnapshot0.blocks.forEach((block) => {
|
|
231
|
-
expect(block.sections).toBeDefined();
|
|
232
|
-
expect(block.canRun).toEqual(false);
|
|
233
|
-
expect(block.currentBlockPack).toBeDefined();
|
|
234
|
-
expect(block.navigationState).toStrictEqual({ href: '/' });
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
const block1StableState0 = await prj.getBlockState(block1Id).awaitStableValue();
|
|
238
|
-
const block2StableState0 = await prj.getBlockState(block2Id).awaitStableValue();
|
|
239
|
-
const block3StableState0 = await prj.getBlockState(block3Id).awaitStableValue();
|
|
240
|
-
|
|
241
|
-
await prj.setNavigationState(block1Id, { href: '/section1' });
|
|
242
|
-
await prj.setBlockArgs(block1Id, { numbers: [1, 2, 3] });
|
|
243
|
-
await prj.setBlockArgs(block2Id, { numbers: [3, 4, 5] });
|
|
244
|
-
await prj.setBlockArgs(block3Id, {
|
|
245
|
-
sources: [outputRef(block1Id, 'column'), outputRef(block2Id, 'column')]
|
|
246
|
-
});
|
|
247
|
-
await prj.runBlock(block3Id);
|
|
248
|
-
await awaitBlockDone(prj, block3Id);
|
|
249
|
-
|
|
250
|
-
const overviewSnapshot1 = await prj.overview.awaitStableValue();
|
|
251
|
-
|
|
252
|
-
expect(overviewSnapshot1.lastModified.valueOf()).toBeGreaterThan(lastModInitial);
|
|
253
|
-
|
|
254
|
-
overviewSnapshot1.blocks.forEach((block) => {
|
|
255
|
-
expect(block.sections).toBeDefined();
|
|
256
|
-
expect(block.canRun).toEqual(false);
|
|
257
|
-
expect(block.stale).toEqual(false);
|
|
258
|
-
expect(block.currentBlockPack).toBeDefined();
|
|
259
|
-
if (block.id === block1Id) expect(block.navigationState).toStrictEqual({ href: '/section1' });
|
|
260
|
-
else expect(block.navigationState).toStrictEqual({ href: '/' });
|
|
261
|
-
});
|
|
262
|
-
// console.dir(overviewSnapshot1, { depth: 5 });
|
|
263
|
-
|
|
264
|
-
const block1StableFrontend = await prj.getBlockFrontend(block1Id).awaitStableValue();
|
|
265
|
-
expect(block1StableFrontend.path).toBeDefined();
|
|
266
|
-
expect(block1StableFrontend.sdkVersion).toBeDefined();
|
|
267
|
-
const block2StableFrontend = await prj.getBlockFrontend(block2Id).awaitStableValue();
|
|
268
|
-
expect(block2StableFrontend.path).toMatch(/block-beta-enter-numbers/);
|
|
269
|
-
expect(block2StableFrontend.sdkVersion).toBeDefined();
|
|
270
|
-
const block3StableFrontend = await prj.getBlockFrontend(block3Id).awaitStableValue();
|
|
271
|
-
expect(block3StableFrontend.path).toBeDefined();
|
|
272
|
-
expect(block3StableFrontend.sdkVersion).toBeDefined();
|
|
273
|
-
// console.dir(
|
|
274
|
-
// { block1StableFrontend, block2StableFrontend, block3StableFrontend },
|
|
275
|
-
// { depth: 5 });
|
|
276
|
-
|
|
277
|
-
const block1StableState1 = await prj.getBlockState(block1Id).awaitStableValue();
|
|
278
|
-
const block2StableState1 = await prj.getBlockState(block2Id).awaitStableValue();
|
|
279
|
-
const block3StableState1 = await prj.getBlockState(block3Id).awaitStableValue();
|
|
280
|
-
|
|
281
|
-
expect(block1StableState1.navigationState).toStrictEqual({ href: '/section1' });
|
|
282
|
-
expect(block2StableState1.navigationState).toStrictEqual({ href: '/' });
|
|
283
|
-
expect(block3StableState1.navigationState).toStrictEqual({ href: '/' });
|
|
284
|
-
// console.dir(block1StableState1, { depth: 5 });
|
|
285
|
-
// console.dir(block2StableState1, { depth: 5 });
|
|
286
|
-
// console.dir(block3StableState1, { depth: 5 });
|
|
287
|
-
|
|
288
|
-
expect(block3StableState1.outputs!['sum']).toStrictEqual({
|
|
289
|
-
ok: true,
|
|
290
|
-
value: 18
|
|
291
|
-
});
|
|
292
|
-
|
|
293
|
-
await prj.resetBlockArgsAndUiState(block2Id);
|
|
294
|
-
|
|
295
|
-
const block2Inputs = await prj.getBlockState(block2Id).getValue();
|
|
296
|
-
expect(block2Inputs.args).toEqual({ numbers: [] });
|
|
297
|
-
|
|
298
|
-
const overviewSnapshot2 = await prj.overview.awaitStableValue();
|
|
299
|
-
expect(overviewSnapshot2.blocks.find((b) => b.id === block3Id)?.canRun).toEqual(false);
|
|
300
|
-
expect(overviewSnapshot2.blocks.find((b) => b.id === block3Id)?.stale).toEqual(true);
|
|
301
|
-
expect(overviewSnapshot2.blocks.find((b) => b.id === block2Id)?.stale).toEqual(true);
|
|
302
|
-
});
|
|
303
|
-
}, 20000);
|
|
304
|
-
|
|
305
|
-
test('reorder & rename blocks', async () => {
|
|
306
|
-
await withMl(async (ml) => {
|
|
307
|
-
const projectList = ml.projectList;
|
|
308
|
-
expect(await projectList.awaitStableValue()).toEqual([]);
|
|
309
|
-
const pRid1 = await ml.createProject({ label: 'Project 1' }, 'id1');
|
|
310
|
-
|
|
311
|
-
await ml.openProject(pRid1);
|
|
312
|
-
const prj = ml.getOpenedProject(pRid1);
|
|
313
|
-
|
|
314
|
-
const {
|
|
315
|
-
enterNumbersSpecFromRemote,
|
|
316
|
-
sumNumbersSpecFromRemote,
|
|
317
|
-
enterNumbersSpecFromDev,
|
|
318
|
-
sumNumbersSpecFromDev
|
|
319
|
-
} = await getStandardBlockSpecs();
|
|
320
|
-
const block1Id = await prj.addBlock('Block 1', enterNumbersSpecFromRemote);
|
|
321
|
-
const block2Id = await prj.addBlock('Block 2', enterNumbersSpecFromDev);
|
|
322
|
-
const block3Id = await prj.addBlock('Block 3', sumNumbersSpecFromRemote);
|
|
323
|
-
|
|
324
|
-
const overviewSnapshot0 = await prj.overview.awaitStableValue();
|
|
325
|
-
|
|
326
|
-
overviewSnapshot0.blocks.forEach((block) => {
|
|
327
|
-
expect(block.sections).toBeDefined();
|
|
328
|
-
expect(block.canRun).toEqual(false);
|
|
329
|
-
expect(block.currentBlockPack).toBeDefined();
|
|
330
|
-
expect(block.navigationState).toStrictEqual({ href: '/' });
|
|
331
|
-
});
|
|
332
|
-
|
|
333
|
-
await prj.setNavigationState(block1Id, { href: '/section1' });
|
|
334
|
-
await prj.setBlockArgs(block1Id, { numbers: [1, 2, 3] });
|
|
335
|
-
await prj.setBlockArgs(block2Id, { numbers: [3, 4, 5] });
|
|
336
|
-
await prj.setBlockArgs(block3Id, {
|
|
337
|
-
sources: [outputRef(block1Id, 'column'), outputRef(block2Id, 'column')]
|
|
338
|
-
});
|
|
339
|
-
await prj.runBlock(block3Id);
|
|
340
|
-
await awaitBlockDone(prj, block3Id);
|
|
341
|
-
|
|
342
|
-
const overviewSnapshot1 = await prj.overview.awaitStableValue();
|
|
343
|
-
expect(overviewSnapshot1).toMatchObject({
|
|
344
|
-
blocks: [
|
|
345
|
-
{ id: block1Id, calculationStatus: 'Done' },
|
|
346
|
-
{ id: block2Id, calculationStatus: 'Done' },
|
|
347
|
-
{ id: block3Id, calculationStatus: 'Done' }
|
|
348
|
-
]
|
|
349
|
-
});
|
|
350
|
-
|
|
351
|
-
await prj.reorderBlocks([block2Id, block3Id, block1Id]);
|
|
352
|
-
|
|
353
|
-
const overviewSnapshot2 = await prj.overview.awaitStableValue();
|
|
354
|
-
expect(overviewSnapshot2).toMatchObject({
|
|
355
|
-
blocks: [
|
|
356
|
-
{ id: block2Id, calculationStatus: 'Done' },
|
|
357
|
-
{ id: block3Id, calculationStatus: 'Limbo' },
|
|
358
|
-
{ id: block1Id, calculationStatus: 'Done' }
|
|
359
|
-
]
|
|
360
|
-
});
|
|
361
|
-
|
|
362
|
-
// await prj.setBlockLabel(block3Id, 'New Block Label');
|
|
363
|
-
// const overviewSnapshot3 = await prj.overview.awaitStableValue();
|
|
364
|
-
// expect(overviewSnapshot3).toMatchObject({
|
|
365
|
-
// blocks: [
|
|
366
|
-
// { id: block2Id, calculationStatus: 'Done' },
|
|
367
|
-
// { id: block3Id, calculationStatus: 'Limbo', label: 'New Block Label' },
|
|
368
|
-
// { id: block1Id, calculationStatus: 'Done' }
|
|
369
|
-
// ]
|
|
370
|
-
// });
|
|
371
|
-
});
|
|
372
|
-
});
|
|
373
|
-
|
|
374
|
-
test('limbo test', async () => {
|
|
375
|
-
await withMl(async (ml) => {
|
|
376
|
-
const pRid1 = await ml.createProject({ label: 'Project 1' }, 'id1');
|
|
377
|
-
await ml.openProject(pRid1);
|
|
378
|
-
const prj = ml.getOpenedProject(pRid1);
|
|
379
|
-
|
|
380
|
-
const { enterNumbersSpecFromRemote, sumNumbersSpecFromRemote } = await getStandardBlockSpecs();
|
|
381
|
-
const block1Id = await prj.addBlock('Block 1', enterNumbersSpecFromRemote);
|
|
382
|
-
const block2Id = await prj.addBlock('Block 2', sumNumbersSpecFromRemote);
|
|
383
|
-
|
|
384
|
-
const overview0 = await prj.overview.awaitStableValue();
|
|
385
|
-
overview0.blocks.forEach((block) => {
|
|
386
|
-
expect(block.sections).toBeDefined();
|
|
387
|
-
expect(block.canRun).toEqual(false);
|
|
388
|
-
expect(block.currentBlockPack).toBeDefined();
|
|
389
|
-
});
|
|
390
|
-
|
|
391
|
-
await prj.setBlockArgs(block1Id, { numbers: [1, 2, 3] });
|
|
392
|
-
await prj.setBlockArgs(block2Id, {
|
|
393
|
-
sources: [outputRef(block1Id, 'column')]
|
|
394
|
-
});
|
|
395
|
-
|
|
396
|
-
const overview1 = await prj.overview.awaitStableValue();
|
|
397
|
-
overview1.blocks.forEach((block) => {
|
|
398
|
-
expect(block.sections).toBeDefined();
|
|
399
|
-
expect(block.canRun).toEqual(true);
|
|
400
|
-
expect(block.currentBlockPack).toBeDefined();
|
|
401
|
-
});
|
|
402
|
-
|
|
403
|
-
await prj.runBlock(block2Id);
|
|
404
|
-
await awaitBlockDone(prj, block2Id);
|
|
405
|
-
|
|
406
|
-
const block2StableState1 = await prj.getBlockState(block2Id).getValue();
|
|
407
|
-
expect(block2StableState1.outputs!['sum']).toStrictEqual({
|
|
408
|
-
ok: true,
|
|
409
|
-
value: 6
|
|
410
|
-
});
|
|
411
|
-
|
|
412
|
-
const overview2 = await prj.overview.awaitStableValue();
|
|
413
|
-
overview2.blocks.forEach((block) => {
|
|
414
|
-
expect(block.sections).toBeDefined();
|
|
415
|
-
expect(block.calculationStatus).toEqual('Done');
|
|
416
|
-
expect(block.canRun).toEqual(false);
|
|
417
|
-
expect(block.currentBlockPack).toBeDefined();
|
|
418
|
-
});
|
|
419
|
-
|
|
420
|
-
await prj.setBlockArgs(block1Id, { numbers: [2, 3] });
|
|
421
|
-
await prj.runBlock(block1Id);
|
|
422
|
-
await awaitBlockDone(prj, block1Id);
|
|
423
|
-
|
|
424
|
-
const overview3 = await prj.overview.awaitStableValue();
|
|
425
|
-
const [overview3Block1, overview3Block2] = overview3.blocks;
|
|
426
|
-
expect(overview3Block1.calculationStatus).toEqual('Done');
|
|
427
|
-
expect(overview3Block2.calculationStatus).toEqual('Limbo');
|
|
428
|
-
|
|
429
|
-
await prj.runBlock(block2Id);
|
|
430
|
-
await awaitBlockDone(prj, block2Id);
|
|
431
|
-
|
|
432
|
-
const block2StableState2 = await prj.getBlockState(block2Id).getValue();
|
|
433
|
-
expect(block2StableState2.outputs!['sum']).toStrictEqual({
|
|
434
|
-
ok: true,
|
|
435
|
-
value: 5
|
|
436
|
-
});
|
|
437
|
-
|
|
438
|
-
const overview4 = await prj.overview.awaitStableValue();
|
|
439
|
-
const [overview4Block1, overview4Block2] = overview4.blocks;
|
|
440
|
-
expect(overview4Block1.calculationStatus).toEqual('Done');
|
|
441
|
-
expect(overview4Block2.calculationStatus).toEqual('Done');
|
|
442
|
-
});
|
|
443
|
-
});
|
|
444
|
-
|
|
445
|
-
test('block update test', async () => {
|
|
446
|
-
await withMl(async (ml, workFolder) => {
|
|
447
|
-
const pRid1 = await ml.createProject({ label: 'Project 1' }, 'id1');
|
|
448
|
-
await ml.openProject(pRid1);
|
|
449
|
-
const prj = ml.getOpenedProject(pRid1);
|
|
450
|
-
|
|
451
|
-
const tmpDevBlockFolder = path.resolve(workFolder, 'dev');
|
|
452
|
-
await fs.promises.mkdir(tmpDevBlockFolder, { recursive: true });
|
|
453
|
-
|
|
454
|
-
const devBlockPath = path.resolve(tmpDevBlockFolder, 'block-beta-enter-numbers');
|
|
455
|
-
await fs.promises.cp(path.resolve('integration', 'block-beta-enter-numbers'), devBlockPath, {
|
|
456
|
-
recursive: true
|
|
457
|
-
});
|
|
458
|
-
const mtime = await getDevV1PacketMtime(devBlockPath);
|
|
459
|
-
const block1Id = await prj.addBlock('Block 1', {
|
|
460
|
-
type: 'dev-v1',
|
|
461
|
-
folder: devBlockPath,
|
|
462
|
-
mtime
|
|
463
|
-
});
|
|
464
|
-
|
|
465
|
-
const overview0 = await prj.overview.awaitStableValue();
|
|
466
|
-
expect(overview0.blocks[0].updatedBlockPack).toBeUndefined();
|
|
467
|
-
|
|
468
|
-
// touch
|
|
469
|
-
await fs.promises.appendFile(path.resolve(devBlockPath, ...LegacyDevBlockPackConfig), ' ');
|
|
470
|
-
|
|
471
|
-
// await update watcher
|
|
472
|
-
await prj.overview.refreshState();
|
|
473
|
-
const overview1 = await prj.overview.awaitStableValue();
|
|
474
|
-
expect(overview1.blocks[0].updatedBlockPack).toBeDefined();
|
|
475
|
-
|
|
476
|
-
await prj.updateBlockPack(block1Id, overview1.blocks[0].updatedBlockPack!);
|
|
477
|
-
|
|
478
|
-
const overview2 = await prj.overview.awaitStableValue();
|
|
479
|
-
expect(overview2.blocks[0].currentBlockPack).toStrictEqual(
|
|
480
|
-
overview1.blocks[0].updatedBlockPack
|
|
481
|
-
);
|
|
482
|
-
expect(overview2.blocks[0].updatedBlockPack).toBeUndefined();
|
|
483
|
-
});
|
|
484
|
-
});
|
|
485
|
-
|
|
486
|
-
test('project open and close test', async () => {
|
|
487
|
-
await withMl(async (ml, workFolder) => {
|
|
488
|
-
const pRid1 = await ml.createProject({ label: 'Project 1' }, 'id1');
|
|
489
|
-
await ml.openProject(pRid1);
|
|
490
|
-
let prj = ml.getOpenedProject(pRid1);
|
|
491
|
-
|
|
492
|
-
const blockId = await prj.addBlock('Test Block', {
|
|
493
|
-
type: 'from-registry-v1',
|
|
494
|
-
registryUrl: 'https://block.registry.platforma.bio/releases',
|
|
495
|
-
id: { organization: 'milaboratory', name: 'enter-numbers', version: '1.1.1' }
|
|
496
|
-
});
|
|
497
|
-
await prj.setBlockArgs(blockId, { numbers: [1, 2, 3] });
|
|
498
|
-
const overview1 = await prj.overview.awaitStableValue();
|
|
499
|
-
expect(overview1.blocks[0].canRun).toEqual(true);
|
|
500
|
-
|
|
501
|
-
ml.closeProject(pRid1);
|
|
502
|
-
await ml.openProject(pRid1);
|
|
503
|
-
prj = ml.getOpenedProject(pRid1);
|
|
504
|
-
|
|
505
|
-
const overview2 = await prj.overview.awaitStableValue();
|
|
506
|
-
expect(overview2.blocks[0].canRun).toEqual(true);
|
|
507
|
-
});
|
|
508
|
-
});
|
|
509
|
-
|
|
510
|
-
test('block error test', async () => {
|
|
511
|
-
await withMl(async (ml) => {
|
|
512
|
-
const pRid1 = await ml.createProject({ label: 'Project 1' }, 'id1');
|
|
513
|
-
await ml.openProject(pRid1);
|
|
514
|
-
const prj = ml.getOpenedProject(pRid1);
|
|
515
|
-
|
|
516
|
-
expect(await prj.overview.awaitStableValue()).toMatchObject({
|
|
517
|
-
meta: { label: 'Project 1' },
|
|
518
|
-
blocks: []
|
|
519
|
-
});
|
|
520
|
-
|
|
521
|
-
const {
|
|
522
|
-
enterNumbersSpecFromRemote,
|
|
523
|
-
sumNumbersSpecFromRemote,
|
|
524
|
-
enterNumbersSpecFromDev,
|
|
525
|
-
sumNumbersSpecFromDev
|
|
526
|
-
} = await getStandardBlockSpecs();
|
|
527
|
-
|
|
528
|
-
const block3Id = await prj.addBlock('Block 3', sumNumbersSpecFromDev);
|
|
529
|
-
|
|
530
|
-
await prj.setBlockArgs(block3Id, {
|
|
531
|
-
sources: [] // empty reference list should produce an error
|
|
532
|
-
});
|
|
533
|
-
|
|
534
|
-
await prj.runBlock(block3Id);
|
|
535
|
-
await awaitBlockDone(prj, block3Id);
|
|
536
|
-
|
|
537
|
-
const overviewSnapshot1 = await prj.overview.awaitStableValue();
|
|
538
|
-
|
|
539
|
-
overviewSnapshot1.blocks.forEach((block) => {
|
|
540
|
-
expect(block.sections).toBeDefined();
|
|
541
|
-
});
|
|
542
|
-
expect(overviewSnapshot1.blocks[0].outputErrors).toStrictEqual(true);
|
|
543
|
-
|
|
544
|
-
const block3StableState = await prj.getBlockState(block3Id).getValue();
|
|
545
|
-
|
|
546
|
-
const sum = block3StableState.outputs!['sum'];
|
|
547
|
-
expect(sum.ok).toStrictEqual(false);
|
|
548
|
-
if (!sum.ok) expect(sum.errors[0]).toContain('tengo-mistd');
|
|
549
|
-
});
|
|
550
|
-
});
|
|
551
|
-
|
|
552
|
-
test('should create download-file block, render it and gets outputs from its config', async () => {
|
|
553
|
-
await withMl(async (ml) => {
|
|
554
|
-
const pRid1 = await ml.createProject({ label: 'Project 1' }, 'id1');
|
|
555
|
-
await ml.openProject(pRid1);
|
|
556
|
-
const prj = ml.getOpenedProject(pRid1);
|
|
557
|
-
|
|
558
|
-
expect(await prj.overview.awaitStableValue()).toMatchObject({
|
|
559
|
-
meta: { label: 'Project 1' },
|
|
560
|
-
blocks: []
|
|
561
|
-
});
|
|
562
|
-
|
|
563
|
-
const { downloadFileSpecFromRemote } = await getStandardBlockSpecs();
|
|
564
|
-
|
|
565
|
-
const block3Id = await prj.addBlock('Block 3', downloadFileSpecFromRemote);
|
|
566
|
-
|
|
567
|
-
await prj.setBlockArgs(block3Id, {
|
|
568
|
-
storageId: 'library',
|
|
569
|
-
filePath: 'answer_to_the_ultimate_question.txt'
|
|
570
|
-
});
|
|
571
|
-
|
|
572
|
-
await prj.runBlock(block3Id);
|
|
573
|
-
await awaitBlockDone(prj, block3Id);
|
|
574
|
-
|
|
575
|
-
const overviewSnapshot1 = await prj.overview.awaitStableValue();
|
|
576
|
-
|
|
577
|
-
overviewSnapshot1.blocks.forEach((block) => {
|
|
578
|
-
expect(block.sections).toBeDefined();
|
|
579
|
-
});
|
|
580
|
-
console.dir(overviewSnapshot1, { depth: 5 });
|
|
581
|
-
|
|
582
|
-
const block3StableFrontend = await prj.getBlockFrontend(block3Id).awaitStableValue();
|
|
583
|
-
expect(block3StableFrontend).toBeDefined();
|
|
584
|
-
console.dir({ block3StableFrontend }, { depth: 5 });
|
|
585
|
-
|
|
586
|
-
const block3StateComputable = prj.getBlockState(block3Id);
|
|
587
|
-
|
|
588
|
-
const block3StableState = await block3StateComputable.awaitStableFullValue();
|
|
589
|
-
|
|
590
|
-
console.dir(block3StableState, { depth: 5 });
|
|
591
|
-
|
|
592
|
-
expect((block3StableState.value.outputs!['contentAsJson'] as any).value).toStrictEqual(42);
|
|
593
|
-
const localBlob = (block3StableState.value.outputs!['downloadedBlobContent'] as any)
|
|
594
|
-
.value as LocalBlobHandleAndSize;
|
|
595
|
-
const remoteBlob = (block3StableState.value.outputs!['onDemandBlobContent'] as any)
|
|
596
|
-
.value as RemoteBlobHandleAndSize;
|
|
597
|
-
|
|
598
|
-
expect(
|
|
599
|
-
Buffer.from(await ml.driverKit.blobDriver.getContent(localBlob.handle)).toString('utf-8')
|
|
600
|
-
).toEqual('42\n');
|
|
601
|
-
|
|
602
|
-
expect(
|
|
603
|
-
Buffer.from(await ml.driverKit.blobDriver.getContent(remoteBlob.handle)).toString('utf-8')
|
|
604
|
-
).toEqual('42\n');
|
|
605
|
-
});
|
|
606
|
-
});
|
|
607
|
-
|
|
608
|
-
test('should create upload-file block, render it and upload a file to pl server', async () => {
|
|
609
|
-
await withMl(async (ml) => {
|
|
610
|
-
const pRid1 = await ml.createProject({ label: 'Project 1' }, 'id1');
|
|
611
|
-
await ml.openProject(pRid1);
|
|
612
|
-
const prj = ml.getOpenedProject(pRid1);
|
|
613
|
-
|
|
614
|
-
expect(await prj.overview.awaitStableValue()).toMatchObject({
|
|
615
|
-
meta: { label: 'Project 1' },
|
|
616
|
-
blocks: []
|
|
617
|
-
});
|
|
618
|
-
|
|
619
|
-
const { uploadFileSpecFromRemote } = await getStandardBlockSpecs();
|
|
620
|
-
// const uploadFileSpecFromDev: BlockPackSpec = {
|
|
621
|
-
// type: 'dev',
|
|
622
|
-
// folder: '/home/snyssfx/prog/mi/tpls/block-beta-upload-file',
|
|
623
|
-
// }
|
|
624
|
-
|
|
625
|
-
const block3Id = await prj.addBlock('Block 3', uploadFileSpecFromRemote);
|
|
626
|
-
|
|
627
|
-
const storages = await ml.driverKit.lsDriver.getStorageList();
|
|
628
|
-
const local = storages.find((s) => s.name == 'local');
|
|
629
|
-
expect(local).not.toBeUndefined();
|
|
630
|
-
const fileDir = path.resolve(__dirname, '..', '..', '..', '..', '..', 'assets');
|
|
631
|
-
const files = await ml.driverKit.lsDriver.listFiles(local!.handle, fileDir);
|
|
632
|
-
const ourFile = files.entries.find(
|
|
633
|
-
(f) => f.name == 'another_answer_to_the_ultimate_question.txt'
|
|
634
|
-
);
|
|
635
|
-
expect(ourFile).not.toBeUndefined();
|
|
636
|
-
expect(ourFile?.type).toBe('file');
|
|
637
|
-
|
|
638
|
-
await prj.setBlockArgs(block3Id, {
|
|
639
|
-
importHandle: (ourFile as any).handle
|
|
640
|
-
});
|
|
641
|
-
|
|
642
|
-
await prj.runBlock(block3Id);
|
|
643
|
-
await awaitBlockDone(prj, block3Id, 5000);
|
|
644
|
-
|
|
645
|
-
const block3StateComputable = prj.getBlockState(block3Id);
|
|
646
|
-
|
|
647
|
-
while (true) {
|
|
648
|
-
const state = await block3StateComputable.getFullValue();
|
|
649
|
-
|
|
650
|
-
// console.dir(state, { depth: 5 });
|
|
651
|
-
|
|
652
|
-
if (state.stable && (state.value.outputs!['handle'] as any).value != undefined) {
|
|
653
|
-
expect(state.type).toEqual('ok');
|
|
654
|
-
expect((state.value.outputs!['handle'] as any).value.isUpload).toBeTruthy();
|
|
655
|
-
expect((state.value.outputs!['handle'] as any).value.done).toBeTruthy();
|
|
656
|
-
return;
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
await block3StateComputable.awaitChange();
|
|
660
|
-
}
|
|
661
|
-
});
|
|
662
|
-
});
|
|
663
|
-
|
|
664
|
-
// TODO: fix. It was skipped because of RunCommand breaking change in pl-core.
|
|
665
|
-
// The block should be rewrote from milib to tengo-sdk
|
|
666
|
-
test.skip('should create read-logs block, render it and read logs from a file', async () => {
|
|
667
|
-
await withMl(async (ml) => {
|
|
668
|
-
const pRid1 = await ml.createProject({ label: 'Project 1' }, 'id1');
|
|
669
|
-
await ml.openProject(pRid1);
|
|
670
|
-
const prj = ml.getOpenedProject(pRid1);
|
|
671
|
-
|
|
672
|
-
expect(await prj.overview.awaitStableValue()).toMatchObject({
|
|
673
|
-
meta: { label: 'Project 1' },
|
|
674
|
-
blocks: []
|
|
675
|
-
});
|
|
676
|
-
|
|
677
|
-
const { readLogsSpecFromRemote } = await getStandardBlockSpecs();
|
|
678
|
-
// const readLogsSpecFromDev: BlockPackSpec = {
|
|
679
|
-
// type: 'dev',
|
|
680
|
-
// folder: '/home/snyssfx/prog/mi/tpls/block-beta-read-logs',
|
|
681
|
-
// }
|
|
682
|
-
const block3Id = await prj.addBlock('Block 3', readLogsSpecFromRemote);
|
|
683
|
-
|
|
684
|
-
const storages = await ml.driverKit.lsDriver.getStorageList();
|
|
685
|
-
const library = storages.find((s) => s.name == 'library');
|
|
686
|
-
expect(library).toBeDefined();
|
|
687
|
-
const files = await ml.driverKit.lsDriver.listFiles(library!.handle, '');
|
|
688
|
-
const ourFile = files.entries.find(
|
|
689
|
-
(f) => f.name == 'maybe_the_number_of_lines_is_the_answer.txt'
|
|
690
|
-
);
|
|
691
|
-
expect(ourFile).toBeDefined();
|
|
692
|
-
expect(ourFile?.type).toBe('file');
|
|
693
|
-
|
|
694
|
-
await prj.setBlockArgs(block3Id, {
|
|
695
|
-
fileHandle: (ourFile as any).handle,
|
|
696
|
-
// args are from here:
|
|
697
|
-
// https://github.com/milaboratory/sleep/blob/3c046cdcc504b63f1a6e592a4aa87ee773a94d72/read-file-to-stdout-with-sleep.go#L24
|
|
698
|
-
readFileWithSleepArgs: ['file.txt', 'PREFIX', '100', '1000']
|
|
699
|
-
});
|
|
700
|
-
|
|
701
|
-
await prj.runBlock(block3Id);
|
|
702
|
-
await awaitBlockDone(prj, block3Id, 15000);
|
|
703
|
-
|
|
704
|
-
const computable = prj.getBlockState(block3Id);
|
|
705
|
-
// await computable.refreshState();
|
|
706
|
-
|
|
707
|
-
while (true) {
|
|
708
|
-
const state = await computable.getFullValue();
|
|
709
|
-
console.dir(state, { depth: 5 });
|
|
710
|
-
|
|
711
|
-
if (
|
|
712
|
-
state.stable &&
|
|
713
|
-
state.value.outputs!['lastLogs'].ok &&
|
|
714
|
-
state.value.outputs!['lastLogs'].value != undefined
|
|
715
|
-
) {
|
|
716
|
-
expect((state.value.outputs!['progressLog'] as any).value).toContain('PREFIX');
|
|
717
|
-
expect((state.value.outputs!['progressLog'] as any).value).toContain('bytes read');
|
|
718
|
-
expect((state.value.outputs!['lastLogs'] as any).value.split('\n').length).toEqual(10 + 1); // 11 because the last element is empty
|
|
719
|
-
return;
|
|
720
|
-
}
|
|
721
|
-
|
|
722
|
-
await computable.awaitChange();
|
|
723
|
-
}
|
|
724
|
-
});
|
|
725
|
-
// The timeout is higher here because pl-core must download a software for this test.
|
|
726
|
-
}, 20000);
|