Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 58 additions & 15 deletions docs/samples/contact-center/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,17 +98,17 @@ function isIncomingTask(task, agentId) {
);
};

// Store and Grab `access-token` from sessionStorage
if (sessionStorage.getItem('date') > new Date().getTime()) {
tokenElm.value = sessionStorage.getItem('access-token');
// Store and Grab `access-token` from localStorage
if (localStorage.getItem('date') > new Date().getTime()) {
tokenElm.value = localStorage.getItem('access-token');
}
else {
sessionStorage.removeItem('access-token');
localStorage.removeItem('access-token');
}

tokenElm.addEventListener('change', (event) => {
sessionStorage.setItem('access-token', event.target.value);
sessionStorage.setItem('date', new Date().getTime() + (12 * 60 * 60 * 1000));
tokenElm.addEventListener('input', (event) => {
localStorage.setItem('access-token', event.target.value);
localStorage.setItem('date', new Date().getTime() + (12 * 60 * 60 * 1000));
});

setAgentStateButton.addEventListener('click', () => {
Expand All @@ -135,6 +135,8 @@ async function uploadLogs() {
function changeAuthType() {
switch (authTypeElm.value) {
case 'accessToken':
tokenElm.readOnly = false;
saveElm.disabled = false;
toggleDisplay('credentials', true);
toggleDisplay('oauth', false);
break;
Expand Down Expand Up @@ -192,10 +194,15 @@ function initOauth() {
.concat(additionalScopes))
).join(' ');

// Use environment variable if available (via build process) or global variable (via test injection), otherwise fallback to hardcoded
const clientId = (typeof process !== 'undefined' && process.env && process.env.CLIENT_ID) ||
(window.WEBEX_CLIENT_ID) ||
'C04ef08ffce356c3161bb66b15dbdd98d26b6c683c5ce1a1a89efad545fdadd74';

webex = window.webex = Webex.init({
config: generateWebexConfig({
credentials: {
client_id: 'C04ef08ffce356c3161bb66b15dbdd98d26b6c683c5ce1a1a89efad545fdadd74',
client_id: clientId,
redirect_uri: redirectUri,
scope: requestedScopes,
}
Expand All @@ -205,14 +212,51 @@ function initOauth() {
localStorage.setItem('OAuth', true);

webex.once('ready', () => {
const syncAccessTokenFromSupertoken = () => {
const accessToken = webex?.credentials?.supertoken?.access_token;
if (!accessToken) {
return;
}

tokenElm.value = accessToken;
localStorage.setItem('access-token', accessToken);
localStorage.setItem('date', new Date().getTime() + (12 * 60 * 60 * 1000));

tokenElm.readOnly = true;
saveElm.disabled = true;
authStatusElm.innerText = 'Saved access token!';
registerStatus.innerHTML = 'Not Subscribed';
registerBtn.disabled = false;
initializeEngageWidget();
};

webex.credentials.on('change:supertoken', syncAccessTokenFromSupertoken);

oauthFormElm.addEventListener('submit', (event) => {
event.preventDefault();
// initiate the login sequence if not authenticated.
webex.authorization.initiateLogin();
const isIframe = window !== window.parent;

// When loaded inside an iframe, pop OAuth in a new window so redirect works.
if (isIframe) {
webex.authorization.initiateLogin({
separateWindow: {
width: 800,
height: 600,
menubar: 'no',
toolbar: 'no',
location: 'yes'
}
});
}
else {
// initiate the login sequence if not authenticated.
webex.authorization.initiateLogin();
}
});

if (webex.canAuthorize) {
oauthStatusElm.innerText = 'Authenticated';
syncAccessTokenFromSupertoken();
}
});
}
Expand Down Expand Up @@ -1351,7 +1395,7 @@ function initWebex(e) {
e.preventDefault();
console.log('Authentication#initWebex()');

tokenElm.disabled = true;
tokenElm.readOnly = true;
saveElm.disabled = true;
authStatusElm.innerText = 'initializing...';

Expand Down Expand Up @@ -1947,9 +1991,9 @@ if (window.location.hash) {
const expiresIn = urlParams.get('expires_in');

if (accessToken) {
sessionStorage.setItem('access-token', accessToken);
sessionStorage.setItem('date', new Date().getTime() + parseInt(expiresIn, 10));
tokenElm.disabled = true;
localStorage.setItem('access-token', accessToken);
localStorage.setItem('date', new Date().getTime() + parseInt(expiresIn, 10));
tokenElm.readOnly = true;
saveElm.disabled = true;
authStatusElm.innerText = 'Saved access token!';
registerStatus.innerHTML = 'Not Subscribed';
Expand Down Expand Up @@ -2354,4 +2398,3 @@ updateLoginOptionElm.addEventListener('change', updateApplyButtonState);
updateDialNumberElm.addEventListener('input', updateApplyButtonState);

updateApplyButtonState();

15 changes: 15 additions & 0 deletions packages/@webex/contact-center/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"license": "Cisco's General Terms (https://www.cisco.com/site/us/en/about/legal/contract-experience/index.html)",
"contributors": [
"Adhwaith Menon <[email protected]>",
"Arun Ganeshan <[email protected]>",
"Bharath Balan <[email protected]>",
"Kesava Krishnan Madavan <[email protected]>",
"Priya Kesari <[email protected]>",
Expand Down Expand Up @@ -41,6 +42,17 @@
"test": "yarn test:style && yarn test:unit",
"test:style": "eslint 'src/**/*.ts'",
"test:unit": "webex-legacy-tools test --unit --runner jest",
"fetch:e2e:tokens": "node ./scripts/fetch-e2e-access-tokens.js",
"pretest:e2e": "yarn run -T fetch:e2e:tokens",
"test:e2e": "playwright test",
"test:e2e:smoke": "playwright test smoke",
"test:e2e:headed": "playwright test --headed",
"test:e2e:ui": "playwright test --ui",
"test:e2e:debug": "playwright test --debug",
"test:e2e:chromium": "playwright test --project=chromium",
"test:e2e:firefox": "playwright test --project=firefox",
"test:e2e:report": "playwright show-report test/e2e/playwright-report",
"test:e2e:codegen": "playwright codegen http://localhost:8080/contact-center/",
"deploy:npm": "yarn npm publish"
},
"dependencies": {
Expand All @@ -58,6 +70,7 @@
"devDependencies": {
"@babel/core": "^7.22.11",
"@babel/preset-typescript": "7.22.11",
"@playwright/test": "^1.57.0",
"@types/jest": "27.4.1",
"@typescript-eslint/eslint-plugin": "5.38.1",
"@typescript-eslint/parser": "5.38.1",
Expand All @@ -66,6 +79,7 @@
"@webex/jest-config-legacy": "workspace:*",
"@webex/legacy-tools": "workspace:*",
"@webex/test-helper-mock-webex": "workspace:*",
"dotenv": "^17.2.3",
"eslint": "^8.24.0",
"eslint-config-airbnb-base": "15.0.0",
"eslint-config-prettier": "8.3.0",
Expand All @@ -76,6 +90,7 @@
"eslint-plugin-tsdoc": "0.2.14",
"jest": "27.5.1",
"jest-junit": "13.0.0",
"nodemailer": "^6.9.13",
"prettier": "2.5.1",
"typedoc": "^0.25.0",
"typescript": "4.9.5"
Expand Down
97 changes: 97 additions & 0 deletions packages/@webex/contact-center/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import {defineConfig, devices} from '@playwright/test';
import dotenv from 'dotenv';
import path from 'path';
import {USER_SETS} from './test/e2e/playwright/test-data';

dotenv.config({path: path.resolve(__dirname, './test/e2e/.env.contact-center.e2e')});

const dummyAudioPath = path.resolve(__dirname, './test/e2e/playwright/wav/dummyAudio.wav');

/**
* Playwright configuration for the Contact Center E2E suites.
* Each USER_SET in test-data.ts becomes its own project so we can parallelize the
* multi-agent scenarios without clashing credentials.
*/
export default defineConfig({
testDir: './test/e2e/playwright/suites',
outputDir: './test/e2e/test-results',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 1 : 0,
workers: Object.keys(USER_SETS).length || 1,
timeout: 3 * 60 * 1000,
expect: {
timeout: 10 * 1000,
},
reporter: [
[
'html',
{
outputFolder: './test/e2e/playwright-report',
open: process.env.CI ? 'never' : 'on-failure',
},
],
[
'junit',
{
outputFile: './test/e2e/junit.xml',
},
],
['list'],
],
use: {
baseURL: 'https://localhost:8000',
trace: process.env.CI ? 'on-first-retry' : 'retain-on-failure',
video: process.env.CI ? 'retain-on-failure' : 'on',
screenshot: 'only-on-failure',
viewport: {width: 1280, height: 720},
ignoreHTTPSErrors: true,
launchOptions: {
args: [
'--use-fake-ui-for-media-stream',
'--use-fake-device-for-media-stream',
'--allow-insecure-localhost',
],
},
},
projects: [
{
name: 'OAuth: Get Access Token',
testDir: './test/e2e/playwright',
testMatch: /global\.setup\.ts/,
},
...Object.entries(USER_SETS).map(([setName, setData], index) => ({
name: setName,
testMatch: [`**/suites/${setData.TEST_SUITE}`],
dependencies: ['OAuth: Get Access Token'],
retries: process.env.CI ? 1 : 0,
use: {
...devices['Desktop Chrome'],
channel: 'chrome',
baseURL: 'https://localhost:8000',
launchOptions: {
args: [
'--use-fake-ui-for-media-stream',
'--use-fake-device-for-media-stream',
'--allow-insecure-localhost',
`--use-file-for-fake-audio-capture=${dummyAudioPath}`,
`--remote-debugging-port=${9221 + index}`,
'--disable-site-isolation-trials',
'--disable-web-security',
'--no-sandbox',
'--disable-features=WebRtcHideLocalIpsWithMdns',
'--allow-file-access-from-files',
`--window-position=${index * 1400},0`,
'--window-size=1280,720',
],
},
},
})),
],
webServer: {
command: 'cd ../../.. && npx webpack serve --color --env NODE_ENV=development --host localhost',
port: 8000,
reuseExistingServer: !process.env.CI,
timeout: 2 * 60 * 1000,
},
});
44 changes: 44 additions & 0 deletions packages/@webex/contact-center/test/e2e/.env.contact-center.e2e
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Contact Center E2E Test Environment Variables

# ============================================================================
# AGENT 1 CREDENTIALS
# ============================================================================
[email protected]
E2E_AGENT1_PASSWORD=$rrA%Z1zB6
E2E_AGENT1_TOKEN=
[email protected]
E2E_AGENT1_EXTENSION=1015

# ============================================================================
# AGENT 2 CREDENTIALS
# ============================================================================
[email protected]
E2E_AGENT2_PASSWORD=$rrA%Z1zB6
E2E_AGENT2_TOKEN=
[email protected]
E2E_AGENT2_EXTENSION=1016

# ============================================================================
# CONTACT CENTER CONFIGURATION
# ============================================================================
E2E_ORG_ID=d9ec32d3-2e8d-411a-bcce-eb2e5e2eb79c
E2E_TENANT_ID=
E2E_DATACENTER=us1
E2E_CLIENT_ID=C70599433db154842e919ad9e18273d835945ff198251c82204b236b157b3a213
E2E_CLIENT_SECRET=575ba9f5034f8a28dfef2770870c50bfc6e0b2b749f14e6a14845a1a47622f87
E2E_REDIRECT_URI=https://localhost:8000/
E2E_OAUTH_SCOPE="cjp:config_read cjp:config_write cjp:config cjp:user spark:webrtc_calling spark:calls_read spark:calls_write spark:xsi spark:kms"

# ============================================================================
# TEST RESOURCES
# ============================================================================
E2E_QUEUE_NAME=Queue e2e 1
E2E_ENTRY_POINT=+16266820030
[email protected]
E2E_CHAT_URL=https://widgets.webex.com/chat-client-e2e.html

# ============================================================================
# TEST CONFIGURATION
# ============================================================================
E2E_TEST_TIMEOUT=120000
E2E_WAIT_FOR_TASK_TIMEOUT=60000
Loading
Loading