@iblai/iblai-js 1.20.13 → 1.20.15
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/dist/data-layer/playwright/tasks-tab-helpers.d.ts +6 -0
- package/dist/playwright/index.cjs +72 -49
- package/dist/playwright/index.cjs.map +1 -1
- package/dist/playwright/index.d.ts +4 -0
- package/dist/playwright/index.esm.js +72 -49
- package/dist/playwright/index.esm.js.map +1 -1
- package/dist/playwright/playwright/tasks-tab-helpers.d.ts +6 -0
- package/dist/web-containers/playwright/tasks-tab-helpers.d.ts +6 -0
- package/dist/web-containers/source/next/index.esm.js +2 -2
- package/dist/web-utils/playwright/tasks-tab-helpers.d.ts +6 -0
- package/package.json +3 -3
|
@@ -69,12 +69,16 @@ export declare const TASKS_LABELS: {
|
|
|
69
69
|
readonly startTimeInPast: "Start time must be in the future.";
|
|
70
70
|
};
|
|
71
71
|
readonly deleteDialog: {
|
|
72
|
+
/** `aria-label` on the dialog's `DialogContent` — scopes in-dialog locators. */
|
|
73
|
+
readonly dialogName: "delete-task";
|
|
72
74
|
readonly title: "Delete Task";
|
|
73
75
|
readonly delete: "Delete Task";
|
|
74
76
|
readonly deleting: "Deleting...";
|
|
75
77
|
readonly cancel: "Cancel";
|
|
76
78
|
};
|
|
77
79
|
readonly logDetails: {
|
|
80
|
+
/** `aria-label` on the dialog's `DialogContent` — scopes in-dialog locators. */
|
|
81
|
+
readonly dialogName: "log-details";
|
|
78
82
|
readonly title: "Log Details";
|
|
79
83
|
readonly status: "Status";
|
|
80
84
|
readonly createdAt: "Created";
|
|
@@ -152,11 +156,13 @@ export declare function scheduleTask(scope: Page | Locator, opts: {
|
|
|
152
156
|
}): Promise<void>;
|
|
153
157
|
/** Asserts the in-dialog past-time error is currently shown. */
|
|
154
158
|
export declare function expectScheduleStartTimeInPastError(scope: Page | Locator): Promise<void>;
|
|
159
|
+
export declare function getDeleteTaskDialog(scope: Page | Locator): Locator;
|
|
155
160
|
/**
|
|
156
161
|
* Click the trash icon on a task row and confirm the delete in the
|
|
157
162
|
* follow-up dialog.
|
|
158
163
|
*/
|
|
159
164
|
export declare function deleteTask(scope: Page | Locator, taskName: string): Promise<void>;
|
|
165
|
+
export declare function getLogDetailsDialog(scope: Page | Locator): Locator;
|
|
160
166
|
/**
|
|
161
167
|
* Wait for the logs panel to finish loading and assert that the selected
|
|
162
168
|
* task name is shown in the header.
|
|
@@ -1841,20 +1841,23 @@ async function openNewInstanceDialog(page) {
|
|
|
1841
1841
|
* @param gatewayToken - Optional auth token
|
|
1842
1842
|
*/
|
|
1843
1843
|
async function createInstance(page, { name, serverUrl, clawType = 'openclaw', gatewayToken, }) {
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
await page
|
|
1844
|
+
// Scope in-dialog queries to the dialog: "Name" also labels the mentor-name
|
|
1845
|
+
// field on the edit-mentor modal behind it, and "Create" is a generic name.
|
|
1846
|
+
const dialog = await openNewInstanceDialog(page);
|
|
1847
|
+
await dialog.getByLabel('Name').fill(name);
|
|
1848
|
+
await dialog.getByLabel('Server URL').fill(serverUrl);
|
|
1847
1849
|
if (clawType !== 'openclaw') {
|
|
1848
|
-
const typeSelect =
|
|
1850
|
+
const typeSelect = dialog.getByLabel('Type');
|
|
1849
1851
|
await typeSelect.click();
|
|
1852
|
+
// Radix renders the option list in a portal outside the dialog subtree.
|
|
1850
1853
|
const option = page.getByRole('option', { name: new RegExp(clawType, 'i') });
|
|
1851
1854
|
await test$1.expect(option).toBeVisible({ timeout: 5000 });
|
|
1852
1855
|
await option.click();
|
|
1853
1856
|
}
|
|
1854
1857
|
if (gatewayToken) {
|
|
1855
|
-
await
|
|
1858
|
+
await dialog.getByLabel('Gateway Token').fill(gatewayToken);
|
|
1856
1859
|
}
|
|
1857
|
-
const createButton =
|
|
1860
|
+
const createButton = dialog.getByRole('button', { name: /^Create$/i });
|
|
1858
1861
|
await test$1.expect(createButton).toBeEnabled({ timeout: 5000 });
|
|
1859
1862
|
await createButton.click();
|
|
1860
1863
|
await test$1.expect(page.getByText('Instance created', { exact: false })).toBeVisible({
|
|
@@ -1893,23 +1896,25 @@ async function openEditInstanceDialog(page, instanceName) {
|
|
|
1893
1896
|
* @param updates - Fields to update
|
|
1894
1897
|
*/
|
|
1895
1898
|
async function editInstance(page, instanceName, updates) {
|
|
1896
|
-
|
|
1899
|
+
// Scope in-dialog queries to the dialog (see createInstance).
|
|
1900
|
+
const dialog = await openEditInstanceDialog(page, instanceName);
|
|
1897
1901
|
if (updates.name !== undefined) {
|
|
1898
|
-
const nameInput =
|
|
1902
|
+
const nameInput = dialog.getByLabel('Name');
|
|
1899
1903
|
await nameInput.fill(updates.name);
|
|
1900
1904
|
}
|
|
1901
1905
|
if (updates.serverUrl !== undefined) {
|
|
1902
|
-
const urlInput =
|
|
1906
|
+
const urlInput = dialog.getByLabel('Server URL');
|
|
1903
1907
|
await urlInput.fill(updates.serverUrl);
|
|
1904
1908
|
}
|
|
1905
1909
|
if (updates.clawType !== undefined) {
|
|
1906
|
-
const typeSelect =
|
|
1910
|
+
const typeSelect = dialog.getByLabel('Type');
|
|
1907
1911
|
await typeSelect.click();
|
|
1912
|
+
// Radix renders the option list in a portal outside the dialog subtree.
|
|
1908
1913
|
const option = page.getByRole('option', { name: new RegExp(updates.clawType, 'i') });
|
|
1909
1914
|
await test$1.expect(option).toBeVisible({ timeout: 5000 });
|
|
1910
1915
|
await option.click();
|
|
1911
1916
|
}
|
|
1912
|
-
const saveButton =
|
|
1917
|
+
const saveButton = dialog.getByRole('button', { name: /^Save$/i });
|
|
1913
1918
|
await test$1.expect(saveButton).toBeEnabled({ timeout: 5000 });
|
|
1914
1919
|
await saveButton.click();
|
|
1915
1920
|
await test$1.expect(page.getByText('Instance updated', { exact: false })).toBeVisible({
|
|
@@ -2138,9 +2143,10 @@ async function openLLMProviderPicker(page) {
|
|
|
2138
2143
|
* @param modelName - The model name (e.g. 'claude-sonnet-4-5-20250929')
|
|
2139
2144
|
*/
|
|
2140
2145
|
async function selectLLMModel(page, providerName, modelName) {
|
|
2141
|
-
await openLLMProviderPicker(page);
|
|
2142
|
-
// Click the provider card
|
|
2143
|
-
|
|
2146
|
+
const providerDialog = await openLLMProviderPicker(page);
|
|
2147
|
+
// Click the provider card, scoped to the picker dialog (bare
|
|
2148
|
+
// page.getByRole('dialog') also matches the edit-mentor modal behind it).
|
|
2149
|
+
const providerCard = providerDialog.getByText(new RegExp(`^${providerName}$`, 'i'));
|
|
2144
2150
|
await test$1.expect(providerCard).toBeVisible({ timeout: 10000 });
|
|
2145
2151
|
await providerCard.click();
|
|
2146
2152
|
// Model selection modal ("LLM Selection")
|
|
@@ -2218,10 +2224,12 @@ async function openAgentPromptEditModal(page, field) {
|
|
|
2218
2224
|
* Edit an agent prompt field (opens the modal, types content, saves).
|
|
2219
2225
|
*/
|
|
2220
2226
|
async function editAgentPrompt(page, field, content) {
|
|
2221
|
-
|
|
2227
|
+
// Scope to the dialog so the contenteditable/textarea `.first()` and the
|
|
2228
|
+
// generic "Save" button don't match elements on the modal behind it.
|
|
2229
|
+
const dialog = await openAgentPromptEditModal(page, field);
|
|
2222
2230
|
// The RichTextEditor renders into a contenteditable element; fall back to textarea if simpler.
|
|
2223
|
-
const editorRegion =
|
|
2224
|
-
const textarea =
|
|
2231
|
+
const editorRegion = dialog.locator('[contenteditable="true"]').first();
|
|
2232
|
+
const textarea = dialog.locator('textarea').first();
|
|
2225
2233
|
const hasEditor = await editorRegion.isVisible().catch(() => false);
|
|
2226
2234
|
if (hasEditor) {
|
|
2227
2235
|
await editorRegion.click();
|
|
@@ -2232,7 +2240,7 @@ async function editAgentPrompt(page, field, content) {
|
|
|
2232
2240
|
else {
|
|
2233
2241
|
await textarea.fill(content);
|
|
2234
2242
|
}
|
|
2235
|
-
const saveButton =
|
|
2243
|
+
const saveButton = dialog.getByRole('button', { name: /^Save$/i });
|
|
2236
2244
|
await test$1.expect(saveButton).toBeEnabled({ timeout: 5000 });
|
|
2237
2245
|
await saveButton.click();
|
|
2238
2246
|
await test$1.expect(page.getByText(`${field} updated successfully`, { exact: false })).toBeVisible({
|
|
@@ -2363,10 +2371,15 @@ async function openNewSkillDialog(page) {
|
|
|
2363
2371
|
/**
|
|
2364
2372
|
* Type content into the RichTextEditor Instruction field.
|
|
2365
2373
|
* The editor renders as a contenteditable element.
|
|
2374
|
+
*
|
|
2375
|
+
* `dialog` scopes the lookups to the open skill modal so the
|
|
2376
|
+
* contenteditable `.first()` and the "Instruction" label don't match
|
|
2377
|
+
* elements on the modal behind it. The keyboard is page-global.
|
|
2366
2378
|
*/
|
|
2367
|
-
async function fillInstructionEditor(
|
|
2379
|
+
async function fillInstructionEditor(dialog, content) {
|
|
2380
|
+
const page = dialog.page();
|
|
2368
2381
|
// RichTextEditor uses a contenteditable region
|
|
2369
|
-
const editor =
|
|
2382
|
+
const editor = dialog.locator('[contenteditable="true"]').first();
|
|
2370
2383
|
const hasEditor = await editor.isVisible().catch(() => false);
|
|
2371
2384
|
if (hasEditor) {
|
|
2372
2385
|
await editor.click();
|
|
@@ -2378,34 +2391,35 @@ async function fillInstructionEditor(page, content) {
|
|
|
2378
2391
|
return;
|
|
2379
2392
|
}
|
|
2380
2393
|
// Fallback: a plain labelled input/textarea named "Instruction"
|
|
2381
|
-
const textarea =
|
|
2394
|
+
const textarea = dialog.getByLabel('Instruction');
|
|
2382
2395
|
await textarea.fill(content);
|
|
2383
2396
|
}
|
|
2384
2397
|
/**
|
|
2385
2398
|
* Fill the skill form fields (name, slug, description, version, instruction).
|
|
2386
|
-
* The form is used by both the New and Edit dialogs.
|
|
2399
|
+
* The form is used by both the New and Edit dialogs. `dialog` scopes every
|
|
2400
|
+
* field query to the open modal (see fillInstructionEditor).
|
|
2387
2401
|
* The Instruction field is a rich-text (markdown) editor.
|
|
2388
2402
|
*/
|
|
2389
|
-
async function fillSkillForm(
|
|
2390
|
-
await
|
|
2391
|
-
await
|
|
2403
|
+
async function fillSkillForm(dialog, values) {
|
|
2404
|
+
await dialog.getByLabel('Name').fill(values.name);
|
|
2405
|
+
await dialog.getByLabel('Slug').fill(values.slug);
|
|
2392
2406
|
if (values.version !== undefined) {
|
|
2393
|
-
await
|
|
2407
|
+
await dialog.getByLabel('Version').fill(values.version);
|
|
2394
2408
|
}
|
|
2395
2409
|
if (values.description !== undefined) {
|
|
2396
|
-
await
|
|
2410
|
+
await dialog.getByLabel('Description').fill(values.description);
|
|
2397
2411
|
}
|
|
2398
2412
|
if (values.instruction !== undefined) {
|
|
2399
|
-
await fillInstructionEditor(
|
|
2413
|
+
await fillInstructionEditor(dialog, values.instruction);
|
|
2400
2414
|
}
|
|
2401
2415
|
}
|
|
2402
2416
|
/**
|
|
2403
2417
|
* Create a new platform-level skill via the Skills tab.
|
|
2404
2418
|
*/
|
|
2405
2419
|
async function createSkill(page, values) {
|
|
2406
|
-
await openNewSkillDialog(page);
|
|
2407
|
-
await fillSkillForm(
|
|
2408
|
-
const createButton =
|
|
2420
|
+
const dialog = await openNewSkillDialog(page);
|
|
2421
|
+
await fillSkillForm(dialog, values);
|
|
2422
|
+
const createButton = dialog.getByRole('button', { name: /^Create$/i });
|
|
2409
2423
|
await test$1.expect(createButton).toBeEnabled({ timeout: 5000 });
|
|
2410
2424
|
await createButton.click();
|
|
2411
2425
|
await test$1.expect(page.getByText('Skill created', { exact: false })).toBeVisible({
|
|
@@ -2443,23 +2457,23 @@ async function openEditSkillDialog(page, skillName) {
|
|
|
2443
2457
|
* @param updates - Fields to update on the skill
|
|
2444
2458
|
*/
|
|
2445
2459
|
async function editSkill(page, skillName, updates) {
|
|
2446
|
-
await openEditSkillDialog(page, skillName);
|
|
2460
|
+
const dialog = await openEditSkillDialog(page, skillName);
|
|
2447
2461
|
if (updates.name !== undefined) {
|
|
2448
|
-
await
|
|
2462
|
+
await dialog.getByLabel('Name').fill(updates.name);
|
|
2449
2463
|
}
|
|
2450
2464
|
if (updates.slug !== undefined) {
|
|
2451
|
-
await
|
|
2465
|
+
await dialog.getByLabel('Slug').fill(updates.slug);
|
|
2452
2466
|
}
|
|
2453
2467
|
if (updates.version !== undefined) {
|
|
2454
|
-
await
|
|
2468
|
+
await dialog.getByLabel('Version').fill(updates.version);
|
|
2455
2469
|
}
|
|
2456
2470
|
if (updates.description !== undefined) {
|
|
2457
|
-
await
|
|
2471
|
+
await dialog.getByLabel('Description').fill(updates.description);
|
|
2458
2472
|
}
|
|
2459
2473
|
if (updates.instruction !== undefined) {
|
|
2460
|
-
await fillInstructionEditor(
|
|
2474
|
+
await fillInstructionEditor(dialog, updates.instruction);
|
|
2461
2475
|
}
|
|
2462
|
-
const saveButton =
|
|
2476
|
+
const saveButton = dialog.getByRole('button', { name: /^Save$/i });
|
|
2463
2477
|
await test$1.expect(saveButton).toBeEnabled({ timeout: 5000 });
|
|
2464
2478
|
await saveButton.click();
|
|
2465
2479
|
await test$1.expect(page.getByText('Skill updated', { exact: false })).toBeVisible({
|
|
@@ -4286,12 +4300,16 @@ const TASKS_LABELS = {
|
|
|
4286
4300
|
startTimeInPast: 'Start time must be in the future.',
|
|
4287
4301
|
},
|
|
4288
4302
|
deleteDialog: {
|
|
4303
|
+
/** `aria-label` on the dialog's `DialogContent` — scopes in-dialog locators. */
|
|
4304
|
+
dialogName: 'delete-task',
|
|
4289
4305
|
title: 'Delete Task',
|
|
4290
4306
|
delete: 'Delete Task',
|
|
4291
4307
|
deleting: 'Deleting...',
|
|
4292
4308
|
cancel: 'Cancel',
|
|
4293
4309
|
},
|
|
4294
4310
|
logDetails: {
|
|
4311
|
+
/** `aria-label` on the dialog's `DialogContent` — scopes in-dialog locators. */
|
|
4312
|
+
dialogName: 'log-details',
|
|
4295
4313
|
title: 'Log Details',
|
|
4296
4314
|
status: 'Status',
|
|
4297
4315
|
createdAt: 'Created',
|
|
@@ -4474,6 +4492,9 @@ async function expectScheduleStartTimeInPastError(scope) {
|
|
|
4474
4492
|
});
|
|
4475
4493
|
}
|
|
4476
4494
|
// ── Delete flow ────────────────────────────────────────────────────────
|
|
4495
|
+
function getDeleteTaskDialog(scope) {
|
|
4496
|
+
return asPage(scope).locator(`[role="dialog"][aria-label="${TASKS_LABELS.deleteDialog.dialogName}"]`);
|
|
4497
|
+
}
|
|
4477
4498
|
/**
|
|
4478
4499
|
* Click the trash icon on a task row and confirm the delete in the
|
|
4479
4500
|
* follow-up dialog.
|
|
@@ -4481,17 +4502,20 @@ async function expectScheduleStartTimeInPastError(scope) {
|
|
|
4481
4502
|
async function deleteTask(scope, taskName) {
|
|
4482
4503
|
const row = getTaskRow(scope, taskName);
|
|
4483
4504
|
await row.getByRole('button', { name: TASKS_LABELS.list.deleteTask }).click();
|
|
4484
|
-
|
|
4485
|
-
|
|
4505
|
+
// Scope to the dialog: the confirm button's "Delete Task" name collides
|
|
4506
|
+
// (case-insensitively) with the row trash buttons' "Delete task" label.
|
|
4507
|
+
const dialog = getDeleteTaskDialog(scope);
|
|
4508
|
+
const confirm = dialog.getByRole('button', { name: TASKS_LABELS.deleteDialog.delete });
|
|
4486
4509
|
await test$1.expect(confirm).toBeVisible({ timeout: 5000 });
|
|
4487
4510
|
await confirm.click();
|
|
4488
4511
|
// Wait for the dialog to close.
|
|
4489
|
-
await test$1.expect(
|
|
4490
|
-
timeout: 10000,
|
|
4491
|
-
});
|
|
4512
|
+
await test$1.expect(dialog).toBeHidden({ timeout: 10000 });
|
|
4492
4513
|
logger.info(`Deleted task: ${taskName}`);
|
|
4493
4514
|
}
|
|
4494
4515
|
// ── Logs panel + log details ───────────────────────────────────────────
|
|
4516
|
+
function getLogDetailsDialog(scope) {
|
|
4517
|
+
return asPage(scope).locator(`[role="dialog"][aria-label="${TASKS_LABELS.logDetails.dialogName}"]`);
|
|
4518
|
+
}
|
|
4495
4519
|
/**
|
|
4496
4520
|
* Wait for the logs panel to finish loading and assert that the selected
|
|
4497
4521
|
* task name is shown in the header.
|
|
@@ -4517,14 +4541,13 @@ async function openFirstLogDetails(scope) {
|
|
|
4517
4541
|
// first interactive child.
|
|
4518
4542
|
const panel = scope.getByText(TASKS_LABELS.logs.title, { exact: true }).locator('xpath=../..');
|
|
4519
4543
|
await panel.getByRole('button').first().click();
|
|
4520
|
-
|
|
4521
|
-
await test$1.expect(page.getByRole('heading', { name: TASKS_LABELS.logDetails.title })).toBeVisible({
|
|
4522
|
-
timeout: 10000,
|
|
4523
|
-
});
|
|
4544
|
+
await test$1.expect(getLogDetailsDialog(scope)).toBeVisible({ timeout: 10000 });
|
|
4524
4545
|
}
|
|
4525
4546
|
async function expectLogDetailsStatus(scope, status) {
|
|
4526
|
-
|
|
4527
|
-
await test$1.expect(
|
|
4547
|
+
// Scope to the dialog so the status text isn't matched on a task row.
|
|
4548
|
+
await test$1.expect(getLogDetailsDialog(scope).getByText(status, { exact: true })).toBeVisible({
|
|
4549
|
+
timeout: 5000,
|
|
4550
|
+
});
|
|
4528
4551
|
}
|
|
4529
4552
|
|
|
4530
4553
|
/** Extract browser key from device name (e.g., 'Desktop Chrome' -> 'chrome') */
|