@solidxai/core 0.1.13-beta.4 → 0.1.13-beta.6

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 (118) hide show
  1. package/.claude/settings.local.json +16 -0
  2. package/CHANGELOG.md +1053 -0
  3. package/CLAUDE.md +26 -0
  4. package/dist/commands/run-tests.command.d.ts +7 -1
  5. package/dist/commands/run-tests.command.d.ts.map +1 -1
  6. package/dist/commands/run-tests.command.js +49 -3
  7. package/dist/commands/run-tests.command.js.map +1 -1
  8. package/dist/constants/chatter-message.constants.d.ts +1 -0
  9. package/dist/constants/chatter-message.constants.d.ts.map +1 -1
  10. package/dist/constants/chatter-message.constants.js +3 -1
  11. package/dist/constants/chatter-message.constants.js.map +1 -1
  12. package/dist/constants/user.constants.d.ts +2 -0
  13. package/dist/constants/user.constants.d.ts.map +1 -0
  14. package/dist/constants/user.constants.js +5 -0
  15. package/dist/constants/user.constants.js.map +1 -0
  16. package/dist/controllers/chatter-message.controller.d.ts +5 -0
  17. package/dist/controllers/chatter-message.controller.d.ts.map +1 -1
  18. package/dist/controllers/chatter-message.controller.js +14 -0
  19. package/dist/controllers/chatter-message.controller.js.map +1 -1
  20. package/dist/entities/user.entity.d.ts.map +1 -1
  21. package/dist/entities/user.entity.js +36 -0
  22. package/dist/entities/user.entity.js.map +1 -1
  23. package/dist/seeders/seed-data/solid-core-metadata.json +12 -7
  24. package/dist/services/chatter-message.service.d.ts +10 -0
  25. package/dist/services/chatter-message.service.d.ts.map +1 -1
  26. package/dist/services/chatter-message.service.js +44 -3
  27. package/dist/services/chatter-message.service.js.map +1 -1
  28. package/dist/services/crud.service.d.ts +2 -0
  29. package/dist/services/crud.service.d.ts.map +1 -1
  30. package/dist/services/crud.service.js +13 -1
  31. package/dist/services/crud.service.js.map +1 -1
  32. package/dist/services/settings/default-settings-provider.service.d.ts +40 -0
  33. package/dist/services/settings/default-settings-provider.service.d.ts.map +1 -1
  34. package/dist/services/settings/default-settings-provider.service.js +22 -0
  35. package/dist/services/settings/default-settings-provider.service.js.map +1 -1
  36. package/dist/testing/adapters/ui/playwright-adapter.d.ts +5 -0
  37. package/dist/testing/adapters/ui/playwright-adapter.d.ts.map +1 -1
  38. package/dist/testing/adapters/ui/playwright-adapter.js +17 -1
  39. package/dist/testing/adapters/ui/playwright-adapter.js.map +1 -1
  40. package/dist/testing/adapters/ui/ui.types.d.ts +2 -0
  41. package/dist/testing/adapters/ui/ui.types.d.ts.map +1 -1
  42. package/dist/testing/adapters/ui/ui.types.js.map +1 -1
  43. package/dist/testing/steps/ui/actions.step.d.ts.map +1 -1
  44. package/dist/testing/steps/ui/actions.step.js +6 -2
  45. package/dist/testing/steps/ui/actions.step.js.map +1 -1
  46. package/dist/testing/steps/ui/assertions.step.d.ts.map +1 -1
  47. package/dist/testing/steps/ui/assertions.step.js +11 -3
  48. package/dist/testing/steps/ui/assertions.step.js.map +1 -1
  49. package/dist/testing/steps/ui/form.step.d.ts.map +1 -1
  50. package/dist/testing/steps/ui/form.step.js +9 -3
  51. package/dist/testing/steps/ui/form.step.js.map +1 -1
  52. package/dist/testing/steps/ui/manual.step.js +1 -1
  53. package/dist/testing/steps/ui/manual.step.js.map +1 -1
  54. package/dist/testing/steps/ui/navigation.step.d.ts.map +1 -1
  55. package/dist/testing/steps/ui/navigation.step.js +3 -1
  56. package/dist/testing/steps/ui/navigation.step.js.map +1 -1
  57. package/package.json +1 -1
  58. package/src/commands/run-tests.command.ts +47 -1
  59. package/src/constants/chatter-message.constants.ts +12 -0
  60. package/src/constants/user.constants.ts +13 -0
  61. package/src/controllers/chatter-message.controller.ts +8 -0
  62. package/src/entities/user.entity.ts +53 -36
  63. package/src/seeders/seed-data/solid-core-metadata.json +12 -7
  64. package/src/services/1.js +6 -0
  65. package/src/services/chatter-message.service.ts +86 -4
  66. package/src/services/crud.service.ts +31 -1
  67. package/src/services/settings/default-settings-provider.service.ts +22 -0
  68. package/src/testing/README.md +64 -2
  69. package/src/testing/adapters/ui/playwright-adapter.ts +25 -0
  70. package/src/testing/adapters/ui/ui.types.ts +2 -0
  71. package/src/testing/steps/ui/actions.step.ts +8 -4
  72. package/src/testing/steps/ui/assertions.step.ts +18 -5
  73. package/src/testing/steps/ui/form.step.ts +16 -6
  74. package/src/testing/steps/ui/manual.step.ts +1 -1
  75. package/src/testing/steps/ui/navigation.step.ts +4 -2
  76. package/dist-tests/api/authenticate.spec.js +0 -119
  77. package/dist-tests/api/authenticate.spec.js.map +0 -1
  78. package/dist-tests/api/crud-service.findOne.cityMaster.spec.js +0 -97
  79. package/dist-tests/api/crud-service.findOne.cityMaster.spec.js.map +0 -1
  80. package/dist-tests/api/ping.spec.js +0 -21
  81. package/dist-tests/api/ping.spec.js.map +0 -1
  82. package/dist-tests/helpers/auth.js +0 -41
  83. package/dist-tests/helpers/auth.js.map +0 -1
  84. package/dist-tests/helpers/env.js +0 -11
  85. package/dist-tests/helpers/env.js.map +0 -1
  86. package/docs/agent-builder/notebook-lm-prompt-for-agent-builder.md +0 -136
  87. package/docs/agent-builder/team-ready-note-agent-builder.md +0 -305
  88. package/docs/agent-hub-grooming.md +0 -301
  89. package/docs/dashboards/AGENTIC_DASHBOARD_IMPLEMENTATION_PLAN.md +0 -438
  90. package/docs/dashboards/dashboard-curl-smoke-tests.txt +0 -146
  91. package/docs/dashboards/delete-legacy-dashboard-metadata.sql +0 -172
  92. package/docs/datasource-introspection-ddl-analysis.md +0 -326
  93. package/docs/datasource-introspection-implementation-plan.md +0 -306
  94. package/docs/grouping-enhancements.md +0 -89
  95. package/docs/java-spring/README.md +0 -3
  96. package/docs/java-spring/solid-core-module-deep-dive-report.md +0 -1317
  97. package/docs/module-package-import-handoff.md +0 -691
  98. package/docs/seed-changes.md +0 -65
  99. package/docs/test-data-workflow.md +0 -200
  100. package/docs/type-declaration-import-issue.md +0 -24
  101. package/docs/workflow/business-automation-example-notes.md +0 -309
  102. package/docs/workflow/control-flow-node-addition-sop.md +0 -324
  103. package/docs/workflow/data-engineering-pipeline-example-notes.md +0 -330
  104. package/docs/workflow/foreach-example-notes.md +0 -187
  105. package/docs/workflow/hello-world-example-notes.md +0 -271
  106. package/docs/workflow/kestra-concepts-plugins-blueprints.md +0 -315
  107. package/docs/workflow/loop-until-example-notes.md +0 -198
  108. package/docs/workflow/microservices-and-apis-example-notes.md +0 -264
  109. package/docs/workflow/samples.md +0 -25
  110. package/docs/workflow/what-is-kestra.md +0 -79
  111. package/docs/workflow/workflow-core-module-handoff-summary.md +0 -191
  112. package/docs/workflow/workflow-module-brd.md +0 -185
  113. package/docs/workflow/workflow-module-domain-model.md +0 -252
  114. package/docs/workflow/workflow-module-metadata-dsl.md +0 -302
  115. package/docs/workflow/workflow-module-technical-spec.md +0 -293
  116. package/docs/workflow/workflow-node-type-contracts.md +0 -635
  117. package/docs/workflow/workflow-node-ui-metadata-contract.md +0 -1262
  118. package/docs/workflow/workflow-solidx-model-field-spec.md +0 -284
@@ -1 +1 @@
1
- {"version":3,"file":"assertions.step.d.ts","sourceRoot":"","sources":["../../../../src/testing/steps/ui/assertions.step.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAYxD,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,YAAY,GAAG,IAAI,CAgDnE"}
1
+ {"version":3,"file":"assertions.step.d.ts","sourceRoot":"","sources":["../../../../src/testing/steps/ui/assertions.step.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAiBxD,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,YAAY,GAAG,IAAI,CAwDnE"}
@@ -14,7 +14,10 @@ function registerAssertionSteps(registry) {
14
14
  if (!input.selector) {
15
15
  throw new Error('Missing "selector" in step.with for op "ui.expectVisible"');
16
16
  }
17
- await page.waitForSelector(input.selector, { state: "visible" });
17
+ await page.waitForSelector(input.selector, {
18
+ state: "visible",
19
+ timeout: ctx.ui?.resolveTimeout(input.timeoutMs),
20
+ });
18
21
  });
19
22
  registry.register("ui.expectHidden", async (ctx, step) => {
20
23
  const page = requirePage(ctx, "ui.expectHidden");
@@ -22,7 +25,10 @@ function registerAssertionSteps(registry) {
22
25
  if (!input.selector) {
23
26
  throw new Error('Missing "selector" in step.with for op "ui.expectHidden"');
24
27
  }
25
- await page.waitForSelector(input.selector, { state: "hidden" });
28
+ await page.waitForSelector(input.selector, {
29
+ state: "hidden",
30
+ timeout: ctx.ui?.resolveTimeout(input.timeoutMs),
31
+ });
26
32
  });
27
33
  registry.register("ui.expectText", async (ctx, step) => {
28
34
  const page = requirePage(ctx, "ui.expectText");
@@ -30,7 +36,9 @@ function registerAssertionSteps(registry) {
30
36
  if (!input.selector) {
31
37
  throw new Error('Missing "selector" in step.with for op "ui.expectText"');
32
38
  }
33
- const text = await page.locator(input.selector).innerText();
39
+ const text = await page
40
+ .locator(input.selector)
41
+ .innerText({ timeout: ctx.ui?.resolveTimeout(input.timeoutMs) });
34
42
  if (input.equals !== undefined) {
35
43
  if (text !== input.equals) {
36
44
  throw new Error(`Expected text to equal "${input.equals}" but got "${text}"`);
@@ -1 +1 @@
1
- {"version":3,"file":"assertions.step.js","sourceRoot":"","sources":["../../../../src/testing/steps/ui/assertions.step.ts"],"names":[],"mappings":";;AAcA,wDAgDC;AAvDD,SAAS,WAAW,CAAC,GAAgB,EAAE,EAAU;IAC/C,IAAI,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,sCAAsC,EAAE,GAAG,CAAC,CAAC;IAC/D,CAAC;IACD,OAAO,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC;AACrB,CAAC;AAED,SAAgB,sBAAsB,CAAC,QAAsB;IAC3D,QAAQ,CAAC,QAAQ,CAAC,kBAAkB,EAAE,KAAK,EAAE,GAAgB,EAAE,IAAY,EAAE,EAAE;QAC7E,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,EAAE,kBAAkB,CAAC,CAAC;QAClD,MAAM,KAAK,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAiB,CAAC;QAChD,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;YACpB,MAAM,IAAI,KAAK,CAAC,2DAA2D,CAAC,CAAC;QAC/E,CAAC;QACD,MAAM,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;IACnE,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,QAAQ,CAAC,iBAAiB,EAAE,KAAK,EAAE,GAAgB,EAAE,IAAY,EAAE,EAAE;QAC5E,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,EAAE,iBAAiB,CAAC,CAAC;QACjD,MAAM,KAAK,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAiB,CAAC;QAChD,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;YACpB,MAAM,IAAI,KAAK,CAAC,0DAA0D,CAAC,CAAC;QAC9E,CAAC;QACD,MAAM,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,QAAQ,CAAC,eAAe,EAAE,KAAK,EAAE,GAAgB,EAAE,IAAY,EAAE,EAAE;QAC1E,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC;QAC/C,MAAM,KAAK,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAoB,CAAC;QACnD,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;YACpB,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;QAC5E,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,SAAS,EAAE,CAAC;QAC5D,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAC/B,IAAI,IAAI,KAAK,KAAK,CAAC,MAAM,EAAE,CAAC;gBAC1B,MAAM,IAAI,KAAK,CACb,2BAA2B,KAAK,CAAC,MAAM,cAAc,IAAI,GAAG,CAC7D,CAAC;YACJ,CAAC;YACD,OAAO;QACT,CAAC;QACD,IAAI,KAAK,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;YACjC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;gBACnC,MAAM,IAAI,KAAK,CACb,6BAA6B,KAAK,CAAC,QAAQ,cAAc,IAAI,GAAG,CACjE,CAAC;YACJ,CAAC;YACD,OAAO;QACT,CAAC;QAED,MAAM,IAAI,KAAK,CACb,oEAAoE,CACrE,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["import type { TestContext } from \"../../contracts/runtime-context.types\";\nimport type { OpStep } from \"../../contracts/testing-metadata.types\";\nimport { StepRegistry } from \"../../core/step-registry\";\n\ntype VisibleInput = { selector: string };\ntype ExpectTextInput = { selector: string; equals?: string; contains?: string };\n\nfunction requirePage(ctx: TestContext, op: string) {\n if (!ctx.ui || !ctx.ui.page) {\n throw new Error(`Missing UI page on context for op \"${op}\"`);\n }\n return ctx.ui.page;\n}\n\nexport function registerAssertionSteps(registry: StepRegistry): void {\n registry.register(\"ui.expectVisible\", async (ctx: TestContext, step: OpStep) => {\n const page = requirePage(ctx, \"ui.expectVisible\");\n const input = (step.with ?? {}) as VisibleInput;\n if (!input.selector) {\n throw new Error('Missing \"selector\" in step.with for op \"ui.expectVisible\"');\n }\n await page.waitForSelector(input.selector, { state: \"visible\" });\n });\n\n registry.register(\"ui.expectHidden\", async (ctx: TestContext, step: OpStep) => {\n const page = requirePage(ctx, \"ui.expectHidden\");\n const input = (step.with ?? {}) as VisibleInput;\n if (!input.selector) {\n throw new Error('Missing \"selector\" in step.with for op \"ui.expectHidden\"');\n }\n await page.waitForSelector(input.selector, { state: \"hidden\" });\n });\n\n registry.register(\"ui.expectText\", async (ctx: TestContext, step: OpStep) => {\n const page = requirePage(ctx, \"ui.expectText\");\n const input = (step.with ?? {}) as ExpectTextInput;\n if (!input.selector) {\n throw new Error('Missing \"selector\" in step.with for op \"ui.expectText\"');\n }\n\n const text = await page.locator(input.selector).innerText();\n if (input.equals !== undefined) {\n if (text !== input.equals) {\n throw new Error(\n `Expected text to equal \"${input.equals}\" but got \"${text}\"`,\n );\n }\n return;\n }\n if (input.contains !== undefined) {\n if (!text.includes(input.contains)) {\n throw new Error(\n `Expected text to contain \"${input.contains}\" but got \"${text}\"`,\n );\n }\n return;\n }\n\n throw new Error(\n 'Missing \"equals\" or \"contains\" in step.with for op \"ui.expectText\"',\n );\n });\n}\n"]}
1
+ {"version":3,"file":"assertions.step.js","sourceRoot":"","sources":["../../../../src/testing/steps/ui/assertions.step.ts"],"names":[],"mappings":"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","sourcesContent":["import type { TestContext } from \"../../contracts/runtime-context.types\";\nimport type { OpStep } from \"../../contracts/testing-metadata.types\";\nimport { StepRegistry } from \"../../core/step-registry\";\n\ntype VisibleInput = { selector: string; timeoutMs?: number };\ntype ExpectTextInput = {\n selector: string;\n equals?: string;\n contains?: string;\n timeoutMs?: number;\n};\n\nfunction requirePage(ctx: TestContext, op: string) {\n if (!ctx.ui || !ctx.ui.page) {\n throw new Error(`Missing UI page on context for op \"${op}\"`);\n }\n return ctx.ui.page;\n}\n\nexport function registerAssertionSteps(registry: StepRegistry): void {\n registry.register(\"ui.expectVisible\", async (ctx: TestContext, step: OpStep) => {\n const page = requirePage(ctx, \"ui.expectVisible\");\n const input = (step.with ?? {}) as VisibleInput;\n if (!input.selector) {\n throw new Error('Missing \"selector\" in step.with for op \"ui.expectVisible\"');\n }\n await page.waitForSelector(input.selector, {\n state: \"visible\",\n timeout: ctx.ui?.resolveTimeout(input.timeoutMs),\n });\n });\n\n registry.register(\"ui.expectHidden\", async (ctx: TestContext, step: OpStep) => {\n const page = requirePage(ctx, \"ui.expectHidden\");\n const input = (step.with ?? {}) as VisibleInput;\n if (!input.selector) {\n throw new Error('Missing \"selector\" in step.with for op \"ui.expectHidden\"');\n }\n await page.waitForSelector(input.selector, {\n state: \"hidden\",\n timeout: ctx.ui?.resolveTimeout(input.timeoutMs),\n });\n });\n\n registry.register(\"ui.expectText\", async (ctx: TestContext, step: OpStep) => {\n const page = requirePage(ctx, \"ui.expectText\");\n const input = (step.with ?? {}) as ExpectTextInput;\n if (!input.selector) {\n throw new Error('Missing \"selector\" in step.with for op \"ui.expectText\"');\n }\n\n const text = await page\n .locator(input.selector)\n .innerText({ timeout: ctx.ui?.resolveTimeout(input.timeoutMs) });\n if (input.equals !== undefined) {\n if (text !== input.equals) {\n throw new Error(\n `Expected text to equal \"${input.equals}\" but got \"${text}\"`,\n );\n }\n return;\n }\n if (input.contains !== undefined) {\n if (!text.includes(input.contains)) {\n throw new Error(\n `Expected text to contain \"${input.contains}\" but got \"${text}\"`,\n );\n }\n return;\n }\n\n throw new Error(\n 'Missing \"equals\" or \"contains\" in step.with for op \"ui.expectText\"',\n );\n });\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"form.step.d.ts","sourceRoot":"","sources":["../../../../src/testing/steps/ui/form.step.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAaxD,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,YAAY,GAAG,IAAI,CAwC9D"}
1
+ {"version":3,"file":"form.step.d.ts","sourceRoot":"","sources":["../../../../src/testing/steps/ui/form.step.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAiBxD,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,YAAY,GAAG,IAAI,CA8C9D"}
@@ -51,7 +51,9 @@ function registerFormSteps(registry) {
51
51
  if (input.value === undefined) {
52
52
  throw new Error('Missing "value" in step.with for op "ui.fill"');
53
53
  }
54
- await page.fill(input.selector, String(input.value));
54
+ await page.fill(input.selector, String(input.value), {
55
+ timeout: ctx.ui?.resolveTimeout(input.timeoutMs),
56
+ });
55
57
  });
56
58
  registry.register("ui.select", async (ctx, step) => {
57
59
  const page = requirePage(ctx, "ui.select");
@@ -62,7 +64,9 @@ function registerFormSteps(registry) {
62
64
  if (input.value === undefined) {
63
65
  throw new Error('Missing "value" in step.with for op "ui.select"');
64
66
  }
65
- await page.selectOption(input.selector, String(input.value));
67
+ await page.selectOption(input.selector, String(input.value), {
68
+ timeout: ctx.ui?.resolveTimeout(input.timeoutMs),
69
+ });
66
70
  });
67
71
  registry.register("ui.uploadFile", async (ctx, step) => {
68
72
  const page = requirePage(ctx, "ui.uploadFile");
@@ -77,7 +81,9 @@ function registerFormSteps(registry) {
77
81
  const resolved = Array.isArray(input.filePath)
78
82
  ? input.filePath.map((p) => path.resolve(cwd, String(p)))
79
83
  : path.resolve(cwd, String(input.filePath));
80
- await page.setInputFiles(input.selector, resolved);
84
+ await page.setInputFiles(input.selector, resolved, {
85
+ timeout: ctx.ui?.resolveTimeout(input.timeoutMs),
86
+ });
81
87
  });
82
88
  }
83
89
  //# sourceMappingURL=form.step.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"form.step.js","sourceRoot":"","sources":["../../../../src/testing/steps/ui/form.step.ts"],"names":[],"mappings":"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","sourcesContent":["import * as path from \"path\";\n\nimport type { TestContext } from \"../../contracts/runtime-context.types\";\nimport type { OpStep } from \"../../contracts/testing-metadata.types\";\nimport { StepRegistry } from \"../../core/step-registry\";\n\ntype FillInput = { selector: string; value: string };\ntype SelectInput = { selector: string; value: string };\ntype UploadFileInput = { selector: string; filePath: string | string[] };\n\nfunction requirePage(ctx: TestContext, op: string) {\n if (!ctx.ui || !ctx.ui.page) {\n throw new Error(`Missing UI page on context for op \"${op}\"`);\n }\n return ctx.ui.page;\n}\n\nexport function registerFormSteps(registry: StepRegistry): void {\n registry.register(\"ui.fill\", async (ctx: TestContext, step: OpStep) => {\n const page = requirePage(ctx, \"ui.fill\");\n const input = (step.with ?? {}) as FillInput;\n if (!input.selector) {\n throw new Error('Missing \"selector\" in step.with for op \"ui.fill\"');\n }\n if (input.value === undefined) {\n throw new Error('Missing \"value\" in step.with for op \"ui.fill\"');\n }\n await page.fill(input.selector, String(input.value));\n });\n\n registry.register(\"ui.select\", async (ctx: TestContext, step: OpStep) => {\n const page = requirePage(ctx, \"ui.select\");\n const input = (step.with ?? {}) as SelectInput;\n if (!input.selector) {\n throw new Error('Missing \"selector\" in step.with for op \"ui.select\"');\n }\n if (input.value === undefined) {\n throw new Error('Missing \"value\" in step.with for op \"ui.select\"');\n }\n await page.selectOption(input.selector, String(input.value));\n });\n\n registry.register(\"ui.uploadFile\", async (ctx: TestContext, step: OpStep) => {\n const page = requirePage(ctx, \"ui.uploadFile\");\n const input = (step.with ?? {}) as UploadFileInput;\n if (!input.selector) {\n throw new Error('Missing \"selector\" in step.with for op \"ui.uploadFile\"');\n }\n if (input.filePath === undefined || input.filePath === null) {\n throw new Error('Missing \"filePath\" in step.with for op \"ui.uploadFile\"');\n }\n const cwd = process.cwd();\n const resolved = Array.isArray(input.filePath)\n ? input.filePath.map((p) => path.resolve(cwd, String(p)))\n : path.resolve(cwd, String(input.filePath));\n await page.setInputFiles(input.selector, resolved);\n });\n}\n"]}
1
+ {"version":3,"file":"form.step.js","sourceRoot":"","sources":["../../../../src/testing/steps/ui/form.step.ts"],"names":[],"mappings":"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","sourcesContent":["import * as path from \"path\";\n\nimport type { TestContext } from \"../../contracts/runtime-context.types\";\nimport type { OpStep } from \"../../contracts/testing-metadata.types\";\nimport { StepRegistry } from \"../../core/step-registry\";\n\ntype FillInput = { selector: string; value: string; timeoutMs?: number };\ntype SelectInput = { selector: string; value: string; timeoutMs?: number };\ntype UploadFileInput = {\n selector: string;\n filePath: string | string[];\n timeoutMs?: number;\n};\n\nfunction requirePage(ctx: TestContext, op: string) {\n if (!ctx.ui || !ctx.ui.page) {\n throw new Error(`Missing UI page on context for op \"${op}\"`);\n }\n return ctx.ui.page;\n}\n\nexport function registerFormSteps(registry: StepRegistry): void {\n registry.register(\"ui.fill\", async (ctx: TestContext, step: OpStep) => {\n const page = requirePage(ctx, \"ui.fill\");\n const input = (step.with ?? {}) as FillInput;\n if (!input.selector) {\n throw new Error('Missing \"selector\" in step.with for op \"ui.fill\"');\n }\n if (input.value === undefined) {\n throw new Error('Missing \"value\" in step.with for op \"ui.fill\"');\n }\n await page.fill(input.selector, String(input.value), {\n timeout: ctx.ui?.resolveTimeout(input.timeoutMs),\n });\n });\n\n registry.register(\"ui.select\", async (ctx: TestContext, step: OpStep) => {\n const page = requirePage(ctx, \"ui.select\");\n const input = (step.with ?? {}) as SelectInput;\n if (!input.selector) {\n throw new Error('Missing \"selector\" in step.with for op \"ui.select\"');\n }\n if (input.value === undefined) {\n throw new Error('Missing \"value\" in step.with for op \"ui.select\"');\n }\n await page.selectOption(input.selector, String(input.value), {\n timeout: ctx.ui?.resolveTimeout(input.timeoutMs),\n });\n });\n\n registry.register(\"ui.uploadFile\", async (ctx: TestContext, step: OpStep) => {\n const page = requirePage(ctx, \"ui.uploadFile\");\n const input = (step.with ?? {}) as UploadFileInput;\n if (!input.selector) {\n throw new Error('Missing \"selector\" in step.with for op \"ui.uploadFile\"');\n }\n if (input.filePath === undefined || input.filePath === null) {\n throw new Error('Missing \"filePath\" in step.with for op \"ui.uploadFile\"');\n }\n const cwd = process.cwd();\n const resolved = Array.isArray(input.filePath)\n ? input.filePath.map((p) => path.resolve(cwd, String(p)))\n : path.resolve(cwd, String(input.filePath));\n await page.setInputFiles(input.selector, resolved, {\n timeout: ctx.ui?.resolveTimeout(input.timeoutMs),\n });\n });\n}\n"]}
@@ -15,7 +15,7 @@ function registerManualSteps(registry) {
15
15
  const inputConfig = (step.with ?? {});
16
16
  const message = inputConfig.message?.trim() || "Manual interaction required.";
17
17
  const prompt = inputConfig.prompt?.trim() || "Press Enter to continue...";
18
- const timeoutMs = inputConfig.timeoutMs;
18
+ const timeoutMs = ctx.ui?.resolveTimeout(inputConfig.timeoutMs);
19
19
  const bringToFront = inputConfig.bringToFront ?? true;
20
20
  if (ctx.ui?.isHeadless()) {
21
21
  throw new Error('Op "ui.waitForManual" requires headed mode. Re-run with --headless false.');
@@ -1 +1 @@
1
- {"version":3,"file":"manual.step.js","sourceRoot":"","sources":["../../../../src/testing/steps/ui/manual.step.ts"],"names":[],"mappings":"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","sourcesContent":["import { createInterface } from \"node:readline/promises\";\nimport { stdin as input, stdout as output } from \"node:process\";\n\nimport type { TestContext } from \"../../contracts/runtime-context.types\";\nimport type { OpStep } from \"../../contracts/testing-metadata.types\";\nimport { StepRegistry } from \"../../core/step-registry\";\n\ntype WaitForManualInput = {\n message?: string;\n prompt?: string;\n waitForSelector?: string;\n waitForUrlContains?: string;\n waitForUrlEquals?: string;\n timeoutMs?: number;\n bringToFront?: boolean;\n};\n\nfunction requirePage(ctx: TestContext, op: string) {\n if (!ctx.ui || !ctx.ui.page) {\n throw new Error(`Missing UI page on context for op \"${op}\"`);\n }\n return ctx.ui.page;\n}\n\nexport function registerManualSteps(registry: StepRegistry): void {\n registry.register(\"ui.waitForManual\", async (ctx: TestContext, step: OpStep) => {\n const page = requirePage(ctx, \"ui.waitForManual\");\n const inputConfig = (step.with ?? {}) as WaitForManualInput;\n const message = inputConfig.message?.trim() || \"Manual interaction required.\";\n const prompt = inputConfig.prompt?.trim() || \"Press Enter to continue...\";\n const timeoutMs = inputConfig.timeoutMs;\n const bringToFront = inputConfig.bringToFront ?? true;\n\n if (ctx.ui?.isHeadless()) {\n throw new Error('Op \"ui.waitForManual\" requires headed mode. Re-run with --headless false.');\n }\n if (!input.isTTY || !output.isTTY) {\n throw new Error('Op \"ui.waitForManual\" requires an interactive terminal (TTY).');\n }\n\n if (bringToFront) {\n await page.bringToFront();\n }\n\n console.log(\"\");\n console.log(\"════════ Manual Interaction Required ════════\");\n console.log(message);\n console.log(`Scenario: ${ctx.scenarioId}`);\n console.log(`Current URL: ${page.url()}`);\n console.log(prompt);\n\n const rl = createInterface({ input, output });\n try {\n await rl.question(\"\");\n } finally {\n rl.close();\n }\n\n if (inputConfig.waitForSelector) {\n await page.waitForSelector(inputConfig.waitForSelector, {\n state: \"visible\",\n timeout: timeoutMs,\n });\n }\n\n if (inputConfig.waitForUrlEquals) {\n await page.waitForURL(\n (url) => url.toString() === inputConfig.waitForUrlEquals,\n { timeout: timeoutMs },\n );\n }\n\n if (inputConfig.waitForUrlContains) {\n await page.waitForURL(\n (url) => url.toString().includes(String(inputConfig.waitForUrlContains)),\n { timeout: timeoutMs },\n );\n }\n });\n}\n"]}
1
+ {"version":3,"file":"manual.step.js","sourceRoot":"","sources":["../../../../src/testing/steps/ui/manual.step.ts"],"names":[],"mappings":"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","sourcesContent":["import { createInterface } from \"node:readline/promises\";\nimport { stdin as input, stdout as output } from \"node:process\";\n\nimport type { TestContext } from \"../../contracts/runtime-context.types\";\nimport type { OpStep } from \"../../contracts/testing-metadata.types\";\nimport { StepRegistry } from \"../../core/step-registry\";\n\ntype WaitForManualInput = {\n message?: string;\n prompt?: string;\n waitForSelector?: string;\n waitForUrlContains?: string;\n waitForUrlEquals?: string;\n timeoutMs?: number;\n bringToFront?: boolean;\n};\n\nfunction requirePage(ctx: TestContext, op: string) {\n if (!ctx.ui || !ctx.ui.page) {\n throw new Error(`Missing UI page on context for op \"${op}\"`);\n }\n return ctx.ui.page;\n}\n\nexport function registerManualSteps(registry: StepRegistry): void {\n registry.register(\"ui.waitForManual\", async (ctx: TestContext, step: OpStep) => {\n const page = requirePage(ctx, \"ui.waitForManual\");\n const inputConfig = (step.with ?? {}) as WaitForManualInput;\n const message = inputConfig.message?.trim() || \"Manual interaction required.\";\n const prompt = inputConfig.prompt?.trim() || \"Press Enter to continue...\";\n const timeoutMs = ctx.ui?.resolveTimeout(inputConfig.timeoutMs);\n const bringToFront = inputConfig.bringToFront ?? true;\n\n if (ctx.ui?.isHeadless()) {\n throw new Error('Op \"ui.waitForManual\" requires headed mode. Re-run with --headless false.');\n }\n if (!input.isTTY || !output.isTTY) {\n throw new Error('Op \"ui.waitForManual\" requires an interactive terminal (TTY).');\n }\n\n if (bringToFront) {\n await page.bringToFront();\n }\n\n console.log(\"\");\n console.log(\"════════ Manual Interaction Required ════════\");\n console.log(message);\n console.log(`Scenario: ${ctx.scenarioId}`);\n console.log(`Current URL: ${page.url()}`);\n console.log(prompt);\n\n const rl = createInterface({ input, output });\n try {\n await rl.question(\"\");\n } finally {\n rl.close();\n }\n\n if (inputConfig.waitForSelector) {\n await page.waitForSelector(inputConfig.waitForSelector, {\n state: \"visible\",\n timeout: timeoutMs,\n });\n }\n\n if (inputConfig.waitForUrlEquals) {\n await page.waitForURL(\n (url) => url.toString() === inputConfig.waitForUrlEquals,\n { timeout: timeoutMs },\n );\n }\n\n if (inputConfig.waitForUrlContains) {\n await page.waitForURL(\n (url) => url.toString().includes(String(inputConfig.waitForUrlContains)),\n { timeout: timeoutMs },\n );\n }\n });\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"navigation.step.d.ts","sourceRoot":"","sources":["../../../../src/testing/steps/ui/navigation.step.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAYxD,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,YAAY,GAAG,IAAI,CAsCpE"}
1
+ {"version":3,"file":"navigation.step.d.ts","sourceRoot":"","sources":["../../../../src/testing/steps/ui/navigation.step.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAYxD,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,YAAY,GAAG,IAAI,CAwCpE"}
@@ -15,7 +15,9 @@ function registerNavigationSteps(registry) {
15
15
  throw new Error('Missing "url" in step.with for op "ui.goto"');
16
16
  }
17
17
  const url = ctx.ui?.resolveUrl(input.url) ?? input.url;
18
- await page.goto(url);
18
+ await page.goto(url, {
19
+ timeout: ctx.ui?.resolveNavigationTimeout(input.timeoutMs),
20
+ });
19
21
  });
20
22
  registry.register("ui.expectUrl", async (ctx, step) => {
21
23
  const page = requirePage(ctx, "ui.expectUrl");
@@ -1 +1 @@
1
- {"version":3,"file":"navigation.step.js","sourceRoot":"","sources":["../../../../src/testing/steps/ui/navigation.step.ts"],"names":[],"mappings":";;AAcA,0DAsCC;AA7CD,SAAS,WAAW,CAAC,GAAgB,EAAE,EAAU;IAC/C,IAAI,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,sCAAsC,EAAE,GAAG,CAAC,CAAC;IAC/D,CAAC;IACD,OAAO,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC;AACrB,CAAC;AAED,SAAgB,uBAAuB,CAAC,QAAsB;IAC5D,QAAQ,CAAC,QAAQ,CAAC,SAAS,EAAE,KAAK,EAAE,GAAgB,EAAE,IAAY,EAAE,EAAE;QACpE,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QACzC,MAAM,KAAK,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAc,CAAC;QAC7C,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;QACjE,CAAC;QACD,MAAM,GAAG,GAAG,GAAG,CAAC,EAAE,EAAE,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,GAAG,CAAC;QACvD,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACvB,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,QAAQ,CAAC,cAAc,EAAE,KAAK,EAAE,GAAgB,EAAE,IAAY,EAAE,EAAE;QACzE,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;QAC9C,MAAM,KAAK,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAmB,CAAC;QAClD,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAE3B,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAC/B,IAAI,OAAO,KAAK,KAAK,CAAC,MAAM,EAAE,CAAC;gBAC7B,MAAM,IAAI,KAAK,CACb,0BAA0B,KAAK,CAAC,MAAM,cAAc,OAAO,GAAG,CAC/D,CAAC;YACJ,CAAC;YACD,OAAO;QACT,CAAC;QAED,IAAI,KAAK,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;YACjC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;gBACtC,MAAM,IAAI,KAAK,CACb,4BAA4B,KAAK,CAAC,QAAQ,cAAc,OAAO,GAAG,CACnE,CAAC;YACJ,CAAC;YACD,OAAO;QACT,CAAC;QAED,MAAM,IAAI,KAAK,CACb,mEAAmE,CACpE,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["import type { TestContext } from \"../../contracts/runtime-context.types\";\nimport type { OpStep } from \"../../contracts/testing-metadata.types\";\nimport { StepRegistry } from \"../../core/step-registry\";\n\ntype GotoInput = { url: string };\ntype ExpectUrlInput = { equals?: string; contains?: string };\n\nfunction requirePage(ctx: TestContext, op: string) {\n if (!ctx.ui || !ctx.ui.page) {\n throw new Error(`Missing UI page on context for op \"${op}\"`);\n }\n return ctx.ui.page;\n}\n\nexport function registerNavigationSteps(registry: StepRegistry): void {\n registry.register(\"ui.goto\", async (ctx: TestContext, step: OpStep) => {\n const page = requirePage(ctx, \"ui.goto\");\n const input = (step.with ?? {}) as GotoInput;\n if (!input.url) {\n throw new Error('Missing \"url\" in step.with for op \"ui.goto\"');\n }\n const url = ctx.ui?.resolveUrl(input.url) ?? input.url;\n await page.goto(url);\n });\n\n registry.register(\"ui.expectUrl\", async (ctx: TestContext, step: OpStep) => {\n const page = requirePage(ctx, \"ui.expectUrl\");\n const input = (step.with ?? {}) as ExpectUrlInput;\n const current = page.url();\n\n if (input.equals !== undefined) {\n if (current !== input.equals) {\n throw new Error(\n `Expected URL to equal \"${input.equals}\" but got \"${current}\"`,\n );\n }\n return;\n }\n\n if (input.contains !== undefined) {\n if (!current.includes(input.contains)) {\n throw new Error(\n `Expected URL to contain \"${input.contains}\" but got \"${current}\"`,\n );\n }\n return;\n }\n\n throw new Error(\n 'Missing \"equals\" or \"contains\" in step.with for op \"ui.expectUrl\"',\n );\n });\n}\n"]}
1
+ {"version":3,"file":"navigation.step.js","sourceRoot":"","sources":["../../../../src/testing/steps/ui/navigation.step.ts"],"names":[],"mappings":";;AAcA,0DAwCC;AA/CD,SAAS,WAAW,CAAC,GAAgB,EAAE,EAAU;IAC/C,IAAI,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,sCAAsC,EAAE,GAAG,CAAC,CAAC;IAC/D,CAAC;IACD,OAAO,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC;AACrB,CAAC;AAED,SAAgB,uBAAuB,CAAC,QAAsB;IAC5D,QAAQ,CAAC,QAAQ,CAAC,SAAS,EAAE,KAAK,EAAE,GAAgB,EAAE,IAAY,EAAE,EAAE;QACpE,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QACzC,MAAM,KAAK,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAc,CAAC;QAC7C,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;QACjE,CAAC;QACD,MAAM,GAAG,GAAG,GAAG,CAAC,EAAE,EAAE,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,GAAG,CAAC;QACvD,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE;YACnB,OAAO,EAAE,GAAG,CAAC,EAAE,EAAE,wBAAwB,CAAC,KAAK,CAAC,SAAS,CAAC;SAC3D,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,QAAQ,CAAC,cAAc,EAAE,KAAK,EAAE,GAAgB,EAAE,IAAY,EAAE,EAAE;QACzE,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;QAC9C,MAAM,KAAK,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAmB,CAAC;QAClD,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAE3B,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAC/B,IAAI,OAAO,KAAK,KAAK,CAAC,MAAM,EAAE,CAAC;gBAC7B,MAAM,IAAI,KAAK,CACb,0BAA0B,KAAK,CAAC,MAAM,cAAc,OAAO,GAAG,CAC/D,CAAC;YACJ,CAAC;YACD,OAAO;QACT,CAAC;QAED,IAAI,KAAK,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;YACjC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;gBACtC,MAAM,IAAI,KAAK,CACb,4BAA4B,KAAK,CAAC,QAAQ,cAAc,OAAO,GAAG,CACnE,CAAC;YACJ,CAAC;YACD,OAAO;QACT,CAAC;QAED,MAAM,IAAI,KAAK,CACb,mEAAmE,CACpE,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["import type { TestContext } from \"../../contracts/runtime-context.types\";\nimport type { OpStep } from \"../../contracts/testing-metadata.types\";\nimport { StepRegistry } from \"../../core/step-registry\";\n\ntype GotoInput = { url: string; timeoutMs?: number };\ntype ExpectUrlInput = { equals?: string; contains?: string };\n\nfunction requirePage(ctx: TestContext, op: string) {\n if (!ctx.ui || !ctx.ui.page) {\n throw new Error(`Missing UI page on context for op \"${op}\"`);\n }\n return ctx.ui.page;\n}\n\nexport function registerNavigationSteps(registry: StepRegistry): void {\n registry.register(\"ui.goto\", async (ctx: TestContext, step: OpStep) => {\n const page = requirePage(ctx, \"ui.goto\");\n const input = (step.with ?? {}) as GotoInput;\n if (!input.url) {\n throw new Error('Missing \"url\" in step.with for op \"ui.goto\"');\n }\n const url = ctx.ui?.resolveUrl(input.url) ?? input.url;\n await page.goto(url, {\n timeout: ctx.ui?.resolveNavigationTimeout(input.timeoutMs),\n });\n });\n\n registry.register(\"ui.expectUrl\", async (ctx: TestContext, step: OpStep) => {\n const page = requirePage(ctx, \"ui.expectUrl\");\n const input = (step.with ?? {}) as ExpectUrlInput;\n const current = page.url();\n\n if (input.equals !== undefined) {\n if (current !== input.equals) {\n throw new Error(\n `Expected URL to equal \"${input.equals}\" but got \"${current}\"`,\n );\n }\n return;\n }\n\n if (input.contains !== undefined) {\n if (!current.includes(input.contains)) {\n throw new Error(\n `Expected URL to contain \"${input.contains}\" but got \"${current}\"`,\n );\n }\n return;\n }\n\n throw new Error(\n 'Missing \"equals\" or \"contains\" in step.with for op \"ui.expectUrl\"',\n );\n });\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solidxai/core",
3
- "version": "0.1.13-beta.4",
3
+ "version": "0.1.13-beta.6",
4
4
  "description": "This module is a NestJS module containing all the required core providers required by a Solid application",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -7,6 +7,8 @@ import { WebhookReporter } from 'src/testing/reporter/webhook-reporter';
7
7
  import { runFromMetadata } from 'src/testing/runner/run-from-metadata';
8
8
  import type { TestingMetadata } from 'src/testing/contracts/testing-metadata.types';
9
9
  import { SpecRegistry } from 'src/testing/core/spec-registry';
10
+ import { SettingService } from 'src/services/setting.service';
11
+ import type { SolidCoreSetting } from 'src/services/settings/default-settings-provider.service';
10
12
 
11
13
  interface TestRunCommandOptions {
12
14
  module?: string;
@@ -18,6 +20,8 @@ interface TestRunCommandOptions {
18
20
  apiBaseUrl?: string;
19
21
  uiBaseUrl?: string;
20
22
  headless?: boolean;
23
+ uiTimeoutMs?: number;
24
+ uiNavigationTimeoutMs?: number;
21
25
  timeoutMs?: number;
22
26
  retries?: number;
23
27
  listSpecs?: boolean;
@@ -34,6 +38,7 @@ export class TestRunCommand extends CommandRunner {
34
38
 
35
39
  constructor(
36
40
  private readonly moduleMetadataHelperService: ModuleMetadataHelperService,
41
+ private readonly settingService: SettingService,
37
42
  ) {
38
43
  super();
39
44
  }
@@ -89,6 +94,14 @@ export class TestRunCommand extends CommandRunner {
89
94
  const apiBaseUrl = options?.apiBaseUrl ?? process.env.BASE_URL;
90
95
  const uiBaseUrl = options?.uiBaseUrl ?? process.env.FRONTEND_BASE_URL;
91
96
  const headless = options?.headless ?? true;
97
+ const uiTimeoutMs =
98
+ options?.uiTimeoutMs
99
+ ?? toPositiveNumber(process.env.UI_TIMEOUT_MS)
100
+ ?? toPositiveNumber(this.settingService.getConfigValue<SolidCoreSetting>('uiTestDefaultTimeoutMs'));
101
+ const uiNavigationTimeoutMs =
102
+ options?.uiNavigationTimeoutMs
103
+ ?? toPositiveNumber(process.env.UI_NAVIGATION_TIMEOUT_MS)
104
+ ?? toPositiveNumber(this.settingService.getConfigValue<SolidCoreSetting>('uiTestNavigationTimeoutMs'));
92
105
  const printApiLogs = options?.printApiLogs ?? false;
93
106
 
94
107
  const webhookUrl = process.env.SOLIDCTL_WEBHOOK_URL;
@@ -105,7 +118,12 @@ export class TestRunCommand extends CommandRunner {
105
118
  skipScenarioIds,
106
119
  reporter,
107
120
  api: apiBaseUrl ? { baseUrl: apiBaseUrl } : undefined,
108
- ui: { baseUrl: uiBaseUrl, headless },
121
+ ui: {
122
+ baseUrl: uiBaseUrl,
123
+ headless,
124
+ defaultTimeoutMs: uiTimeoutMs,
125
+ navigationTimeoutMs: uiNavigationTimeoutMs,
126
+ },
109
127
  defaults: {
110
128
  timeoutMs: options?.timeoutMs,
111
129
  retries: options?.retries,
@@ -225,6 +243,26 @@ export class TestRunCommand extends CommandRunner {
225
243
  return val === 'false' ? false : true;
226
244
  }
227
245
 
246
+ @Option({
247
+ flags: '--ui-timeout-ms [number]',
248
+ description:
249
+ 'Default timeout in ms for UI steps (waits, clicks, fills). Defaults to process.env.UI_TIMEOUT_MS, then the uiTestDefaultTimeoutMs setting, then 30000.',
250
+ required: false,
251
+ })
252
+ parseUiTimeoutMs(val: string): number {
253
+ return Number(val);
254
+ }
255
+
256
+ @Option({
257
+ flags: '--ui-navigation-timeout-ms [number]',
258
+ description:
259
+ 'Default timeout in ms for ui.goto navigation. Defaults to process.env.UI_NAVIGATION_TIMEOUT_MS, then the uiTestNavigationTimeoutMs setting, then --ui-timeout-ms.',
260
+ required: false,
261
+ })
262
+ parseUiNavigationTimeoutMs(val: string): number {
263
+ return Number(val);
264
+ }
265
+
228
266
  @Option({
229
267
  flags: '--timeout-ms [number]',
230
268
  description: 'Default scenario timeout in milliseconds.',
@@ -262,6 +300,14 @@ function splitCsv(value?: string): string[] | undefined {
262
300
  return items.length ? items : undefined;
263
301
  }
264
302
 
303
+ // Accepts unknown because settings come back already parsed as numbers, while env
304
+ // vars arrive as strings.
305
+ function toPositiveNumber(value?: unknown): number | undefined {
306
+ if (value === null || value === undefined || value === '') return undefined;
307
+ const parsed = Number(value);
308
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : undefined;
309
+ }
310
+
265
311
  function resolveSpecPath(entry: string, metadataPath: string): string {
266
312
  if (path.isAbsolute(entry)) {
267
313
  return entry;
@@ -1,3 +1,5 @@
1
+ import { USER_SUMMARY_FIELDS } from 'src/constants/user.constants';
2
+
1
3
  export const CHATTER_MESSAGE_TYPE = {
2
4
  AUDIT: 'audit',
3
5
  CUSTOM: 'custom',
@@ -16,3 +18,13 @@ export const CHATTER_MESSAGE_STATUS = {
16
18
  PENDING: 'pending',
17
19
  COMPLETED: 'completed',
18
20
  } as const;
21
+
22
+ /**
23
+ * Columns of the message author exposed by the chatter APIs.
24
+ *
25
+ * The chatter panel renders `fullName` (name label + initials avatar) and uses `id`
26
+ * for the own-note edit check; the admin list/form/tree views display `fullName` via
27
+ * each view's `coModelFieldToDisplay`. That is exactly the platform-wide summary
28
+ * shape, so this is an alias rather than a second list to keep in step.
29
+ */
30
+ export const CHATTER_MESSAGE_USER_FIELDS = USER_SUMMARY_FIELDS;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * The columns of a `User` that are safe to embed in another entity's API response.
3
+ *
4
+ * Whenever a user is attached to a record the caller asked for - a `createdBy` /
5
+ * `updatedBy` populate, a chatter message author - the caller wants to display who it
6
+ * was, not to read their profile. Everything else on `User` is PII (email, mobile),
7
+ * authz metadata (roles, apiKeys) or secret, and the embedded user is frequently
8
+ * someone the viewer has no other visibility of.
9
+ *
10
+ * `fullName` is nullable; consumers that need a fallback should use the record's own
11
+ * context rather than widening this list.
12
+ */
13
+ export const USER_SUMMARY_FIELDS = ['id', 'fullName'] as const;
@@ -95,6 +95,14 @@ export class ChatterMessageController {
95
95
  return this.service.find(query);
96
96
  }
97
97
 
98
+ @ApiBearerAuth("jwt")
99
+ @ApiQuery({ name: 'search', required: false, type: String })
100
+ @ApiQuery({ name: 'limit', required: false, type: Number })
101
+ @Get('mentionable-users')
102
+ async getMentionableUsers(@Query() query: any) {
103
+ return this.service.getMentionableUsers(query?.search, query?.limit ? +query.limit : undefined);
104
+ }
105
+
98
106
  @ApiBearerAuth("jwt")
99
107
  @Get(':id')
100
108
  async findOne(@Param('id') id: string, @Query() query: any) {
@@ -5,6 +5,23 @@ import { UserViewMetadata } from 'src/entities/user-view-metadata.entity'
5
5
  import { UserApiKey } from 'src/entities/user-api-key.entity'
6
6
  import { Exclude, Expose } from "class-transformer";
7
7
 
8
+ /**
9
+ * Serialization is locked down twice over, and both halves are load-bearing:
10
+ *
11
+ * - The class-level `@Exclude()` below makes User itself an allowlist: only `@Expose()`d
12
+ * properties are serialized.
13
+ * - Every other property additionally carries its own `@Exclude()`. That looks redundant
14
+ * here, and for `User` it is - but class-transformer resolves the *class-level* strategy
15
+ * with a direct lookup on the exact constructor (MetadataStorage.getStrategy), so a
16
+ * subclass inherits nothing and falls back to `exposeAll`. Since this entity is a
17
+ * `@TableInheritance` root that consuming projects extend (see
18
+ * IExtensionUserCreationProvider), an extension user that forgets its own `@Exclude()`
19
+ * would otherwise serialize the password hash and every OAuth/verification token.
20
+ * Property-level metadata *is* walked up the prototype chain, so the per-property
21
+ * decorators are what actually protect subclasses.
22
+ *
23
+ * Keep both. Adding a new column? Give it `@Expose()` or `@Exclude()` - never neither.
24
+ */
8
25
  @Entity("ss_user")
9
26
  @TableInheritance({ column: { type: "varchar", name: "type", default: "User" } })
10
27
  @Exclude()
@@ -30,7 +47,7 @@ export class User extends CommonEntity {
30
47
  mobile: string;
31
48
 
32
49
  @Column({ type: "varchar", nullable: true })
33
- // don't send to client
50
+ @Exclude()
34
51
  password: string;
35
52
 
36
53
  @Column({ nullable: true, default: true })
@@ -38,67 +55,67 @@ export class User extends CommonEntity {
38
55
  forcePasswordChange: boolean = true;
39
56
 
40
57
  @Column({ type: "varchar", default: "local" })
41
- // don't send to client
58
+ @Exclude()
42
59
  lastLoginProvider: string = "local";
43
60
 
44
61
  @Column({ type: "varchar", nullable: true })
45
- // don't send to client (test)
62
+ @Exclude()
46
63
  accessCode: string;
47
64
 
48
65
  @Column({ type: "varchar", nullable: true })
49
- // don't send to client
66
+ @Exclude()
50
67
  googleAccessToken: string;
51
68
 
52
69
  @Column({ type: "varchar", nullable: true })
53
- // don't send to client
70
+ @Exclude()
54
71
  googleId: string;
55
72
 
56
73
  @Column({ type: "varchar", nullable: true })
57
- // don't send to client
74
+ @Exclude()
58
75
  googleProfilePicture: string;
59
76
 
60
77
  @Column({ type: "varchar", nullable: true })
61
- // don't send to client
78
+ @Exclude()
62
79
  facebookId: string;
63
80
 
64
81
  @Column({ type: "varchar", nullable: true })
65
- // don't send to client
82
+ @Exclude()
66
83
  facebookAccessToken: string;
67
84
 
68
85
  @Column({ type: "varchar", nullable: true })
69
- // don't send to client
86
+ @Exclude()
70
87
  facebookProfilePicture: string;
71
88
 
72
89
  @Column({ type: "varchar", nullable: true })
73
- // don't send to client
90
+ @Exclude()
74
91
  appleId: string;
75
92
 
76
93
  @Column({ type: "varchar", nullable: true })
77
- // don't send to client
94
+ @Exclude()
78
95
  appleAccessToken: string;
79
96
 
80
97
  @Column({ type: "varchar", nullable: true })
81
- // don't send to client
98
+ @Exclude()
82
99
  microsoftId: string;
83
100
 
84
101
  @Column({ type: "varchar", nullable: true })
85
- // don't send to client
102
+ @Exclude()
86
103
  microsoftAccessToken: string;
87
104
 
88
105
  @Column({ type: "varchar", nullable: true })
89
- // don't send to client
106
+ @Exclude()
90
107
  microsoftProfilePicture: string;
91
108
 
92
109
  @Column({ type: "varchar", nullable: true })
93
- // don't send to client
110
+ @Exclude()
94
111
  microsoftActiveDirectoryId: string;
95
112
 
96
113
  @Column({ type: "varchar", nullable: true })
97
- // don't send to client
114
+ @Exclude()
98
115
  microsoftActiveDirectoryAccessToken: string;
99
116
 
100
117
  @Column({ type: "varchar", nullable: true })
101
- // don't send to client
118
+ @Exclude()
102
119
  microsoftActiveDirectoryProfilePicture: string;
103
120
 
104
121
  @Index()
@@ -107,63 +124,63 @@ export class User extends CommonEntity {
107
124
  active: boolean = true;
108
125
 
109
126
  @Column({ nullable: true })
110
- // don't send to client
127
+ @Exclude()
111
128
  forgotPasswordConfirmedAt: Date;
112
129
 
113
130
  @Column({ type: "varchar", nullable: true })
114
- // don't send to client
131
+ @Exclude()
115
132
  verificationTokenOnForgotPassword: string;
116
133
 
117
134
  @Column({ nullable: true })
118
- // don't send to client
135
+ @Exclude()
119
136
  verificationTokenOnForgotPasswordExpiresAt: Date;
120
137
 
121
138
  @Column({ nullable: true })
122
- // don't send to client
139
+ @Exclude()
123
140
  emailVerifiedOnRegistrationAt: Date;
124
141
 
125
142
  @Column({ type: "varchar", nullable: true })
126
- // don't send to client
143
+ @Exclude()
127
144
  emailVerificationTokenOnRegistration: string;
128
145
 
129
146
  @Column({ nullable: true })
130
- // don't send to client
147
+ @Exclude()
131
148
  emailVerificationTokenOnRegistrationExpiresAt: Date;
132
149
 
133
150
  @Column({ nullable: true })
134
- // don't send to client
151
+ @Exclude()
135
152
  mobileVerifiedOnRegistrationAt: Date;
136
153
 
137
154
  @Column({ type: "varchar", nullable: true })
138
- // don't send to client
155
+ @Exclude()
139
156
  mobileVerificationTokenOnRegistration: string;
140
157
 
141
158
  @Column({ nullable: true })
142
- // don't send to client
159
+ @Exclude()
143
160
  mobileVerificationTokenOnRegistrationExpiresAt: Date;
144
161
 
145
162
  @Column({ nullable: true })
146
- // don't send to client
163
+ @Exclude()
147
164
  emailVerifiedOnLoginAt: Date;
148
165
 
149
166
  @Column({ type: "varchar", nullable: true })
150
- // don't send to client
167
+ @Exclude()
151
168
  emailVerificationTokenOnLogin: string;
152
169
 
153
170
  @Column({ nullable: true })
154
- // don't send to client
171
+ @Exclude()
155
172
  emailVerificationTokenOnLoginExpiresAt: Date;
156
173
 
157
174
  @Column({ nullable: true })
158
- // don't send to client
175
+ @Exclude()
159
176
  mobileVerifiedOnLoginAt: Date;
160
177
 
161
178
  @Column({ type: "varchar", nullable: true })
162
- // don't send to client
179
+ @Exclude()
163
180
  mobileVerificationTokenOnLogin: string;
164
181
 
165
182
  @Column({ nullable: true })
166
- // don't send to client
183
+ @Exclude()
167
184
  mobileVerificationTokenOnLoginExpiresAt: Date;
168
185
 
169
186
  @Column({ type: "varchar", nullable: true })
@@ -176,18 +193,18 @@ export class User extends CommonEntity {
176
193
  roles: RoleMetadata[];
177
194
 
178
195
  @OneToMany(() => UserViewMetadata, userViewMetadata => userViewMetadata.user, { cascade: true })
179
- // don't send to client
196
+ @Exclude()
180
197
  userViewMetadata: UserViewMetadata[];
181
198
 
182
- // dont send to client
199
+ @Exclude()
183
200
  @Column({ type: "varchar", default: "bcrypt" })
184
201
  passwordScheme: string;
185
202
 
186
- // dont send to client
203
+ @Exclude()
187
204
  @Column({ type: "int", default: 1 })
188
205
  passwordSchemeVersion: number;
189
206
 
190
- // dont send to client
207
+ @Exclude()
191
208
  @Column({ nullable: true })
192
209
  rehashedAt: Date;
193
210