@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.
Files changed (36) hide show
  1. package/@types/src/add-collection.d.ts +1 -1
  2. package/@types/src/page/list.d.ts +1 -0
  3. package/@types/src/page/sealious-list.d.ts +1 -1
  4. package/@types/src/page/sealious-list.test.d.ts +1 -0
  5. package/@types/src/test_utils/exec_async.d.ts +1 -0
  6. package/@types/src/test_utils/test-on-real-app.d.ts +24 -0
  7. package/@types/src/utils/input-wrapper.d.ts +1 -1
  8. package/lib/add-collection.js +18 -8
  9. package/lib/add-route.test.js +9 -92
  10. package/lib/forms/controls/checkboxed-list-input.js +31 -22
  11. package/lib/forms/controls/checkboxed-list-input.test.js +173 -0
  12. package/lib/forms/controls/multiple-files.js +1 -0
  13. package/lib/forms/controls/simple-input.js +1 -0
  14. package/lib/forms/controls/tickable.js +1 -0
  15. package/lib/forms/fields/field.js +1 -1
  16. package/lib/page/list.js +1 -0
  17. package/lib/page/sealious-list.js +5 -2
  18. package/lib/page/sealious-list.test.js +24 -0
  19. package/lib/test_utils/exec_async.js +24 -0
  20. package/lib/test_utils/test-on-real-app.js +147 -0
  21. package/lib/utils/input-wrapper.js +4 -2
  22. package/package.json +1 -1
  23. package/src/add-collection.ts +23 -8
  24. package/src/add-route.test.ts +9 -99
  25. package/src/forms/controls/checkboxed-list-input.test.ts +189 -0
  26. package/src/forms/controls/checkboxed-list-input.ts +36 -23
  27. package/src/forms/controls/multiple-files.ts +1 -0
  28. package/src/forms/controls/simple-input.ts +1 -0
  29. package/src/forms/controls/tickable.ts +1 -0
  30. package/src/forms/fields/field.ts +3 -1
  31. package/src/page/list.ts +1 -1
  32. package/src/page/sealious-list.test.ts +25 -0
  33. package/src/page/sealious-list.ts +7 -3
  34. package/src/test_utils/exec_async.ts +26 -0
  35. package/src/test_utils/test-on-real-app.ts +167 -0
  36. package/src/utils/input-wrapper.ts +4 -1
@@ -1,5 +1,9 @@
1
1
  import { Context } from "koa";
2
- import { BaseListPageFields, ListPage } from "./list.js";
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(modifiers)}"
18
+ class="form-input form-input--type-${type} form-input__wrapper ${getModifierClasses(
19
+ modifiers
20
+ )}"
18
21
  >
19
22
  ${input}
20
23
  </div>`;