@open-mercato/core 0.6.2-develop.3439.1.94b9d9733c → 0.6.2-develop.3446.1.bd060c6017
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/.turbo/turbo-build.log +1 -1
- package/dist/generated/entities/entity_index_row/index.js +2 -0
- package/dist/generated/entities/entity_index_row/index.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +1 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/helpers/integration/queue-runner.js +58 -0
- package/dist/helpers/integration/queue-runner.js.map +7 -0
- package/dist/helpers/integration/queue.js +67 -0
- package/dist/helpers/integration/queue.js.map +7 -0
- package/dist/helpers/integration/salesUi.js +33 -15
- package/dist/helpers/integration/salesUi.js.map +2 -2
- package/dist/modules/customers/ai-agents.js +5 -2
- package/dist/modules/customers/ai-agents.js.map +2 -2
- package/dist/modules/customers/ai-tools/deals-pack.js +52 -8
- package/dist/modules/customers/ai-tools/deals-pack.js.map +2 -2
- package/dist/modules/customers/ai-tools/types.js.map +2 -2
- package/dist/modules/customers/api/people/[id]/route.js +10 -1
- package/dist/modules/customers/api/people/[id]/route.js.map +2 -2
- package/dist/modules/query_index/data/entities.js +13 -1
- package/dist/modules/query_index/data/entities.js.map +2 -2
- package/dist/modules/query_index/migrations/Migration20260518162054_query_index.js +43 -0
- package/dist/modules/query_index/migrations/Migration20260518162054_query_index.js.map +7 -0
- package/generated/entities/entity_index_row/index.ts +1 -0
- package/generated/entity-fields-registry.ts +1 -0
- package/package.json +8 -8
- package/src/helpers/integration/queue-runner.ts +72 -0
- package/src/helpers/integration/queue.ts +70 -0
- package/src/helpers/integration/salesUi.ts +38 -23
- package/src/modules/attachments/i18n/de.json +13 -0
- package/src/modules/attachments/i18n/en.json +13 -0
- package/src/modules/attachments/i18n/es.json +13 -0
- package/src/modules/attachments/i18n/pl.json +13 -0
- package/src/modules/customers/ai-agents.ts +4 -1
- package/src/modules/customers/ai-tools/deals-pack.ts +68 -10
- package/src/modules/customers/ai-tools/types.ts +6 -0
- package/src/modules/customers/api/people/[id]/route.ts +10 -1
- package/src/modules/query_index/data/entities.ts +12 -0
- package/src/modules/query_index/migrations/.snapshot-open-mercato.json +792 -343
- package/src/modules/query_index/migrations/Migration20260518162054_query_index.ts +43 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { bootstrapFromAppRoot } from "@open-mercato/shared/lib/bootstrap/dynamicLoader";
|
|
4
|
+
import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
|
|
5
|
+
import { createQueue } from "@open-mercato/queue";
|
|
6
|
+
async function drainQueueFromAppRoot(queueName, options) {
|
|
7
|
+
const data = await bootstrapFromAppRoot(options.appRoot);
|
|
8
|
+
const worker = data.modules.flatMap((module) => module.workers ?? []).find((entry) => entry.queue === queueName);
|
|
9
|
+
if (!worker) return 0;
|
|
10
|
+
const container = await createRequestContainer();
|
|
11
|
+
const queue = createQueue(queueName, "local", {
|
|
12
|
+
baseDir: path.resolve(options.appRoot, ".mercato/queue"),
|
|
13
|
+
concurrency: 1
|
|
14
|
+
});
|
|
15
|
+
const resolve = (name) => container.resolve(name);
|
|
16
|
+
try {
|
|
17
|
+
let processedJobs = 0;
|
|
18
|
+
while (true) {
|
|
19
|
+
const result = await queue.process(
|
|
20
|
+
async (job, ctx) => {
|
|
21
|
+
await Promise.resolve(worker.handler(job, { ...ctx, resolve }));
|
|
22
|
+
},
|
|
23
|
+
{ limit: options.jobLimit }
|
|
24
|
+
);
|
|
25
|
+
const handled = result.processed + result.failed;
|
|
26
|
+
processedJobs += handled;
|
|
27
|
+
if (handled === 0) return processedJobs;
|
|
28
|
+
}
|
|
29
|
+
} finally {
|
|
30
|
+
await queue.close();
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
async function runQueueDrainFromEnv() {
|
|
34
|
+
const queueName = process.env.OM_INTEGRATION_QUEUE_NAME?.trim();
|
|
35
|
+
const appRoot = process.env.OM_INTEGRATION_APP_ROOT?.trim();
|
|
36
|
+
const jobLimit = Number.parseInt(process.env.OM_INTEGRATION_QUEUE_JOB_LIMIT || "100", 10) || 100;
|
|
37
|
+
if (!queueName || !appRoot) throw new Error("Missing queue drain environment");
|
|
38
|
+
const processed = await drainQueueFromAppRoot(queueName, {
|
|
39
|
+
appRoot: path.resolve(appRoot),
|
|
40
|
+
jobLimit
|
|
41
|
+
});
|
|
42
|
+
console.log(JSON.stringify({ processed }));
|
|
43
|
+
}
|
|
44
|
+
const currentFile = fileURLToPath(import.meta.url);
|
|
45
|
+
const invokedFile = process.argv[1] ? path.resolve(process.argv[1]) : null;
|
|
46
|
+
if (invokedFile === currentFile) {
|
|
47
|
+
runQueueDrainFromEnv().then(() => {
|
|
48
|
+
process.exit(0);
|
|
49
|
+
}).catch((error) => {
|
|
50
|
+
console.error(error);
|
|
51
|
+
process.exit(1);
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
export {
|
|
55
|
+
drainQueueFromAppRoot,
|
|
56
|
+
runQueueDrainFromEnv
|
|
57
|
+
};
|
|
58
|
+
//# sourceMappingURL=queue-runner.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/helpers/integration/queue-runner.ts"],
|
|
4
|
+
"sourcesContent": ["import path from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport { bootstrapFromAppRoot } from '@open-mercato/shared/lib/bootstrap/dynamicLoader'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { createQueue } from '@open-mercato/queue'\n\nexport type QueueDrainRunnerOptions = {\n appRoot: string\n jobLimit: number\n}\n\nexport async function drainQueueFromAppRoot(\n queueName: string,\n options: QueueDrainRunnerOptions,\n): Promise<number> {\n const data = await bootstrapFromAppRoot(options.appRoot)\n const worker = data.modules\n .flatMap((module) => module.workers ?? [])\n .find((entry) => entry.queue === queueName)\n if (!worker) return 0\n\n const container = await createRequestContainer()\n const queue = createQueue(queueName, 'local', {\n baseDir: path.resolve(options.appRoot, '.mercato/queue'),\n concurrency: 1,\n })\n const resolve = <T = unknown>(name: string): T => container.resolve(name) as T\n\n try {\n let processedJobs = 0\n while (true) {\n const result = await queue.process(\n async (job, ctx) => {\n await Promise.resolve(worker.handler(job, { ...ctx, resolve }))\n },\n { limit: options.jobLimit },\n )\n const handled = result.processed + result.failed\n processedJobs += handled\n if (handled === 0) return processedJobs\n }\n } finally {\n await queue.close()\n }\n}\n\nexport async function runQueueDrainFromEnv(): Promise<void> {\n const queueName = process.env.OM_INTEGRATION_QUEUE_NAME?.trim()\n const appRoot = process.env.OM_INTEGRATION_APP_ROOT?.trim()\n const jobLimit = Number.parseInt(process.env.OM_INTEGRATION_QUEUE_JOB_LIMIT || '100', 10) || 100\n if (!queueName || !appRoot) throw new Error('Missing queue drain environment')\n\n const processed = await drainQueueFromAppRoot(queueName, {\n appRoot: path.resolve(appRoot),\n jobLimit,\n })\n console.log(JSON.stringify({ processed }))\n}\n\nconst currentFile = fileURLToPath(import.meta.url)\nconst invokedFile = process.argv[1] ? path.resolve(process.argv[1]) : null\n\nif (invokedFile === currentFile) {\n runQueueDrainFromEnv()\n .then(() => {\n process.exit(0)\n })\n .catch((error) => {\n console.error(error)\n process.exit(1)\n })\n}\n"],
|
|
5
|
+
"mappings": "AAAA,OAAO,UAAU;AACjB,SAAS,qBAAqB;AAC9B,SAAS,4BAA4B;AACrC,SAAS,8BAA8B;AACvC,SAAS,mBAAmB;AAO5B,eAAsB,sBACpB,WACA,SACiB;AACjB,QAAM,OAAO,MAAM,qBAAqB,QAAQ,OAAO;AACvD,QAAM,SAAS,KAAK,QACjB,QAAQ,CAAC,WAAW,OAAO,WAAW,CAAC,CAAC,EACxC,KAAK,CAAC,UAAU,MAAM,UAAU,SAAS;AAC5C,MAAI,CAAC,OAAQ,QAAO;AAEpB,QAAM,YAAY,MAAM,uBAAuB;AAC/C,QAAM,QAAQ,YAAY,WAAW,SAAS;AAAA,IAC5C,SAAS,KAAK,QAAQ,QAAQ,SAAS,gBAAgB;AAAA,IACvD,aAAa;AAAA,EACf,CAAC;AACD,QAAM,UAAU,CAAc,SAAoB,UAAU,QAAQ,IAAI;AAExE,MAAI;AACF,QAAI,gBAAgB;AACpB,WAAO,MAAM;AACX,YAAM,SAAS,MAAM,MAAM;AAAA,QACzB,OAAO,KAAK,QAAQ;AAClB,gBAAM,QAAQ,QAAQ,OAAO,QAAQ,KAAK,EAAE,GAAG,KAAK,QAAQ,CAAC,CAAC;AAAA,QAChE;AAAA,QACA,EAAE,OAAO,QAAQ,SAAS;AAAA,MAC5B;AACA,YAAM,UAAU,OAAO,YAAY,OAAO;AAC1C,uBAAiB;AACjB,UAAI,YAAY,EAAG,QAAO;AAAA,IAC5B;AAAA,EACF,UAAE;AACA,UAAM,MAAM,MAAM;AAAA,EACpB;AACF;AAEA,eAAsB,uBAAsC;AAC1D,QAAM,YAAY,QAAQ,IAAI,2BAA2B,KAAK;AAC9D,QAAM,UAAU,QAAQ,IAAI,yBAAyB,KAAK;AAC1D,QAAM,WAAW,OAAO,SAAS,QAAQ,IAAI,kCAAkC,OAAO,EAAE,KAAK;AAC7F,MAAI,CAAC,aAAa,CAAC,QAAS,OAAM,IAAI,MAAM,iCAAiC;AAE7E,QAAM,YAAY,MAAM,sBAAsB,WAAW;AAAA,IACvD,SAAS,KAAK,QAAQ,OAAO;AAAA,IAC7B;AAAA,EACF,CAAC;AACD,UAAQ,IAAI,KAAK,UAAU,EAAE,UAAU,CAAC,CAAC;AAC3C;AAEA,MAAM,cAAc,cAAc,YAAY,GAAG;AACjD,MAAM,cAAc,QAAQ,KAAK,CAAC,IAAI,KAAK,QAAQ,QAAQ,KAAK,CAAC,CAAC,IAAI;AAEtE,IAAI,gBAAgB,aAAa;AAC/B,uBAAqB,EAClB,KAAK,MAAM;AACV,YAAQ,KAAK,CAAC;AAAA,EAChB,CAAC,EACA,MAAM,CAAC,UAAU;AAChB,YAAQ,MAAM,KAAK;AACnB,YAAQ,KAAK,CAAC;AAAA,EAChB,CAAC;AACL;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { drainQueueFromAppRoot } from "./queue-runner.js";
|
|
5
|
+
const DEFAULT_JOB_LIMIT = 100;
|
|
6
|
+
function resolveAppRoot(input) {
|
|
7
|
+
return path.resolve(input?.trim() || process.env.OM_TEST_APP_ROOT?.trim() || path.resolve(process.cwd(), "apps/mercato"));
|
|
8
|
+
}
|
|
9
|
+
async function drainQueueInCurrentProcess(queueName, options) {
|
|
10
|
+
return drainQueueFromAppRoot(queueName, options);
|
|
11
|
+
}
|
|
12
|
+
function resolveAppRunnerPath(appRoot) {
|
|
13
|
+
const requireFromApp = createRequire(path.join(appRoot, "package.json"));
|
|
14
|
+
return requireFromApp.resolve("@open-mercato/core/helpers/integration/queue-runner");
|
|
15
|
+
}
|
|
16
|
+
function drainQueueInAppProcess(queueName, options) {
|
|
17
|
+
return new Promise((resolve, reject) => {
|
|
18
|
+
const child = spawn(process.execPath, [resolveAppRunnerPath(options.appRoot)], {
|
|
19
|
+
cwd: options.appRoot,
|
|
20
|
+
env: {
|
|
21
|
+
...process.env,
|
|
22
|
+
OM_INTEGRATION_APP_ROOT: options.appRoot,
|
|
23
|
+
OM_INTEGRATION_QUEUE_NAME: queueName,
|
|
24
|
+
OM_INTEGRATION_QUEUE_JOB_LIMIT: String(options.jobLimit),
|
|
25
|
+
QUEUE_BASE_DIR: path.resolve(options.appRoot, ".mercato/queue")
|
|
26
|
+
},
|
|
27
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
28
|
+
});
|
|
29
|
+
let stdout = "";
|
|
30
|
+
let stderr = "";
|
|
31
|
+
child.stdout.on("data", (chunk) => {
|
|
32
|
+
stdout += chunk.toString();
|
|
33
|
+
});
|
|
34
|
+
child.stderr.on("data", (chunk) => {
|
|
35
|
+
stderr += chunk.toString();
|
|
36
|
+
});
|
|
37
|
+
child.on("error", reject);
|
|
38
|
+
child.on("close", (code) => {
|
|
39
|
+
if (code !== 0) {
|
|
40
|
+
reject(new Error(`Queue drain failed for "${queueName}" in ${options.appRoot} (exit ${code}).
|
|
41
|
+
${stderr || stdout}`));
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const lastLine = stdout.trim().split(/\r?\n/).reverse().find((line) => line.trim().startsWith("{")) ?? "{}";
|
|
45
|
+
try {
|
|
46
|
+
const parsed = JSON.parse(lastLine);
|
|
47
|
+
resolve(Number(parsed.processed ?? 0));
|
|
48
|
+
} catch {
|
|
49
|
+
resolve(0);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
async function drainIntegrationQueue(queueName, options = {}) {
|
|
55
|
+
const resolved = {
|
|
56
|
+
appRoot: resolveAppRoot(options.appRoot),
|
|
57
|
+
jobLimit: options.jobLimit ?? DEFAULT_JOB_LIMIT
|
|
58
|
+
};
|
|
59
|
+
if (process.env.OM_TEST_APP_ROOT?.trim()) {
|
|
60
|
+
return drainQueueInAppProcess(queueName, resolved);
|
|
61
|
+
}
|
|
62
|
+
return drainQueueInCurrentProcess(queueName, resolved);
|
|
63
|
+
}
|
|
64
|
+
export {
|
|
65
|
+
drainIntegrationQueue
|
|
66
|
+
};
|
|
67
|
+
//# sourceMappingURL=queue.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/helpers/integration/queue.ts"],
|
|
4
|
+
"sourcesContent": ["import { spawn } from 'node:child_process'\nimport { createRequire } from 'node:module'\nimport path from 'node:path'\nimport { drainQueueFromAppRoot } from './queue-runner'\n\ntype DrainQueueOptions = {\n appRoot?: string\n jobLimit?: number\n}\n\nconst DEFAULT_JOB_LIMIT = 100\n\nfunction resolveAppRoot(input?: string): string {\n return path.resolve(input?.trim() || process.env.OM_TEST_APP_ROOT?.trim() || path.resolve(process.cwd(), 'apps/mercato'))\n}\n\nasync function drainQueueInCurrentProcess(queueName: string, options: Required<DrainQueueOptions>): Promise<number> {\n return drainQueueFromAppRoot(queueName, options)\n}\n\nfunction resolveAppRunnerPath(appRoot: string): string {\n const requireFromApp = createRequire(path.join(appRoot, 'package.json'))\n return requireFromApp.resolve('@open-mercato/core/helpers/integration/queue-runner')\n}\n\nfunction drainQueueInAppProcess(queueName: string, options: Required<DrainQueueOptions>): Promise<number> {\n return new Promise((resolve, reject) => {\n const child = spawn(process.execPath, [resolveAppRunnerPath(options.appRoot)], {\n cwd: options.appRoot,\n env: {\n ...process.env,\n OM_INTEGRATION_APP_ROOT: options.appRoot,\n OM_INTEGRATION_QUEUE_NAME: queueName,\n OM_INTEGRATION_QUEUE_JOB_LIMIT: String(options.jobLimit),\n QUEUE_BASE_DIR: path.resolve(options.appRoot, '.mercato/queue'),\n },\n stdio: ['ignore', 'pipe', 'pipe'],\n })\n\n let stdout = ''\n let stderr = ''\n child.stdout.on('data', (chunk) => { stdout += chunk.toString() })\n child.stderr.on('data', (chunk) => { stderr += chunk.toString() })\n child.on('error', reject)\n child.on('close', (code) => {\n if (code !== 0) {\n reject(new Error(`Queue drain failed for \"${queueName}\" in ${options.appRoot} (exit ${code}).\\n${stderr || stdout}`))\n return\n }\n const lastLine = stdout.trim().split(/\\r?\\n/).reverse().find((line) => line.trim().startsWith('{')) ?? '{}'\n try {\n const parsed = JSON.parse(lastLine) as { processed?: unknown }\n resolve(Number(parsed.processed ?? 0))\n } catch {\n resolve(0)\n }\n })\n })\n}\n\nexport async function drainIntegrationQueue(queueName: string, options: DrainQueueOptions = {}): Promise<number> {\n const resolved = {\n appRoot: resolveAppRoot(options.appRoot),\n jobLimit: options.jobLimit ?? DEFAULT_JOB_LIMIT,\n }\n if (process.env.OM_TEST_APP_ROOT?.trim()) {\n return drainQueueInAppProcess(queueName, resolved)\n }\n return drainQueueInCurrentProcess(queueName, resolved)\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,aAAa;AACtB,SAAS,qBAAqB;AAC9B,OAAO,UAAU;AACjB,SAAS,6BAA6B;AAOtC,MAAM,oBAAoB;AAE1B,SAAS,eAAe,OAAwB;AAC9C,SAAO,KAAK,QAAQ,OAAO,KAAK,KAAK,QAAQ,IAAI,kBAAkB,KAAK,KAAK,KAAK,QAAQ,QAAQ,IAAI,GAAG,cAAc,CAAC;AAC1H;AAEA,eAAe,2BAA2B,WAAmB,SAAuD;AAClH,SAAO,sBAAsB,WAAW,OAAO;AACjD;AAEA,SAAS,qBAAqB,SAAyB;AACrD,QAAM,iBAAiB,cAAc,KAAK,KAAK,SAAS,cAAc,CAAC;AACvE,SAAO,eAAe,QAAQ,qDAAqD;AACrF;AAEA,SAAS,uBAAuB,WAAmB,SAAuD;AACxG,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,QAAQ,MAAM,QAAQ,UAAU,CAAC,qBAAqB,QAAQ,OAAO,CAAC,GAAG;AAAA,MAC7E,KAAK,QAAQ;AAAA,MACb,KAAK;AAAA,QACH,GAAG,QAAQ;AAAA,QACX,yBAAyB,QAAQ;AAAA,QACjC,2BAA2B;AAAA,QAC3B,gCAAgC,OAAO,QAAQ,QAAQ;AAAA,QACvD,gBAAgB,KAAK,QAAQ,QAAQ,SAAS,gBAAgB;AAAA,MAChE;AAAA,MACA,OAAO,CAAC,UAAU,QAAQ,MAAM;AAAA,IAClC,CAAC;AAED,QAAI,SAAS;AACb,QAAI,SAAS;AACb,UAAM,OAAO,GAAG,QAAQ,CAAC,UAAU;AAAE,gBAAU,MAAM,SAAS;AAAA,IAAE,CAAC;AACjE,UAAM,OAAO,GAAG,QAAQ,CAAC,UAAU;AAAE,gBAAU,MAAM,SAAS;AAAA,IAAE,CAAC;AACjE,UAAM,GAAG,SAAS,MAAM;AACxB,UAAM,GAAG,SAAS,CAAC,SAAS;AAC1B,UAAI,SAAS,GAAG;AACd,eAAO,IAAI,MAAM,2BAA2B,SAAS,QAAQ,QAAQ,OAAO,UAAU,IAAI;AAAA,EAAO,UAAU,MAAM,EAAE,CAAC;AACpH;AAAA,MACF;AACA,YAAM,WAAW,OAAO,KAAK,EAAE,MAAM,OAAO,EAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,KAAK,KAAK,EAAE,WAAW,GAAG,CAAC,KAAK;AACvG,UAAI;AACF,cAAM,SAAS,KAAK,MAAM,QAAQ;AAClC,gBAAQ,OAAO,OAAO,aAAa,CAAC,CAAC;AAAA,MACvC,QAAQ;AACN,gBAAQ,CAAC;AAAA,MACX;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AACH;AAEA,eAAsB,sBAAsB,WAAmB,UAA6B,CAAC,GAAoB;AAC/G,QAAM,WAAW;AAAA,IACf,SAAS,eAAe,QAAQ,OAAO;AAAA,IACvC,UAAU,QAAQ,YAAY;AAAA,EAChC;AACA,MAAI,QAAQ,IAAI,kBAAkB,KAAK,GAAG;AACxC,WAAO,uBAAuB,WAAW,QAAQ;AAAA,EACnD;AACA,SAAO,2BAA2B,WAAW,QAAQ;AACvD;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -765,12 +765,19 @@ async function addCustomLine(page, options) {
|
|
|
765
765
|
await dialog.getByRole("textbox", { name: "0.00" }).fill(String(options.unitPriceGross));
|
|
766
766
|
await dialog.getByRole("textbox", { name: "1" }).fill(String(options.quantity));
|
|
767
767
|
if (options.taxClassName) {
|
|
768
|
-
const taxClassTrigger = dialog.locator('[role="combobox"]').
|
|
768
|
+
const taxClassTrigger = dialog.locator('[data-crud-field-id="taxRateId"] [role="combobox"]').first();
|
|
769
769
|
if (await taxClassTrigger.count() > 0) {
|
|
770
|
-
await taxClassTrigger.
|
|
771
|
-
const
|
|
772
|
-
|
|
773
|
-
|
|
770
|
+
await expect(taxClassTrigger).toBeEnabled({ timeout: TEST_WAIT_TIMEOUT_MS });
|
|
771
|
+
const selectedLabel = options.taxClassName.split("\u2022")[0].trim();
|
|
772
|
+
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
773
|
+
await taxClassTrigger.click();
|
|
774
|
+
const opt = page.getByRole("option", { name: options.taxClassName, exact: true }).first();
|
|
775
|
+
await opt.waitFor({ state: "visible", timeout: 3e3 });
|
|
776
|
+
await opt.click();
|
|
777
|
+
const selected = await taxClassTrigger.filter({ hasText: new RegExp(escapeRegExp(selectedLabel), "i") }).waitFor({ state: "visible", timeout: 1500 }).then(() => true, () => false);
|
|
778
|
+
if (selected) break;
|
|
779
|
+
}
|
|
780
|
+
await expect(taxClassTrigger).toContainText(selectedLabel, { timeout: TEST_WAIT_TIMEOUT_MS });
|
|
774
781
|
}
|
|
775
782
|
}
|
|
776
783
|
const submitButton = dialog.getByRole("button", { name: /Add item/i });
|
|
@@ -843,17 +850,28 @@ async function addAdjustment(page, options) {
|
|
|
843
850
|
TEST_WAIT_TIMEOUT_MS
|
|
844
851
|
);
|
|
845
852
|
await expect(kindTrigger).toBeEnabled({ timeout: TEST_WAIT_TIMEOUT_MS });
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
await
|
|
853
|
+
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
854
|
+
const currentLabel = await kindTrigger.textContent().catch(() => "");
|
|
855
|
+
if (currentLabel?.includes(kindLabel)) return;
|
|
856
|
+
const expanded = await kindTrigger.getAttribute("aria-expanded").catch(() => null);
|
|
857
|
+
if (expanded === "true") {
|
|
858
|
+
await page.keyboard.press("Escape").catch(() => {
|
|
859
|
+
});
|
|
860
|
+
}
|
|
861
|
+
await kindTrigger.click();
|
|
862
|
+
const kindOption = page.getByRole("option", { name: new RegExp(`^${escapeRegExp(kindLabel)}$`, "i") }).first();
|
|
863
|
+
const optionVisible = await kindOption.waitFor({ state: "visible", timeout: TEST_WAIT_TIMEOUT_MS }).then(() => true, () => false);
|
|
864
|
+
if (optionVisible) {
|
|
865
|
+
await kindOption.click({ force: true });
|
|
866
|
+
} else {
|
|
867
|
+
await page.keyboard.type(kindLabel.charAt(0));
|
|
868
|
+
await page.waitForTimeout(150);
|
|
869
|
+
await page.keyboard.press("Enter");
|
|
870
|
+
}
|
|
871
|
+
await expect(kindTrigger).toContainText(kindLabel, { timeout: 2e3 }).catch(() => {
|
|
872
|
+
});
|
|
855
873
|
}
|
|
856
|
-
await expect(kindTrigger).toContainText(kindLabel, { timeout:
|
|
874
|
+
await expect(kindTrigger).toContainText(kindLabel, { timeout: 5e3 });
|
|
857
875
|
};
|
|
858
876
|
await selectKindOption();
|
|
859
877
|
const labelInput = dialog.getByPlaceholder(/e\.g\. Shipping fee/i).first();
|