@sealcode/sealgen 0.15.22 → 0.15.24
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/@types/src/add-collection.d.ts +1 -1
- package/@types/src/page/list.d.ts +1 -0
- package/@types/src/page/sealious-list.d.ts +1 -1
- package/@types/src/page/sealious-list.test.d.ts +1 -0
- package/@types/src/test_utils/exec_async.d.ts +1 -0
- package/@types/src/test_utils/test-on-real-app.d.ts +24 -0
- package/@types/src/utils/input-wrapper.d.ts +1 -1
- package/lib/add-collection.js +18 -8
- package/lib/add-route.test.js +9 -92
- package/lib/forms/controls/checkboxed-list-input.js +31 -22
- package/lib/forms/controls/checkboxed-list-input.test.js +173 -0
- package/lib/forms/controls/multiple-files.js +1 -0
- package/lib/forms/controls/simple-input.js +1 -0
- package/lib/forms/controls/tickable.js +1 -0
- package/lib/forms/fields/field.js +1 -1
- package/lib/page/list.js +1 -0
- package/lib/page/sealious-list.js +5 -2
- package/lib/page/sealious-list.test.js +24 -0
- package/lib/test_utils/exec_async.js +24 -0
- package/lib/test_utils/test-on-real-app.js +147 -0
- package/lib/utils/input-wrapper.js +4 -2
- package/package.json +1 -1
- package/src/add-collection.ts +23 -8
- package/src/add-route.test.ts +9 -99
- package/src/forms/controls/checkboxed-list-input.test.ts +189 -0
- package/src/forms/controls/checkboxed-list-input.ts +36 -23
- package/src/forms/controls/multiple-files.ts +1 -0
- package/src/forms/controls/simple-input.ts +1 -0
- package/src/forms/controls/tickable.ts +1 -0
- package/src/forms/fields/field.ts +3 -1
- package/src/page/list.ts +1 -1
- package/src/page/sealious-list.test.ts +25 -0
- package/src/page/sealious-list.ts +7 -3
- package/src/test_utils/exec_async.ts +26 -0
- package/src/test_utils/test-on-real-app.ts +167 -0
- package/src/utils/input-wrapper.ts +4 -1
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { Context } from "koa";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
BaseListPageFields,
|
|
4
|
+
DEFAULT_ITEMS_PER_PAGE,
|
|
5
|
+
ListPage,
|
|
6
|
+
} from "./list.js";
|
|
3
7
|
import {
|
|
4
8
|
Collection,
|
|
5
9
|
CollectionItem,
|
|
@@ -57,12 +61,12 @@ export abstract class SealiousItemListPage<
|
|
|
57
61
|
async getItems(
|
|
58
62
|
ctx: Context,
|
|
59
63
|
page: number,
|
|
60
|
-
itemsPerPage: number
|
|
64
|
+
itemsPerPage: number | null
|
|
61
65
|
): Promise<CollectionItem<C>[]> {
|
|
62
66
|
const query = await this.makeQuery(ctx);
|
|
63
67
|
await this.addFilter(ctx, query);
|
|
64
68
|
const { items } = await query
|
|
65
|
-
.paginate({ items: itemsPerPage, page })
|
|
69
|
+
.paginate({ items: itemsPerPage || DEFAULT_ITEMS_PER_PAGE, page })
|
|
66
70
|
.fetch();
|
|
67
71
|
return items;
|
|
68
72
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/restrict-template-expressions */
|
|
2
|
+
import { exec } from "child_process";
|
|
3
|
+
|
|
4
|
+
export const execAsync = (cmd: string) => {
|
|
5
|
+
return new Promise<void>((resolve, reject) => {
|
|
6
|
+
const process = exec(cmd);
|
|
7
|
+
|
|
8
|
+
console.log("EXECUTING", cmd);
|
|
9
|
+
|
|
10
|
+
process?.stdout?.on("data", (data) => {
|
|
11
|
+
console.log(`stdout: ${data}`);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
process?.stderr?.on("data", (data) => {
|
|
15
|
+
console.log(`stdout: ${data}`);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
process.on("exit", (code) => {
|
|
19
|
+
if (code == 0) {
|
|
20
|
+
resolve();
|
|
21
|
+
} else {
|
|
22
|
+
reject();
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
};
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
|
2
|
+
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
|
3
|
+
import { execAsync } from "./exec_async.js";
|
|
4
|
+
import { spawn } from "child_process";
|
|
5
|
+
import getPort from "get-port";
|
|
6
|
+
import waitPort from "wait-port";
|
|
7
|
+
import { promises as fs } from "fs";
|
|
8
|
+
|
|
9
|
+
import _locreq from "locreq";
|
|
10
|
+
import { module_dirname } from "../utils/module-dirname.js";
|
|
11
|
+
import EventEmitter from "events";
|
|
12
|
+
const locreq = _locreq(module_dirname(import.meta.url));
|
|
13
|
+
|
|
14
|
+
export const callSealgenBin = () => `node ${locreq.resolve("lib/cli.js")}`;
|
|
15
|
+
|
|
16
|
+
const sealious_minimal_commit = "b0d5bf5";
|
|
17
|
+
|
|
18
|
+
export class RealAppTest {
|
|
19
|
+
public node_pid: number;
|
|
20
|
+
public app_port: number;
|
|
21
|
+
public mongo_port: number;
|
|
22
|
+
public app_path: string;
|
|
23
|
+
public container_name: string;
|
|
24
|
+
public emitter = new EventEmitter();
|
|
25
|
+
|
|
26
|
+
static async init(): Promise<RealAppTest> {
|
|
27
|
+
const test_app = new RealAppTest();
|
|
28
|
+
test_app.node_pid = 0;
|
|
29
|
+
const timestamp = Date.now();
|
|
30
|
+
test_app.app_path = `/tmp/sealious-minimal-${timestamp}`;
|
|
31
|
+
test_app.container_name = `sealgen-test-mongo-${timestamp}`;
|
|
32
|
+
console.log("\tcloning sealious-minimal...");
|
|
33
|
+
await execAsync(
|
|
34
|
+
`git clone https://hub.sealcode.org/source/sealious-minimal.git ${test_app.app_path} && cd ${test_app.app_path} && git checkout ${sealious_minimal_commit}`
|
|
35
|
+
);
|
|
36
|
+
console.log("\tinstalling dependencies and sealgen...");
|
|
37
|
+
await execAsync(
|
|
38
|
+
`cd ${
|
|
39
|
+
test_app.app_path
|
|
40
|
+
} && npm ci rm -rf node_modules/@sealcode/sealgen && ln -s $OLDPWD node_modules/@sealcode/sealgen && ${callSealgenBin()} make-env`
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
console.log(
|
|
44
|
+
"\tstarting the database...(if timeouts here, run 'docker pull mongo:4.4-bionic' to solve)"
|
|
45
|
+
);
|
|
46
|
+
test_app.app_port = await getPort();
|
|
47
|
+
return test_app;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async start() {
|
|
51
|
+
this.mongo_port = await getPort();
|
|
52
|
+
await execAsync(
|
|
53
|
+
`docker run -p ${this.mongo_port}:27017 --name ${this.container_name} -d mongo:4.4-bionic`
|
|
54
|
+
);
|
|
55
|
+
const env = {
|
|
56
|
+
...process.env,
|
|
57
|
+
SEALIOUS_MONGO_PORT: this.mongo_port.toString(),
|
|
58
|
+
SEALIOUS_PORT: this.app_port.toString(),
|
|
59
|
+
};
|
|
60
|
+
const node_args = [`${this.app_path}/dist/back/index.js`];
|
|
61
|
+
console.log(
|
|
62
|
+
"EXECUTING APP",
|
|
63
|
+
`${Object.entries(env)
|
|
64
|
+
.map(([key, value]) => `${key}=${value}`)
|
|
65
|
+
.join(" ")} node ${node_args.join(" ")}`
|
|
66
|
+
);
|
|
67
|
+
const app_start = spawn(`node`, node_args, {
|
|
68
|
+
env,
|
|
69
|
+
});
|
|
70
|
+
app_start.stdout.on("data", (data) =>
|
|
71
|
+
console.log("APP OUTPUT:", data.toString())
|
|
72
|
+
);
|
|
73
|
+
app_start.stderr.on("data", (data) => console.log(data.toString()));
|
|
74
|
+
app_start.on("exit", (code) => {
|
|
75
|
+
this.emitter.emit("exit", code);
|
|
76
|
+
this.emitter.emit("process_error");
|
|
77
|
+
});
|
|
78
|
+
if (app_start.pid == undefined) throw new Error("node failed to start");
|
|
79
|
+
this.node_pid = app_start.pid;
|
|
80
|
+
await waitPort({
|
|
81
|
+
host: "localhost",
|
|
82
|
+
port: this.app_port,
|
|
83
|
+
output: "silent",
|
|
84
|
+
timeout: 6000,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async close() {
|
|
89
|
+
try {
|
|
90
|
+
if (this.node_pid != 0) await execAsync(`kill -9 ${this.node_pid}`);
|
|
91
|
+
} catch (error) {
|
|
92
|
+
throw new Error(
|
|
93
|
+
"node process exited unexpectedly, please inspect the output"
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
await Promise.all([
|
|
97
|
+
fs.rm(this.app_path, { recursive: true, force: true }),
|
|
98
|
+
execAsync(`docker rm -f ${this.container_name}`),
|
|
99
|
+
]);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async runSealgenCommand(command: string) {
|
|
103
|
+
await execAsync(
|
|
104
|
+
`cd ${
|
|
105
|
+
this.app_path
|
|
106
|
+
} && ${callSealgenBin()} ${command} && ${callSealgenBin()} build`
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async throwIfItKillsTheApp<T>(cb: () => Promise<T>): Promise<T> {
|
|
111
|
+
let finished = false;
|
|
112
|
+
// eslint-disable-next-line @typescript-eslint/no-misused-promises, no-async-promise-executor
|
|
113
|
+
return new Promise(async (resolve, reject) => {
|
|
114
|
+
const error_handler = () => {
|
|
115
|
+
if (!finished) {
|
|
116
|
+
reject();
|
|
117
|
+
}
|
|
118
|
+
finished = true;
|
|
119
|
+
};
|
|
120
|
+
this.emitter.addListener("process_error", error_handler);
|
|
121
|
+
try {
|
|
122
|
+
const result = await cb();
|
|
123
|
+
if (!finished) {
|
|
124
|
+
resolve(result);
|
|
125
|
+
finished = true;
|
|
126
|
+
}
|
|
127
|
+
} catch (e) {
|
|
128
|
+
if (!finished) {
|
|
129
|
+
reject(e);
|
|
130
|
+
finished = true;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
this.emitter.removeListener("process_error", error_handler);
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async httpGet(path: string): Promise<{ status: number; response: string }> {
|
|
138
|
+
return this.throwIfItKillsTheApp(async () => {
|
|
139
|
+
const ret = await fetch(`http://localhost:${this.app_port}${path}`);
|
|
140
|
+
const status = ret.status;
|
|
141
|
+
const response = await ret.text();
|
|
142
|
+
return { status, response };
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
async httpPOST(
|
|
147
|
+
path: string,
|
|
148
|
+
body: Record<string, unknown>
|
|
149
|
+
): Promise<{ status: number; response: string }> {
|
|
150
|
+
return this.throwIfItKillsTheApp(async () => {
|
|
151
|
+
const ret = await fetch(
|
|
152
|
+
`http://localhost:${this.app_port}${path}`,
|
|
153
|
+
{
|
|
154
|
+
method: "POST",
|
|
155
|
+
headers: { "Content-Type": "application/json" },
|
|
156
|
+
body: JSON.stringify(body),
|
|
157
|
+
}
|
|
158
|
+
);
|
|
159
|
+
const status = ret.status;
|
|
160
|
+
const response = await ret.text();
|
|
161
|
+
|
|
162
|
+
console.log("request done!");
|
|
163
|
+
console.log({ response });
|
|
164
|
+
return { status, response };
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
}
|
|
@@ -10,11 +10,14 @@ function getModifierClasses(modifiers: string[]) {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
export function inputWrapper(
|
|
13
|
+
type: string,
|
|
13
14
|
modifiers: string[],
|
|
14
15
|
input: FlatTemplatable
|
|
15
16
|
): Readable {
|
|
16
17
|
return tempstream/* HTML */ `<div
|
|
17
|
-
class="form-input form-input__wrapper ${getModifierClasses(
|
|
18
|
+
class="form-input form-input--type-${type} form-input__wrapper ${getModifierClasses(
|
|
19
|
+
modifiers
|
|
20
|
+
)}"
|
|
18
21
|
>
|
|
19
22
|
${input}
|
|
20
23
|
</div>`;
|