@iblai/iblai-js 1.11.0 → 1.11.3
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/index.d.ts +2 -0
- package/dist/data-layer/playwright/tasks-tab-helpers.d.ts +163 -0
- package/dist/playwright/index.cjs +322 -2
- package/dist/playwright/index.cjs.map +1 -1
- package/dist/playwright/index.d.ts +165 -2
- package/dist/playwright/index.esm.js +300 -3
- package/dist/playwright/index.esm.js.map +1 -1
- package/dist/playwright/playwright/index.d.ts +2 -0
- package/dist/playwright/playwright/tasks-tab-helpers.d.ts +163 -0
- package/dist/web-containers/playwright/index.d.ts +2 -0
- package/dist/web-containers/playwright/tasks-tab-helpers.d.ts +163 -0
- package/dist/web-containers/source/next/index.esm.js +693 -18
- package/dist/web-utils/playwright/index.d.ts +2 -0
- package/dist/web-utils/playwright/tasks-tab-helpers.d.ts +163 -0
- package/package.json +4 -4
|
@@ -25,5 +25,7 @@ export { PRIVACY_LABELS, isPrivacyTabVisible, switchToPrivacyTab, getPrivacyRout
|
|
|
25
25
|
export type { PrivacyAction, PrivacyEntity } from './privacy-tab-helpers';
|
|
26
26
|
export { billingPlanSection, billingCreditsSection, billingAutoRechargeSection, getBillingPlanLabel, getBillingAutoRechargeStatus, waitForBillingTabReady, expectBillingPlanSection, expectBillingCreditsSection, expectBillingAutoRechargeSection, expectBillingTabForFreePlan, expectBillingTabForTrialPlan, expectBillingTabForPremiumPlan, expectBillingTabForCurrentPlan, clickBillingUpgrade, clickBillingAddCredits, clickBillingManageBilling, clickBillingManageUsage, } from './billing-tab-helpers';
|
|
27
27
|
export type { BillingAutoRechargeStatus } from './billing-tab-helpers';
|
|
28
|
+
export { TASKS_LABELS, isTasksTabVisible, switchToTasksTab, getScheduleTaskButton, getSearchInput, getTaskRow, expectTotalTasks, expectCompletedTasks, expectFailedTasks, expectTasksEmpty, expectTaskInList, expectTaskNotInList, selectTaskInList, expectTaskStatus, searchTasks, openScheduleTaskDialog, scheduleTask, expectScheduleStartTimeInPastError, deleteTask, expectLogsForTask, expectNoLogsForSelectedTask, openFirstLogDetails, expectLogDetailsStatus, } from './tasks-tab-helpers';
|
|
29
|
+
export type { TaskRepeat, TaskStatus } from './tasks-tab-helpers';
|
|
28
30
|
export { createPlaywrightConfig, generateProjectConfig, generateBrowserSetupProjects, getBrowserKey, } from './playwright-config';
|
|
29
31
|
export type { PlatformConfig, CreatePlaywrightConfigOptions } from './playwright-config';
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { Locator, Page } from '@playwright/test';
|
|
2
|
+
/**
|
|
3
|
+
* Tasks tab helpers — Playwright bindings for the `AgentTasksTab`
|
|
4
|
+
* component from `@iblai/web-containers`.
|
|
5
|
+
*
|
|
6
|
+
* All UI strings below mirror `AGENT_TASKS_TAB_LABELS` from
|
|
7
|
+
* `@iblai/web-containers/next`. If a consumer renames a label via the
|
|
8
|
+
* `labels` prop, override these constants per spec — don't edit this file.
|
|
9
|
+
*
|
|
10
|
+
* The helpers assume the Edit Mentor dialog is already open. Call
|
|
11
|
+
* `switchToTasksTab(page)` first; from then on, every helper accepts
|
|
12
|
+
* either the `Page` or the dialog `Locator`.
|
|
13
|
+
*/
|
|
14
|
+
export declare const TASKS_LABELS: {
|
|
15
|
+
readonly tabName: "Tasks";
|
|
16
|
+
readonly headerTitle: "Tasks";
|
|
17
|
+
readonly headerDescription: "Configure automated tasks for your mentor.";
|
|
18
|
+
readonly toolbar: {
|
|
19
|
+
readonly searchPlaceholder: "Search all tasks...";
|
|
20
|
+
readonly selectDate: "Select date";
|
|
21
|
+
readonly scheduleTask: "Schedule Task";
|
|
22
|
+
};
|
|
23
|
+
readonly metrics: {
|
|
24
|
+
readonly total: "Total Tasks";
|
|
25
|
+
readonly completed: "Completed";
|
|
26
|
+
readonly failed: "Failed";
|
|
27
|
+
};
|
|
28
|
+
/** Status badges displayed on each task row. */
|
|
29
|
+
readonly status: {
|
|
30
|
+
readonly completed: "Completed";
|
|
31
|
+
readonly failed: "Failed";
|
|
32
|
+
readonly running: "Running";
|
|
33
|
+
readonly scheduled: "Scheduled";
|
|
34
|
+
readonly disabled: "Disabled";
|
|
35
|
+
};
|
|
36
|
+
readonly list: {
|
|
37
|
+
readonly selectHint: "Select a task to view its run logs.";
|
|
38
|
+
readonly repeatOnce: "Once";
|
|
39
|
+
readonly deleteTask: "Delete task";
|
|
40
|
+
};
|
|
41
|
+
readonly logs: {
|
|
42
|
+
readonly title: "Task Logs";
|
|
43
|
+
readonly selectPrompt: "Select a task on the left to view its run logs.";
|
|
44
|
+
readonly noLogs: "No run logs for this task yet.";
|
|
45
|
+
readonly loading: "Loading logs...";
|
|
46
|
+
readonly forTask: "Logs for";
|
|
47
|
+
};
|
|
48
|
+
readonly states: {
|
|
49
|
+
readonly loading: "Loading tasks...";
|
|
50
|
+
readonly empty: "No tasks scheduled.";
|
|
51
|
+
};
|
|
52
|
+
readonly scheduleDialog: {
|
|
53
|
+
readonly taskName: "Task Name";
|
|
54
|
+
readonly taskNamePlaceholder: "Enter task name";
|
|
55
|
+
readonly taskPrompt: "Task Prompt";
|
|
56
|
+
readonly taskPromptPlaceholder: "Enter task prompt";
|
|
57
|
+
readonly time: "Time";
|
|
58
|
+
readonly repeat: "Repeat";
|
|
59
|
+
readonly notifyByEmail: "Notify me by email";
|
|
60
|
+
readonly schedule: "Schedule Task";
|
|
61
|
+
readonly scheduling: "Scheduling...";
|
|
62
|
+
readonly cancel: "Cancel";
|
|
63
|
+
readonly dontRepeat: "Don't repeat";
|
|
64
|
+
readonly daily: "Daily";
|
|
65
|
+
readonly weekly: "Weekly";
|
|
66
|
+
readonly monthly: "Monthly";
|
|
67
|
+
readonly startTimeInPast: "Start time must be in the future.";
|
|
68
|
+
};
|
|
69
|
+
readonly deleteDialog: {
|
|
70
|
+
readonly title: "Delete Task";
|
|
71
|
+
readonly delete: "Delete Task";
|
|
72
|
+
readonly deleting: "Deleting...";
|
|
73
|
+
readonly cancel: "Cancel";
|
|
74
|
+
};
|
|
75
|
+
readonly logDetails: {
|
|
76
|
+
readonly title: "Log Details";
|
|
77
|
+
readonly status: "Status";
|
|
78
|
+
readonly createdAt: "Created";
|
|
79
|
+
readonly startTime: "Started";
|
|
80
|
+
readonly endTime: "Ended";
|
|
81
|
+
readonly output: "Output";
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
/** The four repeat cadences accepted by `scheduleTask`. */
|
|
85
|
+
export type TaskRepeat = "don't-repeat" | 'daily' | 'weekly' | 'monthly';
|
|
86
|
+
/** Possible status badges on a task row. */
|
|
87
|
+
export type TaskStatus = keyof typeof TASKS_LABELS.status;
|
|
88
|
+
/**
|
|
89
|
+
* Check whether the Tasks tab is currently rendered in the Edit Mentor
|
|
90
|
+
* dialog. Returns false instead of throwing so callers can guard
|
|
91
|
+
* conditionally rendered tabs (e.g. permission-gated).
|
|
92
|
+
*/
|
|
93
|
+
export declare function isTasksTabVisible(page: Page): Promise<boolean>;
|
|
94
|
+
/**
|
|
95
|
+
* Switch to the Tasks tab. Assumes the Edit Mentor dialog is open. After
|
|
96
|
+
* the click we wait for the unique "Schedule Task" button before
|
|
97
|
+
* returning, so callers can rely on the panel being interactive.
|
|
98
|
+
*/
|
|
99
|
+
export declare function switchToTasksTab(page: Page): Promise<void>;
|
|
100
|
+
/** Locator for the "Schedule Task" toolbar button (opens the dialog). */
|
|
101
|
+
export declare function getScheduleTaskButton(scope: Page | Locator): Locator;
|
|
102
|
+
/** Locator for the task list's search input. */
|
|
103
|
+
export declare function getSearchInput(scope: Page | Locator): Locator;
|
|
104
|
+
/**
|
|
105
|
+
* Locator for a task row by its title. We scope to the click-target
|
|
106
|
+
* `role="button"` rendered by `TaskList` so callers can `.click()` to
|
|
107
|
+
* select, or chain `.getByRole('button', { name: 'Delete task' })` to
|
|
108
|
+
* delete.
|
|
109
|
+
*/
|
|
110
|
+
export declare function getTaskRow(scope: Page | Locator, taskName: string): Locator;
|
|
111
|
+
export declare function expectTotalTasks(scope: Page | Locator, n: number): Promise<void>;
|
|
112
|
+
export declare function expectCompletedTasks(scope: Page | Locator, n: number): Promise<void>;
|
|
113
|
+
export declare function expectFailedTasks(scope: Page | Locator, n: number): Promise<void>;
|
|
114
|
+
export declare function expectTasksEmpty(scope: Page | Locator): Promise<void>;
|
|
115
|
+
export declare function expectTaskInList(scope: Page | Locator, taskName: string): Promise<void>;
|
|
116
|
+
export declare function expectTaskNotInList(scope: Page | Locator, taskName: string): Promise<void>;
|
|
117
|
+
/**
|
|
118
|
+
* Click a task row to select it. Marks the row's `aria-pressed=true` and
|
|
119
|
+
* the logs panel rebinds to that task.
|
|
120
|
+
*/
|
|
121
|
+
export declare function selectTaskInList(scope: Page | Locator, taskName: string): Promise<void>;
|
|
122
|
+
/**
|
|
123
|
+
* Assert that a given task's status badge matches the expected status.
|
|
124
|
+
*/
|
|
125
|
+
export declare function expectTaskStatus(scope: Page | Locator, taskName: string, status: TaskStatus): Promise<void>;
|
|
126
|
+
/** Type into the search input. Returns once the input value matches. */
|
|
127
|
+
export declare function searchTasks(scope: Page | Locator, query: string): Promise<void>;
|
|
128
|
+
/** Open the Schedule Task dialog and wait for it to be interactive. */
|
|
129
|
+
export declare function openScheduleTaskDialog(scope: Page | Locator): Promise<void>;
|
|
130
|
+
/**
|
|
131
|
+
* End-to-end: open the dialog, fill the form, click Schedule, and wait
|
|
132
|
+
* for it to close.
|
|
133
|
+
*
|
|
134
|
+
* `time` is an HTML `<input type="time">` string ("HH:mm", 24-hour). If
|
|
135
|
+
* the chosen date is today and the chosen time is in the past, the
|
|
136
|
+
* dialog will block submission — choose accordingly.
|
|
137
|
+
*/
|
|
138
|
+
export declare function scheduleTask(scope: Page | Locator, opts: {
|
|
139
|
+
name: string;
|
|
140
|
+
prompt?: string;
|
|
141
|
+
time: string;
|
|
142
|
+
repeat?: TaskRepeat;
|
|
143
|
+
notifyByEmail?: boolean;
|
|
144
|
+
}): Promise<void>;
|
|
145
|
+
/** Asserts the in-dialog past-time error is currently shown. */
|
|
146
|
+
export declare function expectScheduleStartTimeInPastError(scope: Page | Locator): Promise<void>;
|
|
147
|
+
/**
|
|
148
|
+
* Click the trash icon on a task row and confirm the delete in the
|
|
149
|
+
* follow-up dialog.
|
|
150
|
+
*/
|
|
151
|
+
export declare function deleteTask(scope: Page | Locator, taskName: string): Promise<void>;
|
|
152
|
+
/**
|
|
153
|
+
* Wait for the logs panel to finish loading and assert that the selected
|
|
154
|
+
* task name is shown in the header.
|
|
155
|
+
*/
|
|
156
|
+
export declare function expectLogsForTask(scope: Page | Locator, taskName: string): Promise<void>;
|
|
157
|
+
export declare function expectNoLogsForSelectedTask(scope: Page | Locator): Promise<void>;
|
|
158
|
+
/**
|
|
159
|
+
* Click the first log entry under the selected task to open the log
|
|
160
|
+
* details modal. Returns once the dialog title is visible.
|
|
161
|
+
*/
|
|
162
|
+
export declare function openFirstLogDetails(scope: Page | Locator): Promise<void>;
|
|
163
|
+
export declare function expectLogDetailsStatus(scope: Page | Locator, status: 'success' | 'error' | 'running' | string): Promise<void>;
|
|
@@ -3152,7 +3152,7 @@ const PRIVACY_LABELS = {
|
|
|
3152
3152
|
* reach DOM that Radix renders outside the dialog subtree (popovers,
|
|
3153
3153
|
* select options, etc.).
|
|
3154
3154
|
*/
|
|
3155
|
-
function asPage(scope) {
|
|
3155
|
+
function asPage$1(scope) {
|
|
3156
3156
|
return 'page' in scope ? scope.page() : scope;
|
|
3157
3157
|
}
|
|
3158
3158
|
/**
|
|
@@ -3236,7 +3236,7 @@ async function selectPrivacyAction(scope, action) {
|
|
|
3236
3236
|
await trigger.click();
|
|
3237
3237
|
// Radix Select renders options in a portal at the document root, so we
|
|
3238
3238
|
// always look them up on the Page — never on the dialog Locator.
|
|
3239
|
-
const option = asPage(scope).getByRole('option', {
|
|
3239
|
+
const option = asPage$1(scope).getByRole('option', {
|
|
3240
3240
|
name: PRIVACY_LABELS.actionOptions[action],
|
|
3241
3241
|
});
|
|
3242
3242
|
await test$1.expect(option).toBeVisible({ timeout: 5000 });
|
|
@@ -3500,6 +3500,303 @@ async function clickBillingManageUsage(page) {
|
|
|
3500
3500
|
await btn.click();
|
|
3501
3501
|
}
|
|
3502
3502
|
|
|
3503
|
+
/**
|
|
3504
|
+
* Tasks tab helpers — Playwright bindings for the `AgentTasksTab`
|
|
3505
|
+
* component from `@iblai/web-containers`.
|
|
3506
|
+
*
|
|
3507
|
+
* All UI strings below mirror `AGENT_TASKS_TAB_LABELS` from
|
|
3508
|
+
* `@iblai/web-containers/next`. If a consumer renames a label via the
|
|
3509
|
+
* `labels` prop, override these constants per spec — don't edit this file.
|
|
3510
|
+
*
|
|
3511
|
+
* The helpers assume the Edit Mentor dialog is already open. Call
|
|
3512
|
+
* `switchToTasksTab(page)` first; from then on, every helper accepts
|
|
3513
|
+
* either the `Page` or the dialog `Locator`.
|
|
3514
|
+
*/
|
|
3515
|
+
const TASKS_LABELS = {
|
|
3516
|
+
tabName: 'Tasks',
|
|
3517
|
+
headerTitle: 'Tasks',
|
|
3518
|
+
headerDescription: 'Configure automated tasks for your mentor.',
|
|
3519
|
+
toolbar: {
|
|
3520
|
+
searchPlaceholder: 'Search all tasks...',
|
|
3521
|
+
selectDate: 'Select date',
|
|
3522
|
+
scheduleTask: 'Schedule Task',
|
|
3523
|
+
},
|
|
3524
|
+
metrics: {
|
|
3525
|
+
total: 'Total Tasks',
|
|
3526
|
+
completed: 'Completed',
|
|
3527
|
+
failed: 'Failed',
|
|
3528
|
+
},
|
|
3529
|
+
/** Status badges displayed on each task row. */
|
|
3530
|
+
status: {
|
|
3531
|
+
completed: 'Completed',
|
|
3532
|
+
failed: 'Failed',
|
|
3533
|
+
running: 'Running',
|
|
3534
|
+
scheduled: 'Scheduled',
|
|
3535
|
+
disabled: 'Disabled',
|
|
3536
|
+
},
|
|
3537
|
+
list: {
|
|
3538
|
+
selectHint: 'Select a task to view its run logs.',
|
|
3539
|
+
repeatOnce: 'Once',
|
|
3540
|
+
deleteTask: 'Delete task',
|
|
3541
|
+
},
|
|
3542
|
+
logs: {
|
|
3543
|
+
title: 'Task Logs',
|
|
3544
|
+
selectPrompt: 'Select a task on the left to view its run logs.',
|
|
3545
|
+
noLogs: 'No run logs for this task yet.',
|
|
3546
|
+
loading: 'Loading logs...',
|
|
3547
|
+
forTask: 'Logs for',
|
|
3548
|
+
},
|
|
3549
|
+
states: {
|
|
3550
|
+
loading: 'Loading tasks...',
|
|
3551
|
+
empty: 'No tasks scheduled.',
|
|
3552
|
+
},
|
|
3553
|
+
scheduleDialog: {
|
|
3554
|
+
taskName: 'Task Name',
|
|
3555
|
+
taskNamePlaceholder: 'Enter task name',
|
|
3556
|
+
taskPrompt: 'Task Prompt',
|
|
3557
|
+
taskPromptPlaceholder: 'Enter task prompt',
|
|
3558
|
+
time: 'Time',
|
|
3559
|
+
repeat: 'Repeat',
|
|
3560
|
+
notifyByEmail: 'Notify me by email',
|
|
3561
|
+
schedule: 'Schedule Task',
|
|
3562
|
+
scheduling: 'Scheduling...',
|
|
3563
|
+
cancel: 'Cancel',
|
|
3564
|
+
dontRepeat: "Don't repeat",
|
|
3565
|
+
daily: 'Daily',
|
|
3566
|
+
weekly: 'Weekly',
|
|
3567
|
+
monthly: 'Monthly',
|
|
3568
|
+
startTimeInPast: 'Start time must be in the future.',
|
|
3569
|
+
},
|
|
3570
|
+
deleteDialog: {
|
|
3571
|
+
title: 'Delete Task',
|
|
3572
|
+
delete: 'Delete Task',
|
|
3573
|
+
deleting: 'Deleting...',
|
|
3574
|
+
cancel: 'Cancel',
|
|
3575
|
+
},
|
|
3576
|
+
logDetails: {
|
|
3577
|
+
title: 'Log Details',
|
|
3578
|
+
status: 'Status',
|
|
3579
|
+
createdAt: 'Created',
|
|
3580
|
+
startTime: 'Started',
|
|
3581
|
+
endTime: 'Ended',
|
|
3582
|
+
output: 'Output',
|
|
3583
|
+
},
|
|
3584
|
+
};
|
|
3585
|
+
/**
|
|
3586
|
+
* Resolve a Page from either a Page or a Locator. Used when we need to
|
|
3587
|
+
* reach DOM that Radix renders outside the dialog subtree (popovers,
|
|
3588
|
+
* select options, the schedule / delete / log-details dialogs, etc.).
|
|
3589
|
+
*/
|
|
3590
|
+
function asPage(scope) {
|
|
3591
|
+
return 'page' in scope ? scope.page() : scope;
|
|
3592
|
+
}
|
|
3593
|
+
// ── Tab navigation ─────────────────────────────────────────────────────
|
|
3594
|
+
/**
|
|
3595
|
+
* Check whether the Tasks tab is currently rendered in the Edit Mentor
|
|
3596
|
+
* dialog. Returns false instead of throwing so callers can guard
|
|
3597
|
+
* conditionally rendered tabs (e.g. permission-gated).
|
|
3598
|
+
*/
|
|
3599
|
+
async function isTasksTabVisible(page) {
|
|
3600
|
+
const tab = page.getByRole('tab', { name: TASKS_LABELS.tabName, exact: true });
|
|
3601
|
+
try {
|
|
3602
|
+
await test$1.expect(tab).toBeVisible({ timeout: 5000 });
|
|
3603
|
+
return true;
|
|
3604
|
+
}
|
|
3605
|
+
catch (_a) {
|
|
3606
|
+
return false;
|
|
3607
|
+
}
|
|
3608
|
+
}
|
|
3609
|
+
/**
|
|
3610
|
+
* Switch to the Tasks tab. Assumes the Edit Mentor dialog is open. After
|
|
3611
|
+
* the click we wait for the unique "Schedule Task" button before
|
|
3612
|
+
* returning, so callers can rely on the panel being interactive.
|
|
3613
|
+
*/
|
|
3614
|
+
async function switchToTasksTab(page) {
|
|
3615
|
+
const tab = page.getByRole('tab', { name: TASKS_LABELS.tabName, exact: true });
|
|
3616
|
+
await test$1.expect(tab).toBeVisible({ timeout: 10000 });
|
|
3617
|
+
await tab.click();
|
|
3618
|
+
// The toolbar "Schedule Task" button is unique to the Tasks pane.
|
|
3619
|
+
await test$1.expect(page.getByRole('button', { name: TASKS_LABELS.toolbar.scheduleTask })).toBeVisible({
|
|
3620
|
+
timeout: 10000,
|
|
3621
|
+
});
|
|
3622
|
+
logger.info('Switched to Tasks tab');
|
|
3623
|
+
}
|
|
3624
|
+
// ── Top-level locators ─────────────────────────────────────────────────
|
|
3625
|
+
/** Locator for the "Schedule Task" toolbar button (opens the dialog). */
|
|
3626
|
+
function getScheduleTaskButton(scope) {
|
|
3627
|
+
return scope.getByRole('button', { name: TASKS_LABELS.toolbar.scheduleTask });
|
|
3628
|
+
}
|
|
3629
|
+
/** Locator for the task list's search input. */
|
|
3630
|
+
function getSearchInput(scope) {
|
|
3631
|
+
return scope.getByPlaceholder(TASKS_LABELS.toolbar.searchPlaceholder);
|
|
3632
|
+
}
|
|
3633
|
+
/**
|
|
3634
|
+
* Locator for a task row by its title. We scope to the click-target
|
|
3635
|
+
* `role="button"` rendered by `TaskList` so callers can `.click()` to
|
|
3636
|
+
* select, or chain `.getByRole('button', { name: 'Delete task' })` to
|
|
3637
|
+
* delete.
|
|
3638
|
+
*/
|
|
3639
|
+
function getTaskRow(scope, taskName) {
|
|
3640
|
+
return scope.getByRole('button', { name: new RegExp(taskName, 'i') }).first();
|
|
3641
|
+
}
|
|
3642
|
+
// ── Metric card assertions ─────────────────────────────────────────────
|
|
3643
|
+
async function metricValue(scope, label) {
|
|
3644
|
+
// The metric card structure is <h3>label</h3><p>N</p> inside a flex col;
|
|
3645
|
+
// the heading's parent is the wrapper around both.
|
|
3646
|
+
const heading = scope.getByText(label, { exact: true }).first();
|
|
3647
|
+
const wrapper = heading.locator('xpath=..');
|
|
3648
|
+
return (await wrapper.innerText()).replace(label, '').trim();
|
|
3649
|
+
}
|
|
3650
|
+
async function expectTotalTasks(scope, n) {
|
|
3651
|
+
await test$1.expect.poll(() => metricValue(scope, TASKS_LABELS.metrics.total)).toBe(String(n));
|
|
3652
|
+
}
|
|
3653
|
+
async function expectCompletedTasks(scope, n) {
|
|
3654
|
+
await test$1.expect.poll(() => metricValue(scope, TASKS_LABELS.metrics.completed)).toBe(String(n));
|
|
3655
|
+
}
|
|
3656
|
+
async function expectFailedTasks(scope, n) {
|
|
3657
|
+
await test$1.expect.poll(() => metricValue(scope, TASKS_LABELS.metrics.failed)).toBe(String(n));
|
|
3658
|
+
}
|
|
3659
|
+
// ── Listing / row state ────────────────────────────────────────────────
|
|
3660
|
+
async function expectTasksEmpty(scope) {
|
|
3661
|
+
await test$1.expect(scope.getByText(TASKS_LABELS.states.empty)).toBeVisible({ timeout: 10000 });
|
|
3662
|
+
}
|
|
3663
|
+
async function expectTaskInList(scope, taskName) {
|
|
3664
|
+
await test$1.expect(getTaskRow(scope, taskName)).toBeVisible({ timeout: 10000 });
|
|
3665
|
+
}
|
|
3666
|
+
async function expectTaskNotInList(scope, taskName) {
|
|
3667
|
+
await test$1.expect(getTaskRow(scope, taskName)).toBeHidden({ timeout: 10000 });
|
|
3668
|
+
}
|
|
3669
|
+
/**
|
|
3670
|
+
* Click a task row to select it. Marks the row's `aria-pressed=true` and
|
|
3671
|
+
* the logs panel rebinds to that task.
|
|
3672
|
+
*/
|
|
3673
|
+
async function selectTaskInList(scope, taskName) {
|
|
3674
|
+
const row = getTaskRow(scope, taskName);
|
|
3675
|
+
await row.click();
|
|
3676
|
+
await test$1.expect(row).toHaveAttribute('aria-pressed', 'true', { timeout: 5000 });
|
|
3677
|
+
}
|
|
3678
|
+
/**
|
|
3679
|
+
* Assert that a given task's status badge matches the expected status.
|
|
3680
|
+
*/
|
|
3681
|
+
async function expectTaskStatus(scope, taskName, status) {
|
|
3682
|
+
const row = getTaskRow(scope, taskName);
|
|
3683
|
+
await test$1.expect(row.getByText(TASKS_LABELS.status[status], { exact: true })).toBeVisible({
|
|
3684
|
+
timeout: 10000,
|
|
3685
|
+
});
|
|
3686
|
+
}
|
|
3687
|
+
/** Type into the search input. Returns once the input value matches. */
|
|
3688
|
+
async function searchTasks(scope, query) {
|
|
3689
|
+
const input = getSearchInput(scope);
|
|
3690
|
+
await input.fill(query);
|
|
3691
|
+
await test$1.expect(input).toHaveValue(query);
|
|
3692
|
+
}
|
|
3693
|
+
// ── Schedule flow ──────────────────────────────────────────────────────
|
|
3694
|
+
/** Open the Schedule Task dialog and wait for it to be interactive. */
|
|
3695
|
+
async function openScheduleTaskDialog(scope) {
|
|
3696
|
+
await getScheduleTaskButton(scope).click();
|
|
3697
|
+
const page = asPage(scope);
|
|
3698
|
+
await test$1.expect(page.getByLabel(TASKS_LABELS.scheduleDialog.taskName)).toBeVisible({
|
|
3699
|
+
timeout: 10000,
|
|
3700
|
+
});
|
|
3701
|
+
}
|
|
3702
|
+
/**
|
|
3703
|
+
* End-to-end: open the dialog, fill the form, click Schedule, and wait
|
|
3704
|
+
* for it to close.
|
|
3705
|
+
*
|
|
3706
|
+
* `time` is an HTML `<input type="time">` string ("HH:mm", 24-hour). If
|
|
3707
|
+
* the chosen date is today and the chosen time is in the past, the
|
|
3708
|
+
* dialog will block submission — choose accordingly.
|
|
3709
|
+
*/
|
|
3710
|
+
async function scheduleTask(scope, opts) {
|
|
3711
|
+
await openScheduleTaskDialog(scope);
|
|
3712
|
+
const page = asPage(scope);
|
|
3713
|
+
await page.getByLabel(TASKS_LABELS.scheduleDialog.taskName).fill(opts.name);
|
|
3714
|
+
if (opts.prompt !== undefined) {
|
|
3715
|
+
await page.getByLabel(TASKS_LABELS.scheduleDialog.taskPrompt).fill(opts.prompt);
|
|
3716
|
+
}
|
|
3717
|
+
await page.getByLabel(TASKS_LABELS.scheduleDialog.time).fill(opts.time);
|
|
3718
|
+
if (opts.repeat) {
|
|
3719
|
+
// The repeat <Select> renders as a combobox via Radix.
|
|
3720
|
+
const repeatLabel = {
|
|
3721
|
+
"don't-repeat": TASKS_LABELS.scheduleDialog.dontRepeat,
|
|
3722
|
+
daily: TASKS_LABELS.scheduleDialog.daily,
|
|
3723
|
+
weekly: TASKS_LABELS.scheduleDialog.weekly,
|
|
3724
|
+
monthly: TASKS_LABELS.scheduleDialog.monthly,
|
|
3725
|
+
}[opts.repeat];
|
|
3726
|
+
await page.getByLabel(TASKS_LABELS.scheduleDialog.repeat).click();
|
|
3727
|
+
await page.getByRole('option', { name: repeatLabel }).click();
|
|
3728
|
+
}
|
|
3729
|
+
if (opts.notifyByEmail) {
|
|
3730
|
+
await page.getByLabel(TASKS_LABELS.scheduleDialog.notifyByEmail).click();
|
|
3731
|
+
}
|
|
3732
|
+
const submit = page.getByRole('button', { name: TASKS_LABELS.scheduleDialog.schedule });
|
|
3733
|
+
await test$1.expect(submit).toBeEnabled({ timeout: 5000 });
|
|
3734
|
+
await submit.click();
|
|
3735
|
+
await test$1.expect(page.getByLabel(TASKS_LABELS.scheduleDialog.taskName)).toBeHidden({
|
|
3736
|
+
timeout: 15000,
|
|
3737
|
+
});
|
|
3738
|
+
logger.info(`Scheduled task: ${opts.name}`);
|
|
3739
|
+
}
|
|
3740
|
+
/** Asserts the in-dialog past-time error is currently shown. */
|
|
3741
|
+
async function expectScheduleStartTimeInPastError(scope) {
|
|
3742
|
+
await test$1.expect(asPage(scope).getByText(TASKS_LABELS.scheduleDialog.startTimeInPast)).toBeVisible({
|
|
3743
|
+
timeout: 5000,
|
|
3744
|
+
});
|
|
3745
|
+
}
|
|
3746
|
+
// ── Delete flow ────────────────────────────────────────────────────────
|
|
3747
|
+
/**
|
|
3748
|
+
* Click the trash icon on a task row and confirm the delete in the
|
|
3749
|
+
* follow-up dialog.
|
|
3750
|
+
*/
|
|
3751
|
+
async function deleteTask(scope, taskName) {
|
|
3752
|
+
const row = getTaskRow(scope, taskName);
|
|
3753
|
+
await row.getByRole('button', { name: TASKS_LABELS.list.deleteTask }).click();
|
|
3754
|
+
const page = asPage(scope);
|
|
3755
|
+
const confirm = page.getByRole('button', { name: TASKS_LABELS.deleteDialog.delete });
|
|
3756
|
+
await test$1.expect(confirm).toBeVisible({ timeout: 5000 });
|
|
3757
|
+
await confirm.click();
|
|
3758
|
+
// Wait for the dialog to close.
|
|
3759
|
+
await test$1.expect(page.getByRole('heading', { name: TASKS_LABELS.deleteDialog.title })).toBeHidden({
|
|
3760
|
+
timeout: 10000,
|
|
3761
|
+
});
|
|
3762
|
+
logger.info(`Deleted task: ${taskName}`);
|
|
3763
|
+
}
|
|
3764
|
+
// ── Logs panel + log details ───────────────────────────────────────────
|
|
3765
|
+
/**
|
|
3766
|
+
* Wait for the logs panel to finish loading and assert that the selected
|
|
3767
|
+
* task name is shown in the header.
|
|
3768
|
+
*/
|
|
3769
|
+
async function expectLogsForTask(scope, taskName) {
|
|
3770
|
+
await test$1.expect(scope.getByText(TASKS_LABELS.logs.forTask, { exact: false })).toBeVisible({
|
|
3771
|
+
timeout: 10000,
|
|
3772
|
+
});
|
|
3773
|
+
await test$1.expect(scope.getByText(taskName, { exact: true }).first()).toBeVisible({
|
|
3774
|
+
timeout: 10000,
|
|
3775
|
+
});
|
|
3776
|
+
}
|
|
3777
|
+
async function expectNoLogsForSelectedTask(scope) {
|
|
3778
|
+
await test$1.expect(scope.getByText(TASKS_LABELS.logs.noLogs)).toBeVisible({ timeout: 10000 });
|
|
3779
|
+
}
|
|
3780
|
+
/**
|
|
3781
|
+
* Click the first log entry under the selected task to open the log
|
|
3782
|
+
* details modal. Returns once the dialog title is visible.
|
|
3783
|
+
*/
|
|
3784
|
+
async function openFirstLogDetails(scope) {
|
|
3785
|
+
// Log rows are buttons. Filter by the chevron pattern won't be reliable
|
|
3786
|
+
// across renders, so target the panel by its header text and pick the
|
|
3787
|
+
// first interactive child.
|
|
3788
|
+
const panel = scope.getByText(TASKS_LABELS.logs.title, { exact: true }).locator('xpath=../..');
|
|
3789
|
+
await panel.getByRole('button').first().click();
|
|
3790
|
+
const page = asPage(scope);
|
|
3791
|
+
await test$1.expect(page.getByRole('heading', { name: TASKS_LABELS.logDetails.title })).toBeVisible({
|
|
3792
|
+
timeout: 10000,
|
|
3793
|
+
});
|
|
3794
|
+
}
|
|
3795
|
+
async function expectLogDetailsStatus(scope, status) {
|
|
3796
|
+
const page = asPage(scope);
|
|
3797
|
+
await test$1.expect(page.getByText(status, { exact: true })).toBeVisible({ timeout: 5000 });
|
|
3798
|
+
}
|
|
3799
|
+
|
|
3503
3800
|
/** Extract browser key from device name (e.g., 'Desktop Chrome' -> 'chrome') */
|
|
3504
3801
|
function getBrowserKey(deviceName) {
|
|
3505
3802
|
return deviceName.toLowerCase().replace(/^desktop\s+/, '');
|
|
@@ -3633,6 +3930,7 @@ exports.AuthFlowBuilder = AuthFlowBuilder;
|
|
|
3633
3930
|
exports.CustomReporter = CustomReporter;
|
|
3634
3931
|
exports.MailsacClient = MailsacClient;
|
|
3635
3932
|
exports.PRIVACY_LABELS = PRIVACY_LABELS;
|
|
3933
|
+
exports.TASKS_LABELS = TASKS_LABELS;
|
|
3636
3934
|
exports.addMemory = addMemory;
|
|
3637
3935
|
exports.archiveFirstMemory = archiveFirstMemory;
|
|
3638
3936
|
exports.archiveMemoryByContent = archiveMemoryByContent;
|
|
@@ -3670,6 +3968,7 @@ exports.deleteFirstMemory = deleteFirstMemory;
|
|
|
3670
3968
|
exports.deleteInstance = deleteInstance;
|
|
3671
3969
|
exports.deleteMemoryByContent = deleteMemoryByContent;
|
|
3672
3970
|
exports.deleteSkill = deleteSkill;
|
|
3971
|
+
exports.deleteTask = deleteTask;
|
|
3673
3972
|
exports.disableSkill = disableSkill;
|
|
3674
3973
|
exports.disconnectInstance = disconnectInstance;
|
|
3675
3974
|
exports.editAgentPrompt = editAgentPrompt;
|
|
@@ -3683,18 +3982,29 @@ exports.expectBillingTabForCurrentPlan = expectBillingTabForCurrentPlan;
|
|
|
3683
3982
|
exports.expectBillingTabForFreePlan = expectBillingTabForFreePlan;
|
|
3684
3983
|
exports.expectBillingTabForPremiumPlan = expectBillingTabForPremiumPlan;
|
|
3685
3984
|
exports.expectBillingTabForTrialPlan = expectBillingTabForTrialPlan;
|
|
3985
|
+
exports.expectCompletedTasks = expectCompletedTasks;
|
|
3686
3986
|
exports.expectCreditBalanceForCurrentPlan = expectCreditBalanceForCurrentPlan;
|
|
3687
3987
|
exports.expectCreditBalancePanelForFreePlan = expectCreditBalancePanelForFreePlan;
|
|
3688
3988
|
exports.expectCreditBalancePanelForPremiumPlan = expectCreditBalancePanelForPremiumPlan;
|
|
3689
3989
|
exports.expectCreditBalancePanelForTrialPlan = expectCreditBalancePanelForTrialPlan;
|
|
3690
3990
|
exports.expectCreditBalanceVisibilityForTenant = expectCreditBalanceVisibilityForTenant;
|
|
3691
3991
|
exports.expectEntitySelected = expectEntitySelected;
|
|
3992
|
+
exports.expectFailedTasks = expectFailedTasks;
|
|
3993
|
+
exports.expectLogDetailsStatus = expectLogDetailsStatus;
|
|
3994
|
+
exports.expectLogsForTask = expectLogsForTask;
|
|
3692
3995
|
exports.expectNoAccessibilityViolations = expectNoAccessibilityViolations;
|
|
3693
3996
|
exports.expectNoAccessibilityViolationsOnDialogs = expectNoAccessibilityViolationsOnDialogs;
|
|
3997
|
+
exports.expectNoLogsForSelectedTask = expectNoLogsForSelectedTask;
|
|
3694
3998
|
exports.expectOutputFilterEnabled = expectOutputFilterEnabled;
|
|
3695
3999
|
exports.expectPrivacyFieldsHidden = expectPrivacyFieldsHidden;
|
|
3696
4000
|
exports.expectPrivacyFieldsVisible = expectPrivacyFieldsVisible;
|
|
3697
4001
|
exports.expectPrivacyRouterEnabled = expectPrivacyRouterEnabled;
|
|
4002
|
+
exports.expectScheduleStartTimeInPastError = expectScheduleStartTimeInPastError;
|
|
4003
|
+
exports.expectTaskInList = expectTaskInList;
|
|
4004
|
+
exports.expectTaskNotInList = expectTaskNotInList;
|
|
4005
|
+
exports.expectTaskStatus = expectTaskStatus;
|
|
4006
|
+
exports.expectTasksEmpty = expectTasksEmpty;
|
|
4007
|
+
exports.expectTotalTasks = expectTotalTasks;
|
|
3698
4008
|
exports.filterByAction = filterByAction;
|
|
3699
4009
|
exports.filterByActionAndVerify = filterByActionAndVerify;
|
|
3700
4010
|
exports.filterByActor = filterByActor;
|
|
@@ -3720,7 +4030,10 @@ exports.getMentorIdFromUrl = getMentorIdFromUrl;
|
|
|
3720
4030
|
exports.getOutputFilterSwitch = getOutputFilterSwitch;
|
|
3721
4031
|
exports.getPaginationInfo = getPaginationInfo;
|
|
3722
4032
|
exports.getPrivacyRouterSwitch = getPrivacyRouterSwitch;
|
|
4033
|
+
exports.getScheduleTaskButton = getScheduleTaskButton;
|
|
4034
|
+
exports.getSearchInput = getSearchInput;
|
|
3723
4035
|
exports.getSkillRowCount = getSkillRowCount;
|
|
4036
|
+
exports.getTaskRow = getTaskRow;
|
|
3724
4037
|
exports.goToFirstPage = goToFirstPage;
|
|
3725
4038
|
exports.goToLastPage = goToLastPage;
|
|
3726
4039
|
exports.goToNextPage = goToNextPage;
|
|
@@ -3735,6 +4048,7 @@ exports.isOnLastPage = isOnLastPage;
|
|
|
3735
4048
|
exports.isPrivacyTabVisible = isPrivacyTabVisible;
|
|
3736
4049
|
exports.isSandboxTabVisible = isSandboxTabVisible;
|
|
3737
4050
|
exports.isSkillEnabled = isSkillEnabled;
|
|
4051
|
+
exports.isTasksTabVisible = isTasksTabVisible;
|
|
3738
4052
|
exports.logger = logger;
|
|
3739
4053
|
exports.loginWithEmailAndPassword = loginWithEmailAndPassword;
|
|
3740
4054
|
exports.loginWithMicrosoftIdp = loginWithMicrosoftIdp;
|
|
@@ -3748,10 +4062,12 @@ exports.openAgentPromptEditModal = openAgentPromptEditModal;
|
|
|
3748
4062
|
exports.openCreditBalanceDropdown = openCreditBalanceDropdown;
|
|
3749
4063
|
exports.openEditInstanceDialog = openEditInstanceDialog;
|
|
3750
4064
|
exports.openEditSkillDialog = openEditSkillDialog;
|
|
4065
|
+
exports.openFirstLogDetails = openFirstLogDetails;
|
|
3751
4066
|
exports.openInstanceActionsMenu = openInstanceActionsMenu;
|
|
3752
4067
|
exports.openLLMProviderPicker = openLLMProviderPicker;
|
|
3753
4068
|
exports.openNewInstanceDialog = openNewInstanceDialog;
|
|
3754
4069
|
exports.openNewSkillDialog = openNewSkillDialog;
|
|
4070
|
+
exports.openScheduleTaskDialog = openScheduleTaskDialog;
|
|
3755
4071
|
exports.openSkillActionsMenu = openSkillActionsMenu;
|
|
3756
4072
|
exports.parseReportUrlParams = parseReportUrlParams;
|
|
3757
4073
|
exports.pushConfiguration = pushConfiguration;
|
|
@@ -3761,10 +4077,13 @@ exports.retry = retry;
|
|
|
3761
4077
|
exports.runConnectedInstanceChecks = runConnectedInstanceChecks;
|
|
3762
4078
|
exports.runInstanceChecks = runInstanceChecks;
|
|
3763
4079
|
exports.safeWaitForURL = safeWaitForURL;
|
|
4080
|
+
exports.scheduleTask = scheduleTask;
|
|
3764
4081
|
exports.searchInstances = searchInstances;
|
|
4082
|
+
exports.searchTasks = searchTasks;
|
|
3765
4083
|
exports.selectDateFromCalendar = selectDateFromCalendar;
|
|
3766
4084
|
exports.selectLLMModel = selectLLMModel;
|
|
3767
4085
|
exports.selectPrivacyAction = selectPrivacyAction;
|
|
4086
|
+
exports.selectTaskInList = selectTaskInList;
|
|
3768
4087
|
exports.setBlockMessage = setBlockMessage;
|
|
3769
4088
|
exports.setEntitySelected = setEntitySelected;
|
|
3770
4089
|
exports.setOutputFilterEnabled = setOutputFilterEnabled;
|
|
@@ -3791,6 +4110,7 @@ exports.switchToMemoryTab = switchToMemoryTab;
|
|
|
3791
4110
|
exports.switchToPrivacyTab = switchToPrivacyTab;
|
|
3792
4111
|
exports.switchToSandboxTab = switchToSandboxTab;
|
|
3793
4112
|
exports.switchToSkillsTab = switchToSkillsTab;
|
|
4113
|
+
exports.switchToTasksTab = switchToTasksTab;
|
|
3794
4114
|
exports.teardownSandboxInstance = teardownSandboxInstance;
|
|
3795
4115
|
exports.test = test;
|
|
3796
4116
|
exports.toggleAutoPush = toggleAutoPush;
|