@iblai/iblai-js 1.12.3 → 1.13.2
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 +4 -0
- package/dist/data-layer/playwright/screenshare-tab-helpers.d.ts +57 -0
- package/dist/data-layer/playwright/voice-tab-helpers.d.ts +194 -0
- package/dist/playwright/index.cjs +523 -2
- package/dist/playwright/index.cjs.map +1 -1
- package/dist/playwright/index.d.ts +253 -2
- package/dist/playwright/index.esm.js +485 -3
- package/dist/playwright/index.esm.js.map +1 -1
- package/dist/playwright/playwright/index.d.ts +4 -0
- package/dist/playwright/playwright/screenshare-tab-helpers.d.ts +57 -0
- package/dist/playwright/playwright/voice-tab-helpers.d.ts +194 -0
- package/dist/web-containers/playwright/index.d.ts +4 -0
- package/dist/web-containers/playwright/screenshare-tab-helpers.d.ts +57 -0
- package/dist/web-containers/playwright/voice-tab-helpers.d.ts +194 -0
- package/dist/web-containers/source/next/index.esm.js +908 -21
- package/dist/web-utils/playwright/index.d.ts +4 -0
- package/dist/web-utils/playwright/screenshare-tab-helpers.d.ts +57 -0
- package/dist/web-utils/playwright/voice-tab-helpers.d.ts +194 -0
- package/package.json +5 -5
|
@@ -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$
|
|
3155
|
+
function asPage$3(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$
|
|
3239
|
+
const option = asPage$3(scope).getByRole('option', {
|
|
3240
3240
|
name: PRIVACY_LABELS.actionOptions[action],
|
|
3241
3241
|
});
|
|
3242
3242
|
await test$1.expect(option).toBeVisible({ timeout: 5000 });
|
|
@@ -3305,6 +3305,488 @@ async function expectOutputFilterEnabled(scope, enabled) {
|
|
|
3305
3305
|
await test$1.expect(getOutputFilterSwitch(scope)).toHaveAttribute('aria-checked', String(enabled));
|
|
3306
3306
|
}
|
|
3307
3307
|
|
|
3308
|
+
/**
|
|
3309
|
+
* Voice tab helpers — Playwright bindings for the `AgentVoiceTab` component
|
|
3310
|
+
* from `@iblai/web-containers`.
|
|
3311
|
+
*
|
|
3312
|
+
* The Voice tab contains two sub-tabs:
|
|
3313
|
+
* 1. "Voice" — provider + voice-picker, persisted via mentor settings
|
|
3314
|
+
* 2. "Call Configuration" — TTS / STT / LLM / video stack, persisted via the
|
|
3315
|
+
* `/call-configurations/` endpoint
|
|
3316
|
+
*
|
|
3317
|
+
* All UI strings mirror `AGENT_VOICE_TAB_LABELS` from
|
|
3318
|
+
* `@iblai/web-containers/next`. If a consumer renames a label via the
|
|
3319
|
+
* `labels` prop, override these constants per spec — don't edit this file.
|
|
3320
|
+
*
|
|
3321
|
+
* Selectors below use stable hooks only: `data-testid`, role + name, and
|
|
3322
|
+
* label-based queries. No CSS class selectors anywhere — the underlying
|
|
3323
|
+
* Tailwind classes are not part of the public contract.
|
|
3324
|
+
*
|
|
3325
|
+
* The helpers assume the Edit Mentor dialog is already open. Call
|
|
3326
|
+
* `switchToVoiceTab(page)` first; from then on every helper accepts either
|
|
3327
|
+
* the `Page` or the dialog `Locator`.
|
|
3328
|
+
*/
|
|
3329
|
+
const VOICE_LABELS = {
|
|
3330
|
+
tabName: 'Voice',
|
|
3331
|
+
headerTitle: 'Voice',
|
|
3332
|
+
subTabs: {
|
|
3333
|
+
voice: 'Voice',
|
|
3334
|
+
callConfig: 'Voice call',
|
|
3335
|
+
},
|
|
3336
|
+
providers: {
|
|
3337
|
+
browser: 'Browser',
|
|
3338
|
+
openai: 'OpenAI',
|
|
3339
|
+
google: 'Google',
|
|
3340
|
+
},
|
|
3341
|
+
voicePickerLabel: {
|
|
3342
|
+
openai: 'OpenAI Voice',
|
|
3343
|
+
google: 'Google Voice',
|
|
3344
|
+
},
|
|
3345
|
+
saveVoiceButton: /save voice settings|saving…/i,
|
|
3346
|
+
// The "Save" button reads "Save" when no config exists yet and
|
|
3347
|
+
// "Save changes" once one does. Helpers match both via a regex.
|
|
3348
|
+
callConfigSaveCreate: 'Save',
|
|
3349
|
+
callConfigSaveUpdate: 'Save changes',
|
|
3350
|
+
callConfigResetButton: 'Reset',
|
|
3351
|
+
callConfigFields: {
|
|
3352
|
+
mode: 'Call style',
|
|
3353
|
+
language: 'Spoken language',
|
|
3354
|
+
llmProvider: 'AI provider',
|
|
3355
|
+
ttsProvider: 'Text-to-speech provider',
|
|
3356
|
+
sttProvider: 'Speech-to-text provider',
|
|
3357
|
+
useFunctionCalling: 'Look things up only when needed',
|
|
3358
|
+
enableVideo: 'Allow screen sharing on a call',
|
|
3359
|
+
},
|
|
3360
|
+
modeOptions: {
|
|
3361
|
+
realtime: 'Live conversation',
|
|
3362
|
+
inference: 'Step-by-step',
|
|
3363
|
+
},
|
|
3364
|
+
ttsOptions: {
|
|
3365
|
+
openai: 'OpenAI',
|
|
3366
|
+
google: 'Google',
|
|
3367
|
+
elevenlabs: 'ElevenLabs',
|
|
3368
|
+
},
|
|
3369
|
+
llmOptions: {
|
|
3370
|
+
openai: 'OpenAI',
|
|
3371
|
+
google: 'Google',
|
|
3372
|
+
anthropic: 'Anthropic',
|
|
3373
|
+
},
|
|
3374
|
+
};
|
|
3375
|
+
/**
|
|
3376
|
+
* Resolve a Page from either a Page or a Locator. Used when we need to
|
|
3377
|
+
* reach DOM that Radix renders outside the dialog subtree (popovers,
|
|
3378
|
+
* select options, etc.).
|
|
3379
|
+
*/
|
|
3380
|
+
function asPage$2(scope) {
|
|
3381
|
+
return 'page' in scope ? scope.page() : scope;
|
|
3382
|
+
}
|
|
3383
|
+
// ─── Tab navigation ──────────────────────────────────────────────────────
|
|
3384
|
+
/**
|
|
3385
|
+
* Check whether the Voice tab is currently rendered in the Edit Mentor
|
|
3386
|
+
* dialog. Returns false instead of throwing so consumers can guard
|
|
3387
|
+
* conditionally rendered tabs.
|
|
3388
|
+
*/
|
|
3389
|
+
async function isVoiceTabVisible(page) {
|
|
3390
|
+
const tab = page.getByRole('tab', { name: VOICE_LABELS.tabName, exact: true });
|
|
3391
|
+
try {
|
|
3392
|
+
await test$1.expect(tab).toBeVisible({ timeout: 5000 });
|
|
3393
|
+
return true;
|
|
3394
|
+
}
|
|
3395
|
+
catch (_a) {
|
|
3396
|
+
return false;
|
|
3397
|
+
}
|
|
3398
|
+
}
|
|
3399
|
+
/**
|
|
3400
|
+
* Switch to the Voice tab. Assumes the Edit Mentor dialog is open.
|
|
3401
|
+
*/
|
|
3402
|
+
async function switchToVoiceTab(page) {
|
|
3403
|
+
const tab = page.getByRole('tab', { name: VOICE_LABELS.tabName, exact: true });
|
|
3404
|
+
await test$1.expect(tab).toBeVisible({ timeout: 10000 });
|
|
3405
|
+
await tab.click();
|
|
3406
|
+
// The segmented sub-tab control is unique to the Voice tab; its presence
|
|
3407
|
+
// confirms we landed on the right pane.
|
|
3408
|
+
await test$1.expect(page.getByTestId('voice-sub-tabs')).toBeVisible({ timeout: 10000 });
|
|
3409
|
+
logger.info('Switched to Voice tab');
|
|
3410
|
+
}
|
|
3411
|
+
/**
|
|
3412
|
+
* Switch between the Voice / Voice call sub-tabs.
|
|
3413
|
+
*
|
|
3414
|
+
* Note: "Screen share" used to live here as a third sub-tab but is now
|
|
3415
|
+
* a peer top-level tab in the edit-mentor modal. Use the helpers in
|
|
3416
|
+
* `screenshare-tab-helpers.ts` for it.
|
|
3417
|
+
*/
|
|
3418
|
+
async function switchToVoiceSubTab(scope, sub) {
|
|
3419
|
+
const testId = sub === 'voice' ? 'voice-sub-tab-voice' : 'voice-sub-tab-call-config';
|
|
3420
|
+
const pill = scope.getByTestId(testId);
|
|
3421
|
+
await test$1.expect(pill).toBeVisible({ timeout: 10000 });
|
|
3422
|
+
await pill.click();
|
|
3423
|
+
await test$1.expect(pill).toHaveAttribute('aria-selected', 'true', { timeout: 10000 });
|
|
3424
|
+
logger.info(`Switched to "${VOICE_LABELS.subTabs[sub]}" sub-tab`);
|
|
3425
|
+
}
|
|
3426
|
+
// ─── Mentor voice section ────────────────────────────────────────────────
|
|
3427
|
+
/**
|
|
3428
|
+
* Locator for one of the three provider cards on the "Voice" sub-tab.
|
|
3429
|
+
*/
|
|
3430
|
+
function getVoiceProviderCard(scope, provider) {
|
|
3431
|
+
return scope.getByTestId(`voice-provider-${provider}`);
|
|
3432
|
+
}
|
|
3433
|
+
/**
|
|
3434
|
+
* Click a provider card. The picker below changes shape based on the
|
|
3435
|
+
* selection (Browser → no picker; OpenAI / Google → searchable voice list).
|
|
3436
|
+
*/
|
|
3437
|
+
async function selectVoiceProvider(scope, provider) {
|
|
3438
|
+
const card = getVoiceProviderCard(scope, provider);
|
|
3439
|
+
await test$1.expect(card).toBeVisible({ timeout: 10000 });
|
|
3440
|
+
await card.click();
|
|
3441
|
+
await test$1.expect(card).toHaveAttribute('aria-checked', 'true', { timeout: 10000 });
|
|
3442
|
+
logger.info(`Voice provider set to "${provider}"`);
|
|
3443
|
+
}
|
|
3444
|
+
async function expectVoiceProviderSelected(scope, provider) {
|
|
3445
|
+
await test$1.expect(getVoiceProviderCard(scope, provider)).toHaveAttribute('aria-checked', 'true');
|
|
3446
|
+
}
|
|
3447
|
+
/**
|
|
3448
|
+
* Open the modal voice picker for the active mentor-voice provider
|
|
3449
|
+
* (OpenAI or Google). The trigger renders the currently-picked voice on
|
|
3450
|
+
* the Voice sub-tab; clicking its "open" button opens a Dialog with
|
|
3451
|
+
* the searchable + paginated picker inside.
|
|
3452
|
+
*
|
|
3453
|
+
* The trigger root container exposes `data-testid="mentor-voice-trigger"`,
|
|
3454
|
+
* and the open button (inside it) is `mentor-voice-trigger-open`. The
|
|
3455
|
+
* inline play button is `mentor-voice-trigger-preview` — see
|
|
3456
|
+
* `previewMentorVoiceInline`.
|
|
3457
|
+
*/
|
|
3458
|
+
async function openMentorVoicePicker(scope) {
|
|
3459
|
+
const openBtn = scope.getByTestId('mentor-voice-trigger-open');
|
|
3460
|
+
await test$1.expect(openBtn).toBeVisible({ timeout: 10000 });
|
|
3461
|
+
await openBtn.click();
|
|
3462
|
+
await test$1.expect(asPage$2(scope).getByTestId('voice-picker-modal')).toBeVisible({
|
|
3463
|
+
timeout: 10000,
|
|
3464
|
+
});
|
|
3465
|
+
}
|
|
3466
|
+
/**
|
|
3467
|
+
* Open the modal voice picker inside the Call Configuration sub-tab.
|
|
3468
|
+
* Only present when an LLM provider has been chosen.
|
|
3469
|
+
*/
|
|
3470
|
+
async function openCallConfigVoicePicker(scope) {
|
|
3471
|
+
const openBtn = scope.getByTestId('call-config-voice-trigger-open');
|
|
3472
|
+
await test$1.expect(openBtn).toBeVisible({ timeout: 10000 });
|
|
3473
|
+
await openBtn.click();
|
|
3474
|
+
await test$1.expect(asPage$2(scope).getByTestId('voice-picker-modal')).toBeVisible({
|
|
3475
|
+
timeout: 10000,
|
|
3476
|
+
});
|
|
3477
|
+
}
|
|
3478
|
+
/**
|
|
3479
|
+
* Click the inline Play button on the mentor-voice trigger to preview
|
|
3480
|
+
* the currently-selected voice without opening the modal. Audio
|
|
3481
|
+
* playback isn't deterministically observable from Playwright, so this
|
|
3482
|
+
* just clicks and returns.
|
|
3483
|
+
*/
|
|
3484
|
+
async function previewMentorVoiceInline(scope) {
|
|
3485
|
+
const previewBtn = scope.getByTestId('mentor-voice-trigger-preview');
|
|
3486
|
+
await test$1.expect(previewBtn).toBeVisible({ timeout: 10000 });
|
|
3487
|
+
await previewBtn.click();
|
|
3488
|
+
logger.info('Previewed mentor voice (inline)');
|
|
3489
|
+
}
|
|
3490
|
+
/**
|
|
3491
|
+
* Same as `previewMentorVoiceInline`, but for the Call Configuration
|
|
3492
|
+
* voice trigger.
|
|
3493
|
+
*/
|
|
3494
|
+
async function previewCallConfigVoiceInline(scope) {
|
|
3495
|
+
const previewBtn = scope.getByTestId('call-config-voice-trigger-preview');
|
|
3496
|
+
await test$1.expect(previewBtn).toBeVisible({ timeout: 10000 });
|
|
3497
|
+
await previewBtn.click();
|
|
3498
|
+
logger.info('Previewed call-config voice (inline)');
|
|
3499
|
+
}
|
|
3500
|
+
/**
|
|
3501
|
+
* Assert that the trigger shows the given voice name (title-cased).
|
|
3502
|
+
* Useful for verifying that a previously-saved voice loads correctly.
|
|
3503
|
+
*/
|
|
3504
|
+
async function expectMentorVoiceTriggerShows(scope, voiceName) {
|
|
3505
|
+
await test$1.expect(scope.getByTestId('mentor-voice-trigger')).toContainText(voiceName);
|
|
3506
|
+
}
|
|
3507
|
+
async function expectCallConfigVoiceTriggerShows(scope, voiceName) {
|
|
3508
|
+
await test$1.expect(scope.getByTestId('call-config-voice-trigger')).toContainText(voiceName);
|
|
3509
|
+
}
|
|
3510
|
+
/**
|
|
3511
|
+
* Search the voice picker. Typing is debounced ~300ms inside the
|
|
3512
|
+
* component, so callers usually want to assert on the eventual results
|
|
3513
|
+
* (see `expectVoiceVisible`). When the picker lives inside the modal,
|
|
3514
|
+
* scope to the dialog: `searchVoices(page.getByTestId('voice-picker-modal'), 'al')`.
|
|
3515
|
+
*
|
|
3516
|
+
* Targets the input via `data-testid="voice-picker-search"` — a stable
|
|
3517
|
+
* hook that doesn't depend on the (overridable) placeholder copy and
|
|
3518
|
+
* avoids ReDoS-shaped placeholder regexes.
|
|
3519
|
+
*/
|
|
3520
|
+
async function searchVoices(scope, query) {
|
|
3521
|
+
const input = scope.getByTestId('voice-picker-search');
|
|
3522
|
+
await test$1.expect(input).toBeVisible({ timeout: 10000 });
|
|
3523
|
+
await input.fill(query);
|
|
3524
|
+
}
|
|
3525
|
+
/**
|
|
3526
|
+
* Locator for an individual voice row in the picker. Rows are
|
|
3527
|
+
* `role="radio"` with `aria-label` matching the voice name.
|
|
3528
|
+
*/
|
|
3529
|
+
function getVoiceRow(scope, voiceName) {
|
|
3530
|
+
return scope.getByRole('radio', { name: voiceName, exact: true });
|
|
3531
|
+
}
|
|
3532
|
+
async function expectVoiceVisible(scope, voiceName) {
|
|
3533
|
+
await test$1.expect(getVoiceRow(scope, voiceName)).toBeVisible({ timeout: 10000 });
|
|
3534
|
+
}
|
|
3535
|
+
/**
|
|
3536
|
+
* Click a voice row to select it. Returns once the row reports
|
|
3537
|
+
* aria-checked=true.
|
|
3538
|
+
*/
|
|
3539
|
+
async function selectVoice(scope, voiceName) {
|
|
3540
|
+
const row = getVoiceRow(scope, voiceName);
|
|
3541
|
+
await test$1.expect(row).toBeVisible({ timeout: 10000 });
|
|
3542
|
+
await row.click();
|
|
3543
|
+
await test$1.expect(row).toHaveAttribute('aria-checked', 'true', { timeout: 10000 });
|
|
3544
|
+
logger.info(`Selected voice "${voiceName}"`);
|
|
3545
|
+
}
|
|
3546
|
+
/**
|
|
3547
|
+
* Click the inline preview button next to a voice. Returns immediately —
|
|
3548
|
+
* audio playback is async and not deterministically observable from
|
|
3549
|
+
* Playwright.
|
|
3550
|
+
*/
|
|
3551
|
+
async function previewVoice(scope, voiceName) {
|
|
3552
|
+
const previewBtn = scope.getByRole('button', { name: `Preview ${voiceName} voice` });
|
|
3553
|
+
await test$1.expect(previewBtn).toBeVisible({ timeout: 10000 });
|
|
3554
|
+
await previewBtn.click();
|
|
3555
|
+
}
|
|
3556
|
+
/**
|
|
3557
|
+
* Click the Save button on the Voice sub-tab. Asserts the button is
|
|
3558
|
+
* enabled first (a no-op on a pristine form would be a test bug).
|
|
3559
|
+
*/
|
|
3560
|
+
async function saveVoiceSettings(scope) {
|
|
3561
|
+
const btn = scope.getByTestId('voice-save-button');
|
|
3562
|
+
await test$1.expect(btn).toBeVisible({ timeout: 10000 });
|
|
3563
|
+
await test$1.expect(btn).toBeEnabled({ timeout: 10000 });
|
|
3564
|
+
await btn.click();
|
|
3565
|
+
logger.info('Clicked Save voice settings');
|
|
3566
|
+
}
|
|
3567
|
+
// ─── Call configuration section ──────────────────────────────────────────
|
|
3568
|
+
/**
|
|
3569
|
+
* Locator for the call configuration form, useful as a scope root for
|
|
3570
|
+
* subsequent helpers.
|
|
3571
|
+
*/
|
|
3572
|
+
function getCallConfigForm(scope) {
|
|
3573
|
+
return scope.getByTestId('call-config-form');
|
|
3574
|
+
}
|
|
3575
|
+
async function expectCallConfigVisible(scope) {
|
|
3576
|
+
await test$1.expect(getCallConfigForm(scope)).toBeVisible({ timeout: 10000 });
|
|
3577
|
+
}
|
|
3578
|
+
/**
|
|
3579
|
+
* Pick a call mode (realtime/inference). The TTS/STT selects below become
|
|
3580
|
+
* disabled in realtime mode — assert via `expectTtsSelectDisabled` if your
|
|
3581
|
+
* test cares.
|
|
3582
|
+
*/
|
|
3583
|
+
async function selectCallMode(scope, mode) {
|
|
3584
|
+
const trigger = scope.getByRole('combobox', { name: VOICE_LABELS.callConfigFields.mode });
|
|
3585
|
+
await test$1.expect(trigger).toBeVisible({ timeout: 10000 });
|
|
3586
|
+
await trigger.click();
|
|
3587
|
+
const option = asPage$2(scope).getByRole('option', { name: VOICE_LABELS.modeOptions[mode] });
|
|
3588
|
+
await test$1.expect(option).toBeVisible({ timeout: 5000 });
|
|
3589
|
+
await option.click();
|
|
3590
|
+
await test$1.expect(trigger).toHaveText(new RegExp(VOICE_LABELS.modeOptions[mode]));
|
|
3591
|
+
logger.info(`Call mode set to "${mode}"`);
|
|
3592
|
+
}
|
|
3593
|
+
async function setCallLanguage(scope, code) {
|
|
3594
|
+
const input = scope.getByLabel(VOICE_LABELS.callConfigFields.language);
|
|
3595
|
+
await test$1.expect(input).toBeVisible({ timeout: 10000 });
|
|
3596
|
+
await input.fill(code);
|
|
3597
|
+
await test$1.expect(input).toHaveValue(code);
|
|
3598
|
+
}
|
|
3599
|
+
async function selectFromCombobox(scope, triggerName, optionName) {
|
|
3600
|
+
const trigger = scope.getByRole('combobox', { name: triggerName });
|
|
3601
|
+
await test$1.expect(trigger).toBeVisible({ timeout: 10000 });
|
|
3602
|
+
await trigger.click();
|
|
3603
|
+
const option = asPage$2(scope).getByRole('option', { name: optionName });
|
|
3604
|
+
await test$1.expect(option).toBeVisible({ timeout: 5000 });
|
|
3605
|
+
await option.click();
|
|
3606
|
+
}
|
|
3607
|
+
async function selectLlmProvider(scope, provider) {
|
|
3608
|
+
await selectFromCombobox(scope, VOICE_LABELS.callConfigFields.llmProvider, VOICE_LABELS.llmOptions[provider]);
|
|
3609
|
+
logger.info(`Call LLM provider set to "${provider}"`);
|
|
3610
|
+
}
|
|
3611
|
+
async function selectTtsProvider(scope, provider) {
|
|
3612
|
+
await selectFromCombobox(scope, VOICE_LABELS.callConfigFields.ttsProvider, VOICE_LABELS.ttsOptions[provider]);
|
|
3613
|
+
logger.info(`Call TTS provider set to "${provider}"`);
|
|
3614
|
+
}
|
|
3615
|
+
async function selectSttProvider(scope, provider) {
|
|
3616
|
+
await selectFromCombobox(scope, VOICE_LABELS.callConfigFields.sttProvider, VOICE_LABELS.ttsOptions[provider]);
|
|
3617
|
+
logger.info(`Call STT provider set to "${provider}"`);
|
|
3618
|
+
}
|
|
3619
|
+
async function expectTtsSelectDisabled(scope) {
|
|
3620
|
+
const trigger = scope.getByRole('combobox', {
|
|
3621
|
+
name: VOICE_LABELS.callConfigFields.ttsProvider,
|
|
3622
|
+
});
|
|
3623
|
+
await test$1.expect(trigger).toBeDisabled();
|
|
3624
|
+
}
|
|
3625
|
+
async function expectSttSelectDisabled(scope) {
|
|
3626
|
+
const trigger = scope.getByRole('combobox', {
|
|
3627
|
+
name: VOICE_LABELS.callConfigFields.sttProvider,
|
|
3628
|
+
});
|
|
3629
|
+
await test$1.expect(trigger).toBeDisabled();
|
|
3630
|
+
}
|
|
3631
|
+
/**
|
|
3632
|
+
* Generic toggle helper for the call-config switches (functionCalling /
|
|
3633
|
+
* enableVideo). The switch is `role="switch"`; its name has the
|
|
3634
|
+
* pattern `<label> enabled|disabled`.
|
|
3635
|
+
*/
|
|
3636
|
+
async function toggleCallSwitch(scope, label, enabled) {
|
|
3637
|
+
const escapedLabel = label.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
3638
|
+
const toggle = scope.getByRole('switch', {
|
|
3639
|
+
name: new RegExp(`^${escapedLabel} (enabled|disabled)$`),
|
|
3640
|
+
});
|
|
3641
|
+
await test$1.expect(toggle).toBeVisible({ timeout: 10000 });
|
|
3642
|
+
const isChecked = (await toggle.getAttribute('aria-checked')) === 'true';
|
|
3643
|
+
if (isChecked !== enabled) {
|
|
3644
|
+
await toggle.click();
|
|
3645
|
+
await test$1.expect(toggle).toHaveAttribute('aria-checked', String(enabled), {
|
|
3646
|
+
timeout: 10000,
|
|
3647
|
+
});
|
|
3648
|
+
}
|
|
3649
|
+
}
|
|
3650
|
+
async function setUseFunctionCallingEnabled(scope, enabled) {
|
|
3651
|
+
await toggleCallSwitch(scope, VOICE_LABELS.callConfigFields.useFunctionCalling, enabled);
|
|
3652
|
+
logger.info(`Function calling for RAG set to ${enabled ? 'enabled' : 'disabled'}`);
|
|
3653
|
+
}
|
|
3654
|
+
async function setEnableVideo(scope, enabled) {
|
|
3655
|
+
await toggleCallSwitch(scope, VOICE_LABELS.callConfigFields.enableVideo, enabled);
|
|
3656
|
+
logger.info(`Call video set to ${enabled ? 'enabled' : 'disabled'}`);
|
|
3657
|
+
}
|
|
3658
|
+
// Note: screensharing prompts moved to a standalone top-level tab —
|
|
3659
|
+
// see `screenshare-tab-helpers.ts` for editor / save helpers.
|
|
3660
|
+
/**
|
|
3661
|
+
* Click the call-config Save button. The label switches between
|
|
3662
|
+
* "Create configuration" and "Save changes" depending on whether a config
|
|
3663
|
+
* exists; we match either.
|
|
3664
|
+
*/
|
|
3665
|
+
async function saveCallConfig(scope) {
|
|
3666
|
+
const btn = scope.getByRole('button', {
|
|
3667
|
+
name: new RegExp(`${VOICE_LABELS.callConfigSaveCreate}|${VOICE_LABELS.callConfigSaveUpdate}`, 'i'),
|
|
3668
|
+
});
|
|
3669
|
+
await test$1.expect(btn).toBeVisible({ timeout: 10000 });
|
|
3670
|
+
await test$1.expect(btn).toBeEnabled({ timeout: 10000 });
|
|
3671
|
+
await btn.click();
|
|
3672
|
+
logger.info('Clicked Save call configuration');
|
|
3673
|
+
}
|
|
3674
|
+
async function resetCallConfig(scope) {
|
|
3675
|
+
const btn = scope.getByRole('button', { name: VOICE_LABELS.callConfigResetButton });
|
|
3676
|
+
await test$1.expect(btn).toBeVisible({ timeout: 10000 });
|
|
3677
|
+
await btn.click();
|
|
3678
|
+
}
|
|
3679
|
+
|
|
3680
|
+
/**
|
|
3681
|
+
* Screen share tab helpers — Playwright bindings for the standalone
|
|
3682
|
+
* `AgentScreenShareTab` component from `@iblai/web-containers`.
|
|
3683
|
+
*
|
|
3684
|
+
* The tab is a peer to Voice/LLM/Tools/etc in the edit-mentor modal.
|
|
3685
|
+
* It only edits the two screensharing prompts on the mentor's
|
|
3686
|
+
* CallConfiguration; the on/off toggle lives on the Settings tab.
|
|
3687
|
+
*
|
|
3688
|
+
* Selectors below use stable hooks only: `data-testid`, role + name,
|
|
3689
|
+
* and label-based queries. No CSS class selectors.
|
|
3690
|
+
*/
|
|
3691
|
+
const SCREENSHARE_LABELS = {
|
|
3692
|
+
tabName: 'Screen Share',
|
|
3693
|
+
headerTitle: 'Screen Share',
|
|
3694
|
+
fields: {
|
|
3695
|
+
systemPrompt: 'Instructions during screen sharing',
|
|
3696
|
+
proactivePrompt: 'What the agent says when screen sharing starts',
|
|
3697
|
+
},
|
|
3698
|
+
saveButton: 'Save',
|
|
3699
|
+
};
|
|
3700
|
+
function asPage$1(scope) {
|
|
3701
|
+
return 'page' in scope ? scope.page() : scope;
|
|
3702
|
+
}
|
|
3703
|
+
/**
|
|
3704
|
+
* Returns false if the Screen share tab isn't currently rendered in
|
|
3705
|
+
* the Edit Mentor dialog (e.g. the host hasn't registered it, or it's
|
|
3706
|
+
* hidden by RBAC).
|
|
3707
|
+
*/
|
|
3708
|
+
async function isScreenShareTabVisible(page) {
|
|
3709
|
+
const tab = page.getByRole('tab', { name: SCREENSHARE_LABELS.tabName, exact: true });
|
|
3710
|
+
try {
|
|
3711
|
+
await test$1.expect(tab).toBeVisible({ timeout: 5000 });
|
|
3712
|
+
return true;
|
|
3713
|
+
}
|
|
3714
|
+
catch (_a) {
|
|
3715
|
+
return false;
|
|
3716
|
+
}
|
|
3717
|
+
}
|
|
3718
|
+
/**
|
|
3719
|
+
* Switch to the Screen share top-level tab. Assumes the Edit Mentor
|
|
3720
|
+
* dialog is open.
|
|
3721
|
+
*/
|
|
3722
|
+
async function switchToScreenShareTab(page) {
|
|
3723
|
+
const tab = page.getByRole('tab', { name: SCREENSHARE_LABELS.tabName, exact: true });
|
|
3724
|
+
await test$1.expect(tab).toBeVisible({ timeout: 10000 });
|
|
3725
|
+
await tab.click();
|
|
3726
|
+
// The tab body's testid confirms we landed on the right pane.
|
|
3727
|
+
await test$1.expect(page.getByTestId('screenshare-tab-body')).toBeVisible({ timeout: 10000 });
|
|
3728
|
+
logger.info('Switched to Screen share tab');
|
|
3729
|
+
}
|
|
3730
|
+
/**
|
|
3731
|
+
* Open the inline prompt editor for one of the two screensharing
|
|
3732
|
+
* prompts. Mirrors the Prompts tab pattern: each prompt is rendered as
|
|
3733
|
+
* a card with an "Edit <label>" button that pops the rich-text modal.
|
|
3734
|
+
*/
|
|
3735
|
+
async function openScreenSharePromptEditor(scope, field) {
|
|
3736
|
+
const label = SCREENSHARE_LABELS.fields[field];
|
|
3737
|
+
const btn = scope.getByRole('button', { name: `Edit ${label}` });
|
|
3738
|
+
await test$1.expect(btn).toBeVisible({ timeout: 10000 });
|
|
3739
|
+
await btn.click();
|
|
3740
|
+
await test$1.expect(asPage$1(scope).getByText(`Edit ${label}`)).toBeVisible({ timeout: 10000 });
|
|
3741
|
+
logger.info(`Opened screen-share ${field} editor`);
|
|
3742
|
+
}
|
|
3743
|
+
/**
|
|
3744
|
+
* Replace the prompt text inside the rich-text modal and confirm. The
|
|
3745
|
+
* new value is written to local form state; call `saveScreenSharePrompts`
|
|
3746
|
+
* afterwards to persist.
|
|
3747
|
+
*/
|
|
3748
|
+
async function setScreenSharePrompt(scope, field, text) {
|
|
3749
|
+
await openScreenSharePromptEditor(scope, field);
|
|
3750
|
+
const page = asPage$1(scope);
|
|
3751
|
+
const editor = page.getByRole('dialog').locator('[contenteditable="true"]').first();
|
|
3752
|
+
await test$1.expect(editor).toBeVisible({ timeout: 10000 });
|
|
3753
|
+
await editor.click();
|
|
3754
|
+
await page.keyboard.press('ControlOrMeta+A');
|
|
3755
|
+
await page.keyboard.press('Delete');
|
|
3756
|
+
await editor.pressSequentially(text);
|
|
3757
|
+
// The modal's Save button — scope to dialog so we don't grab the
|
|
3758
|
+
// outer tab's Save.
|
|
3759
|
+
await page.getByRole('dialog').getByRole('button', { name: /^save$/i }).click();
|
|
3760
|
+
await test$1.expect(page.getByRole('dialog')).toBeHidden({ timeout: 10000 });
|
|
3761
|
+
logger.info(`Set screen-share ${field}`);
|
|
3762
|
+
}
|
|
3763
|
+
/**
|
|
3764
|
+
* Click the tab's Save button to persist any pending prompt edits.
|
|
3765
|
+
* Asserts the button is enabled first — a no-op call on a pristine
|
|
3766
|
+
* tab would be a test bug.
|
|
3767
|
+
*/
|
|
3768
|
+
async function saveScreenSharePrompts(scope) {
|
|
3769
|
+
const btn = scope.getByTestId('screenshare-save-button');
|
|
3770
|
+
await test$1.expect(btn).toBeVisible({ timeout: 10000 });
|
|
3771
|
+
await test$1.expect(btn).toBeEnabled({ timeout: 10000 });
|
|
3772
|
+
await btn.click();
|
|
3773
|
+
logger.info('Clicked Save on Screen share tab');
|
|
3774
|
+
}
|
|
3775
|
+
/**
|
|
3776
|
+
* Assert whether the amber off-state hint is shown. The hint renders
|
|
3777
|
+
* when `enable_video` is false on the underlying CallConfiguration
|
|
3778
|
+
* (or when no call_configuration exists yet).
|
|
3779
|
+
*/
|
|
3780
|
+
async function expectScreenShareDisabledHint(scope, visible) {
|
|
3781
|
+
const hint = scope.getByTestId('screenshare-disabled-hint');
|
|
3782
|
+
if (visible) {
|
|
3783
|
+
await test$1.expect(hint).toBeVisible({ timeout: 10000 });
|
|
3784
|
+
}
|
|
3785
|
+
else {
|
|
3786
|
+
await test$1.expect(hint).toBeHidden();
|
|
3787
|
+
}
|
|
3788
|
+
}
|
|
3789
|
+
|
|
3308
3790
|
const DEFAULT_TIMEOUT = 10000;
|
|
3309
3791
|
/** Locator for the Plan section card on the BillingTab. */
|
|
3310
3792
|
function billingPlanSection(page) {
|
|
@@ -3930,7 +4412,9 @@ exports.AuthFlowBuilder = AuthFlowBuilder;
|
|
|
3930
4412
|
exports.CustomReporter = CustomReporter;
|
|
3931
4413
|
exports.MailsacClient = MailsacClient;
|
|
3932
4414
|
exports.PRIVACY_LABELS = PRIVACY_LABELS;
|
|
4415
|
+
exports.SCREENSHARE_LABELS = SCREENSHARE_LABELS;
|
|
3933
4416
|
exports.TASKS_LABELS = TASKS_LABELS;
|
|
4417
|
+
exports.VOICE_LABELS = VOICE_LABELS;
|
|
3934
4418
|
exports.addMemory = addMemory;
|
|
3935
4419
|
exports.archiveFirstMemory = archiveFirstMemory;
|
|
3936
4420
|
exports.archiveMemoryByContent = archiveMemoryByContent;
|
|
@@ -3982,6 +4466,8 @@ exports.expectBillingTabForCurrentPlan = expectBillingTabForCurrentPlan;
|
|
|
3982
4466
|
exports.expectBillingTabForFreePlan = expectBillingTabForFreePlan;
|
|
3983
4467
|
exports.expectBillingTabForPremiumPlan = expectBillingTabForPremiumPlan;
|
|
3984
4468
|
exports.expectBillingTabForTrialPlan = expectBillingTabForTrialPlan;
|
|
4469
|
+
exports.expectCallConfigVisible = expectCallConfigVisible;
|
|
4470
|
+
exports.expectCallConfigVoiceTriggerShows = expectCallConfigVoiceTriggerShows;
|
|
3985
4471
|
exports.expectCompletedTasks = expectCompletedTasks;
|
|
3986
4472
|
exports.expectCreditBalanceForCurrentPlan = expectCreditBalanceForCurrentPlan;
|
|
3987
4473
|
exports.expectCreditBalancePanelForFreePlan = expectCreditBalancePanelForFreePlan;
|
|
@@ -3992,6 +4478,7 @@ exports.expectEntitySelected = expectEntitySelected;
|
|
|
3992
4478
|
exports.expectFailedTasks = expectFailedTasks;
|
|
3993
4479
|
exports.expectLogDetailsStatus = expectLogDetailsStatus;
|
|
3994
4480
|
exports.expectLogsForTask = expectLogsForTask;
|
|
4481
|
+
exports.expectMentorVoiceTriggerShows = expectMentorVoiceTriggerShows;
|
|
3995
4482
|
exports.expectNoAccessibilityViolations = expectNoAccessibilityViolations;
|
|
3996
4483
|
exports.expectNoAccessibilityViolationsOnDialogs = expectNoAccessibilityViolationsOnDialogs;
|
|
3997
4484
|
exports.expectNoLogsForSelectedTask = expectNoLogsForSelectedTask;
|
|
@@ -4000,11 +4487,16 @@ exports.expectPrivacyFieldsHidden = expectPrivacyFieldsHidden;
|
|
|
4000
4487
|
exports.expectPrivacyFieldsVisible = expectPrivacyFieldsVisible;
|
|
4001
4488
|
exports.expectPrivacyRouterEnabled = expectPrivacyRouterEnabled;
|
|
4002
4489
|
exports.expectScheduleStartTimeInPastError = expectScheduleStartTimeInPastError;
|
|
4490
|
+
exports.expectScreenShareDisabledHint = expectScreenShareDisabledHint;
|
|
4491
|
+
exports.expectSttSelectDisabled = expectSttSelectDisabled;
|
|
4003
4492
|
exports.expectTaskInList = expectTaskInList;
|
|
4004
4493
|
exports.expectTaskNotInList = expectTaskNotInList;
|
|
4005
4494
|
exports.expectTaskStatus = expectTaskStatus;
|
|
4006
4495
|
exports.expectTasksEmpty = expectTasksEmpty;
|
|
4007
4496
|
exports.expectTotalTasks = expectTotalTasks;
|
|
4497
|
+
exports.expectTtsSelectDisabled = expectTtsSelectDisabled;
|
|
4498
|
+
exports.expectVoiceProviderSelected = expectVoiceProviderSelected;
|
|
4499
|
+
exports.expectVoiceVisible = expectVoiceVisible;
|
|
4008
4500
|
exports.filterByAction = filterByAction;
|
|
4009
4501
|
exports.filterByActionAndVerify = filterByActionAndVerify;
|
|
4010
4502
|
exports.filterByActor = filterByActor;
|
|
@@ -4017,6 +4509,7 @@ exports.getAvailableActors = getAvailableActors;
|
|
|
4017
4509
|
exports.getBillingAutoRechargeStatus = getBillingAutoRechargeStatus;
|
|
4018
4510
|
exports.getBillingPlanLabel = getBillingPlanLabel;
|
|
4019
4511
|
exports.getBrowserKey = getBrowserKey;
|
|
4512
|
+
exports.getCallConfigForm = getCallConfigForm;
|
|
4020
4513
|
exports.getCreditBalancePlanLabel = getCreditBalancePlanLabel;
|
|
4021
4514
|
exports.getCreditBalanceRemaining = getCreditBalanceRemaining;
|
|
4022
4515
|
exports.getCurrentModel = getCurrentModel;
|
|
@@ -4034,6 +4527,8 @@ exports.getScheduleTaskButton = getScheduleTaskButton;
|
|
|
4034
4527
|
exports.getSearchInput = getSearchInput;
|
|
4035
4528
|
exports.getSkillRowCount = getSkillRowCount;
|
|
4036
4529
|
exports.getTaskRow = getTaskRow;
|
|
4530
|
+
exports.getVoiceProviderCard = getVoiceProviderCard;
|
|
4531
|
+
exports.getVoiceRow = getVoiceRow;
|
|
4037
4532
|
exports.goToFirstPage = goToFirstPage;
|
|
4038
4533
|
exports.goToLastPage = goToLastPage;
|
|
4039
4534
|
exports.goToNextPage = goToNextPage;
|
|
@@ -4047,8 +4542,10 @@ exports.isOnFirstPage = isOnFirstPage;
|
|
|
4047
4542
|
exports.isOnLastPage = isOnLastPage;
|
|
4048
4543
|
exports.isPrivacyTabVisible = isPrivacyTabVisible;
|
|
4049
4544
|
exports.isSandboxTabVisible = isSandboxTabVisible;
|
|
4545
|
+
exports.isScreenShareTabVisible = isScreenShareTabVisible;
|
|
4050
4546
|
exports.isSkillEnabled = isSkillEnabled;
|
|
4051
4547
|
exports.isTasksTabVisible = isTasksTabVisible;
|
|
4548
|
+
exports.isVoiceTabVisible = isVoiceTabVisible;
|
|
4052
4549
|
exports.logger = logger;
|
|
4053
4550
|
exports.loginWithEmailAndPassword = loginWithEmailAndPassword;
|
|
4054
4551
|
exports.loginWithMicrosoftIdp = loginWithMicrosoftIdp;
|
|
@@ -4059,35 +4556,56 @@ exports.navigateToDataReports = navigateToDataReports;
|
|
|
4059
4556
|
exports.navigateToReportDownload = navigateToReportDownload;
|
|
4060
4557
|
exports.openAddMemoryDialog = openAddMemoryDialog;
|
|
4061
4558
|
exports.openAgentPromptEditModal = openAgentPromptEditModal;
|
|
4559
|
+
exports.openCallConfigVoicePicker = openCallConfigVoicePicker;
|
|
4062
4560
|
exports.openCreditBalanceDropdown = openCreditBalanceDropdown;
|
|
4063
4561
|
exports.openEditInstanceDialog = openEditInstanceDialog;
|
|
4064
4562
|
exports.openEditSkillDialog = openEditSkillDialog;
|
|
4065
4563
|
exports.openFirstLogDetails = openFirstLogDetails;
|
|
4066
4564
|
exports.openInstanceActionsMenu = openInstanceActionsMenu;
|
|
4067
4565
|
exports.openLLMProviderPicker = openLLMProviderPicker;
|
|
4566
|
+
exports.openMentorVoicePicker = openMentorVoicePicker;
|
|
4068
4567
|
exports.openNewInstanceDialog = openNewInstanceDialog;
|
|
4069
4568
|
exports.openNewSkillDialog = openNewSkillDialog;
|
|
4070
4569
|
exports.openScheduleTaskDialog = openScheduleTaskDialog;
|
|
4570
|
+
exports.openScreenSharePromptEditor = openScreenSharePromptEditor;
|
|
4071
4571
|
exports.openSkillActionsMenu = openSkillActionsMenu;
|
|
4072
4572
|
exports.parseReportUrlParams = parseReportUrlParams;
|
|
4573
|
+
exports.previewCallConfigVoiceInline = previewCallConfigVoiceInline;
|
|
4574
|
+
exports.previewMentorVoiceInline = previewMentorVoiceInline;
|
|
4575
|
+
exports.previewVoice = previewVoice;
|
|
4073
4576
|
exports.pushConfiguration = pushConfiguration;
|
|
4074
4577
|
exports.reliableClick = reliableClick;
|
|
4075
4578
|
exports.reliableFill = reliableFill;
|
|
4579
|
+
exports.resetCallConfig = resetCallConfig;
|
|
4076
4580
|
exports.retry = retry;
|
|
4077
4581
|
exports.runConnectedInstanceChecks = runConnectedInstanceChecks;
|
|
4078
4582
|
exports.runInstanceChecks = runInstanceChecks;
|
|
4079
4583
|
exports.safeWaitForURL = safeWaitForURL;
|
|
4584
|
+
exports.saveCallConfig = saveCallConfig;
|
|
4585
|
+
exports.saveScreenSharePrompts = saveScreenSharePrompts;
|
|
4586
|
+
exports.saveVoiceSettings = saveVoiceSettings;
|
|
4080
4587
|
exports.scheduleTask = scheduleTask;
|
|
4081
4588
|
exports.searchInstances = searchInstances;
|
|
4082
4589
|
exports.searchTasks = searchTasks;
|
|
4590
|
+
exports.searchVoices = searchVoices;
|
|
4591
|
+
exports.selectCallMode = selectCallMode;
|
|
4083
4592
|
exports.selectDateFromCalendar = selectDateFromCalendar;
|
|
4084
4593
|
exports.selectLLMModel = selectLLMModel;
|
|
4594
|
+
exports.selectLlmProvider = selectLlmProvider;
|
|
4085
4595
|
exports.selectPrivacyAction = selectPrivacyAction;
|
|
4596
|
+
exports.selectSttProvider = selectSttProvider;
|
|
4086
4597
|
exports.selectTaskInList = selectTaskInList;
|
|
4598
|
+
exports.selectTtsProvider = selectTtsProvider;
|
|
4599
|
+
exports.selectVoice = selectVoice;
|
|
4600
|
+
exports.selectVoiceProvider = selectVoiceProvider;
|
|
4087
4601
|
exports.setBlockMessage = setBlockMessage;
|
|
4602
|
+
exports.setCallLanguage = setCallLanguage;
|
|
4603
|
+
exports.setEnableVideo = setEnableVideo;
|
|
4088
4604
|
exports.setEntitySelected = setEntitySelected;
|
|
4089
4605
|
exports.setOutputFilterEnabled = setOutputFilterEnabled;
|
|
4090
4606
|
exports.setPrivacyRouterEnabled = setPrivacyRouterEnabled;
|
|
4607
|
+
exports.setScreenSharePrompt = setScreenSharePrompt;
|
|
4608
|
+
exports.setUseFunctionCallingEnabled = setUseFunctionCallingEnabled;
|
|
4091
4609
|
exports.setupSandboxInstance = setupSandboxInstance;
|
|
4092
4610
|
exports.shouldAddNewRowWhenClickingAddRowButton = shouldAddNewRowWhenClickingAddRowButton;
|
|
4093
4611
|
exports.shouldAllowEditingCellValuesInCSVEditor = shouldAllowEditingCellValuesInCSVEditor;
|
|
@@ -4109,8 +4627,11 @@ exports.signUpWithEmailAndPassword = signUpWithEmailAndPassword;
|
|
|
4109
4627
|
exports.switchToMemoryTab = switchToMemoryTab;
|
|
4110
4628
|
exports.switchToPrivacyTab = switchToPrivacyTab;
|
|
4111
4629
|
exports.switchToSandboxTab = switchToSandboxTab;
|
|
4630
|
+
exports.switchToScreenShareTab = switchToScreenShareTab;
|
|
4112
4631
|
exports.switchToSkillsTab = switchToSkillsTab;
|
|
4113
4632
|
exports.switchToTasksTab = switchToTasksTab;
|
|
4633
|
+
exports.switchToVoiceSubTab = switchToVoiceSubTab;
|
|
4634
|
+
exports.switchToVoiceTab = switchToVoiceTab;
|
|
4114
4635
|
exports.teardownSandboxInstance = teardownSandboxInstance;
|
|
4115
4636
|
exports.test = test;
|
|
4116
4637
|
exports.toggleAutoPush = toggleAutoPush;
|