genesys-spark 4.0.0-beta.64 → 4.0.0-beta.65
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/index.js +1 -1
- package/package.json +5 -1
- package/.eslintignore +0 -5
- package/.eslintrc.json +0 -34
- package/.lintstagedrc.json +0 -6
- package/.prettierignore +0 -2
- package/dist/test/hosts.spec.d.ts +0 -1
- package/dist/test/intl.spec.d.ts +0 -1
- package/dist/test/loading.spec.d.ts +0 -1
- package/jest.config.js +0 -5
- package/rollup.config.js +0 -23
- package/src/hosts.ts +0 -57
- package/src/index.ts +0 -39
- package/src/intl.ts +0 -88
- package/src/loading.ts +0 -90
- package/test/hosts.spec.ts +0 -69
- package/test/intl.spec.ts +0 -104
- package/test/loading.spec.ts +0 -150
- package/tsconfig.json +0 -10
package/dist/index.js
CHANGED
|
@@ -241,7 +241,7 @@ var intl = /*#__PURE__*/Object.freeze({
|
|
|
241
241
|
relativeTimeFormat: relativeTimeFormat
|
|
242
242
|
});
|
|
243
243
|
|
|
244
|
-
var ASSET_PREFIX = '/spark-components/build-assets/4.0.0-beta.
|
|
244
|
+
var ASSET_PREFIX = '/spark-components/build-assets/4.0.0-beta.65-3/genesys-webcomponents/';
|
|
245
245
|
var SCRIPT_PATH = 'genesys-webcomponents.esm.js';
|
|
246
246
|
var STYLE_PATH = 'genesys-webcomponents.css';
|
|
247
247
|
var assetsOrigin = getAssetsOrigin();
|
package/package.json
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "genesys-spark",
|
|
3
|
-
"version": "4.0.0-beta.
|
|
3
|
+
"version": "4.0.0-beta.65",
|
|
4
4
|
"description": "",
|
|
5
5
|
"license": "ISC",
|
|
6
6
|
"author": "",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"main": "dist/index.js",
|
|
9
|
+
"files": [
|
|
10
|
+
"dist/src/*.d.ts"
|
|
11
|
+
],
|
|
9
12
|
"scripts": {
|
|
10
13
|
"build": "rollup -c",
|
|
11
14
|
"dev": "rollup -c --watch",
|
|
@@ -16,6 +19,7 @@
|
|
|
16
19
|
"test.watch": "jest --watch",
|
|
17
20
|
"version-sync": "npm version --no-git-tag-version --allow-same-version"
|
|
18
21
|
},
|
|
22
|
+
"types": "dist/src/index.d.ts",
|
|
19
23
|
"dependencies": {
|
|
20
24
|
"@rollup/plugin-replace": "^5.0.5",
|
|
21
25
|
"@rollup/plugin-typescript": "^11.1.5",
|
package/.eslintignore
DELETED
package/.eslintrc.json
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"parser": "@typescript-eslint/parser",
|
|
3
|
-
"plugins": ["@typescript-eslint"],
|
|
4
|
-
"root": true,
|
|
5
|
-
"env": {
|
|
6
|
-
"browser": true,
|
|
7
|
-
"es6": true
|
|
8
|
-
},
|
|
9
|
-
"rules": {
|
|
10
|
-
"no-unused-vars": "off",
|
|
11
|
-
"no-undef": "off"
|
|
12
|
-
},
|
|
13
|
-
"extends": ["genesys-spark-components"],
|
|
14
|
-
"parserOptions": {
|
|
15
|
-
"sourceType": "module",
|
|
16
|
-
"project": ["./tsconfig.json"]
|
|
17
|
-
},
|
|
18
|
-
"overrides": [
|
|
19
|
-
{
|
|
20
|
-
"files": ["./test/**/*.ts"],
|
|
21
|
-
"env": {
|
|
22
|
-
"node": true,
|
|
23
|
-
"es6": true,
|
|
24
|
-
"jest": true
|
|
25
|
-
},
|
|
26
|
-
"extends": ["genesys-spark-components/typescript"],
|
|
27
|
-
"rules": {
|
|
28
|
-
"@typescript-eslint/no-unsafe-assignment": "off",
|
|
29
|
-
"@typescript-eslint/ban-ts-comment": "off",
|
|
30
|
-
"@typescript-eslint/no-explicit-any": "off"
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
]
|
|
34
|
-
}
|
package/.lintstagedrc.json
DELETED
package/.prettierignore
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/test/intl.spec.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/jest.config.js
DELETED
package/rollup.config.js
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import replace from '@rollup/plugin-replace';
|
|
2
|
-
import typescript from '@rollup/plugin-typescript';
|
|
3
|
-
|
|
4
|
-
const IS_DEV_MODE = process.env.ROLLUP_WATCH === 'true';
|
|
5
|
-
|
|
6
|
-
export default {
|
|
7
|
-
input: 'src/index.ts',
|
|
8
|
-
output: {
|
|
9
|
-
dir: 'dist'
|
|
10
|
-
},
|
|
11
|
-
plugins: [
|
|
12
|
-
replace({
|
|
13
|
-
values: {
|
|
14
|
-
IS_DEV_MODE: IS_DEV_MODE,
|
|
15
|
-
__ASSET_PREFIX__: IS_DEV_MODE
|
|
16
|
-
? '/dist/genesys-webcomponents/'
|
|
17
|
-
: process.env.COMPONENT_ASSETS_PATH
|
|
18
|
-
},
|
|
19
|
-
preventAssignment: true
|
|
20
|
-
}),
|
|
21
|
-
typescript({ noEmitOnError: false })
|
|
22
|
-
]
|
|
23
|
-
};
|
package/src/hosts.ts
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
declare global {
|
|
2
|
-
// IS_DEV_MODE is rewritten by @rollup/plugin-replace. This definition lets
|
|
3
|
-
// our typescript file typecheck.
|
|
4
|
-
var IS_DEV_MODE: boolean;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
// Default domain to load assets from
|
|
8
|
-
const DEFAULT_DOMAIN = 'mypurecloud.com';
|
|
9
|
-
|
|
10
|
-
// List of Genesys UI domains that do not follow the ${region}.pure.cloud format
|
|
11
|
-
const NON_STANDARD_DOMAINS = [
|
|
12
|
-
'inindca.com',
|
|
13
|
-
'inintca.com',
|
|
14
|
-
'mypurecloud.com.au',
|
|
15
|
-
'mypurecloud.com',
|
|
16
|
-
'mypurecloud.de',
|
|
17
|
-
'mypurecloud.ie',
|
|
18
|
-
'mypurecloud.jp'
|
|
19
|
-
// 'use2.us-gov-pure.cloud', Assets are not currently deployed to FedRAMP. It should fall back to the default domain.
|
|
20
|
-
];
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Returns the origin that web component assets should be loaded from.
|
|
24
|
-
* Will use the domain of the current window if it matches a Genesys domain.
|
|
25
|
-
*/
|
|
26
|
-
export function getAssetsOrigin(): string {
|
|
27
|
-
if (IS_DEV_MODE == true) {
|
|
28
|
-
// This conditional is optimized out in production due to @rollup/plugin-replace
|
|
29
|
-
// and rollup's dead code elimination
|
|
30
|
-
return 'http://localhost:3333';
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const matchedDomain = getRegionDomain();
|
|
34
|
-
return `https://app.${matchedDomain || DEFAULT_DOMAIN}`;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export function getFontOrigin(): string {
|
|
38
|
-
if (IS_DEV_MODE == true) {
|
|
39
|
-
// Fonts aren't locally hosted during dev mode
|
|
40
|
-
return 'http://app.inindca.com';
|
|
41
|
-
}
|
|
42
|
-
return getAssetsOrigin();
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function getRegionDomain() {
|
|
46
|
-
const pageHost = window.location.hostname;
|
|
47
|
-
|
|
48
|
-
// We can automatically handle the standard domain format: ${region}.pure.cloud
|
|
49
|
-
if (pageHost.endsWith('.pure.cloud')) {
|
|
50
|
-
return pageHost.split('.').slice(-3).join('.');
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// For older domains, we have to do a lookup
|
|
54
|
-
return NON_STANDARD_DOMAINS.find(regionDomain =>
|
|
55
|
-
pageHost.endsWith(regionDomain)
|
|
56
|
-
);
|
|
57
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { getAssetsOrigin, getFontOrigin } from './hosts';
|
|
2
|
-
import {
|
|
3
|
-
checkAndLoadScript,
|
|
4
|
-
checkAndLoadStyle,
|
|
5
|
-
checkAndLoadFonts
|
|
6
|
-
} from './loading';
|
|
7
|
-
|
|
8
|
-
const ASSET_PREFIX = '__ASSET_PREFIX__';
|
|
9
|
-
const SCRIPT_PATH = 'genesys-webcomponents.esm.js';
|
|
10
|
-
const STYLE_PATH = 'genesys-webcomponents.css';
|
|
11
|
-
|
|
12
|
-
const assetsOrigin = getAssetsOrigin();
|
|
13
|
-
const SCRIPT_SRC = `${assetsOrigin}${ASSET_PREFIX}${SCRIPT_PATH}`;
|
|
14
|
-
const STYLE_HREF = `${assetsOrigin}${ASSET_PREFIX}${STYLE_PATH}`;
|
|
15
|
-
const fontOrigin = getFontOrigin();
|
|
16
|
-
const FONTS = {
|
|
17
|
-
Urbanist: `${fontOrigin}/webfonts/urbanist.css`,
|
|
18
|
-
'Noto Sans': `${fontOrigin}/webfonts/noto-sans.css`
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Loads the spark web components, as well as required CSS and fonts from a
|
|
23
|
-
* shared CDN. Performance can be optimized by pre-loading fonts in static HTML.
|
|
24
|
-
*
|
|
25
|
-
* @returns a promise that succeeds if the component script and styles
|
|
26
|
-
* load successfully. It is not recommended to wait on this promise or to stop
|
|
27
|
-
* application bootstrap if it rejects. Its primary use should be for logging
|
|
28
|
-
* unexpected failures.
|
|
29
|
-
*/
|
|
30
|
-
export function registerSparkComponents(): Promise<void> {
|
|
31
|
-
return Promise.all([
|
|
32
|
-
checkAndLoadScript(SCRIPT_SRC),
|
|
33
|
-
checkAndLoadStyle(STYLE_HREF),
|
|
34
|
-
checkAndLoadFonts(FONTS)
|
|
35
|
-
]).then();
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// Re-export of utility modules
|
|
39
|
-
export * as Intl from './intl';
|
package/src/intl.ts
DELETED
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Provides an [Intl.DateTimeFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat)
|
|
3
|
-
* object for formatting dates and times. Unlike the native version, `locale` is
|
|
4
|
-
* an optional argument. If not provided, the function will try to determine the
|
|
5
|
-
* locale from the DOM, where it should be set for a11y reasons.
|
|
6
|
-
* @param locale optional locale to use for formatting
|
|
7
|
-
* @param options options to pass to the Intl.DateTimeFormat constructor
|
|
8
|
-
* @returns a new DateTimeFormat
|
|
9
|
-
*/
|
|
10
|
-
export function dateTimeFormat(
|
|
11
|
-
localeOrOptions: string | Intl.DateTimeFormatOptions,
|
|
12
|
-
options?: Intl.DateTimeFormatOptions
|
|
13
|
-
): Intl.DateTimeFormat {
|
|
14
|
-
let locale = undefined;
|
|
15
|
-
if (typeof localeOrOptions === 'string') {
|
|
16
|
-
locale = localeOrOptions;
|
|
17
|
-
} else {
|
|
18
|
-
options = localeOrOptions;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
if (locale != undefined) {
|
|
22
|
-
return new Intl.DateTimeFormat(locale, options);
|
|
23
|
-
} else {
|
|
24
|
-
const userLocale = determineDisplayLocale();
|
|
25
|
-
return new Intl.DateTimeFormat(userLocale, options);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
/**
|
|
29
|
-
* Provides an [Intl.RelativeTimeFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/RelativeTimeFormat)
|
|
30
|
-
* object for formatting dates and times. Unlike the native version, `locale` is
|
|
31
|
-
* an optional argument. If not provided, the function will try to determine the
|
|
32
|
-
* locale from the DOM, where it should be set for a11y reasons.
|
|
33
|
-
* @param locale optional locale to use for formatting
|
|
34
|
-
* @param options options to pass to the Intl.RelativeTimeFormat constructor
|
|
35
|
-
* @returns a new RelativeTimeFormat
|
|
36
|
-
*/
|
|
37
|
-
export function relativeTimeFormat(
|
|
38
|
-
localeOrOptions: string | Intl.RelativeTimeFormatOptions,
|
|
39
|
-
options?: Intl.RelativeTimeFormatOptions
|
|
40
|
-
): Intl.RelativeTimeFormat {
|
|
41
|
-
let locale = undefined;
|
|
42
|
-
if (typeof localeOrOptions === 'string') {
|
|
43
|
-
locale = localeOrOptions;
|
|
44
|
-
} else {
|
|
45
|
-
options = localeOrOptions;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (locale != undefined) {
|
|
49
|
-
return new Intl.RelativeTimeFormat(locale, options);
|
|
50
|
-
} else {
|
|
51
|
-
const userLocale = determineDisplayLocale();
|
|
52
|
-
return new Intl.RelativeTimeFormat(userLocale, options);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Makes a best effort to return the locale that should be used for a given element
|
|
58
|
-
* by checking language tags on ancestors. If no element is provided, it will
|
|
59
|
-
* start with the document's <body> tag. If no locale can be found, it will use
|
|
60
|
-
* the browser's locale preference. It will also try to add a region to regionless
|
|
61
|
-
* locales when there is a partial match with the browser's locale.
|
|
62
|
-
* @returns a locale string (e.g. 'en-US', 'en', 'de-DE', etc)
|
|
63
|
-
*/
|
|
64
|
-
export function determineDisplayLocale(
|
|
65
|
-
element: HTMLElement = document.body
|
|
66
|
-
): string {
|
|
67
|
-
const domLocale = element.closest<HTMLElement>('[lang]')?.lang;
|
|
68
|
-
if (!domLocale || browserHasRegionData(domLocale)) {
|
|
69
|
-
// If we can't find a locale in the DOM, or we find a locale without a region that matches the
|
|
70
|
-
// users's browser locale, use the browser locale.
|
|
71
|
-
return navigator.language;
|
|
72
|
-
} else {
|
|
73
|
-
return domLocale;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Returns true if the provided locale only has a language, but the user's
|
|
79
|
-
* browser settings have the same language with a locale.
|
|
80
|
-
* @param localeString The locale to check
|
|
81
|
-
* @returns true if the region can be guessed from the browser settings.
|
|
82
|
-
*/
|
|
83
|
-
function browserHasRegionData(localeString: string): boolean {
|
|
84
|
-
return (
|
|
85
|
-
localeString.length == 2 &&
|
|
86
|
-
navigator.language.startsWith(`${localeString}-`)
|
|
87
|
-
);
|
|
88
|
-
}
|
package/src/loading.ts
DELETED
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Add a script tag to the document if it is not already present.
|
|
3
|
-
* @param scriptSrc The src attribute of the script
|
|
4
|
-
* @returns a promise that resolves if the script loads or is already present
|
|
5
|
-
*/
|
|
6
|
-
export function checkAndLoadScript(scriptSrc: string): Promise<void> {
|
|
7
|
-
const existingTag = document.querySelector(`script[src="${scriptSrc}"]`);
|
|
8
|
-
if (existingTag) {
|
|
9
|
-
return Promise.resolve();
|
|
10
|
-
} else {
|
|
11
|
-
const scriptTag = document.createElement('script');
|
|
12
|
-
scriptTag.setAttribute('type', 'module');
|
|
13
|
-
scriptTag.setAttribute('src', scriptSrc);
|
|
14
|
-
const result = new Promise<void>((resolve, reject) => {
|
|
15
|
-
scriptTag.addEventListener('load', () => {
|
|
16
|
-
resolve();
|
|
17
|
-
});
|
|
18
|
-
scriptTag.addEventListener('error', () => {
|
|
19
|
-
reject(`Spark script failed to load: ${scriptSrc}`);
|
|
20
|
-
});
|
|
21
|
-
});
|
|
22
|
-
document.head.appendChild(scriptTag);
|
|
23
|
-
return result;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Add a stylesheet link tag to the document if it is not already present.
|
|
29
|
-
* @param styleHref The href attribute of the stylesheet
|
|
30
|
-
* @returns a promise that resolves if the style loads or is already present
|
|
31
|
-
*/
|
|
32
|
-
export function checkAndLoadStyle(styleHref: string): Promise<void> {
|
|
33
|
-
const existingTag = document.querySelector(`link[href="${styleHref}"]`);
|
|
34
|
-
if (existingTag) {
|
|
35
|
-
return Promise.resolve();
|
|
36
|
-
} else {
|
|
37
|
-
const styleTag = document.createElement('link');
|
|
38
|
-
styleTag.setAttribute('href', styleHref);
|
|
39
|
-
styleTag.setAttribute('rel', 'stylesheet');
|
|
40
|
-
const result = new Promise<void>((resolve, reject) => {
|
|
41
|
-
styleTag.addEventListener('load', () => {
|
|
42
|
-
resolve();
|
|
43
|
-
});
|
|
44
|
-
styleTag.addEventListener('error', () => {
|
|
45
|
-
reject(`Spark styles failed to load: ${styleHref}`);
|
|
46
|
-
});
|
|
47
|
-
});
|
|
48
|
-
document.head.appendChild(styleTag);
|
|
49
|
-
return result;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Given an object that maps font-family identifiers to CSS urls e.g:
|
|
55
|
-
* {
|
|
56
|
-
* "Urbanist": "/urbanist.css",
|
|
57
|
-
* "Noto Sans": "/noto-sans.css"
|
|
58
|
-
* }
|
|
59
|
-
* This function checks for loaded fonts with those identifiers. If a font is
|
|
60
|
-
* not found, the corresponding stylesheet is added to the document.
|
|
61
|
-
* @param fonts An object mapping font-family identifiers to CSS file urls
|
|
62
|
-
* @returns A promise that resolves once the script tags has finished loading.
|
|
63
|
-
* It does not fail if the script tags fail to load because we don't want to fail
|
|
64
|
-
* the whole component loading process in that situation.
|
|
65
|
-
*/
|
|
66
|
-
export function checkAndLoadFonts(fonts: {
|
|
67
|
-
[key: string]: string;
|
|
68
|
-
}): Promise<void> {
|
|
69
|
-
const fontsToLoad = { ...fonts }; //clone our input so we can safely mutate it.
|
|
70
|
-
|
|
71
|
-
document.fonts.forEach(fontFace => {
|
|
72
|
-
// If the family is defined with quotes in CSS (e.g. `font-family: "Noto Sans"),
|
|
73
|
-
// those quotes may be preserved JS, depending on the browser.
|
|
74
|
-
const normalizedFamily = fontFace.family.replace(/"/g, '');
|
|
75
|
-
if (fontsToLoad[normalizedFamily]) {
|
|
76
|
-
// remove the font from the set to load
|
|
77
|
-
delete fontsToLoad[normalizedFamily];
|
|
78
|
-
}
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
return Promise.all(
|
|
82
|
-
Object.values(fontsToLoad).map(href => {
|
|
83
|
-
return checkAndLoadStyle(href).catch(() => {
|
|
84
|
-
// Don't fail loading process for fonts, since the components
|
|
85
|
-
// should still be reasonably usable.
|
|
86
|
-
console.info(`genesys-spark: couldn't load font style ${href}`);
|
|
87
|
-
});
|
|
88
|
-
})
|
|
89
|
-
).then(() => {}); // flatten the promise array
|
|
90
|
-
}
|
package/test/hosts.spec.ts
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import { getAssetsOrigin, getFontOrigin } from '../src/hosts';
|
|
2
|
-
|
|
3
|
-
const NON_STANDARD_DOMAINS = [
|
|
4
|
-
'inindca.com',
|
|
5
|
-
'inintca.com',
|
|
6
|
-
'mypurecloud.com.au',
|
|
7
|
-
'mypurecloud.com',
|
|
8
|
-
'mypurecloud.de',
|
|
9
|
-
'mypurecloud.ie',
|
|
10
|
-
'mypurecloud.jp'
|
|
11
|
-
];
|
|
12
|
-
|
|
13
|
-
describe('The hosts module', () => {
|
|
14
|
-
beforeEach(() => {
|
|
15
|
-
window.IS_DEV_MODE = false;
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
describe('when determining asset domains', () => {
|
|
19
|
-
it('Will return the correct domain for pages in a `pure.cloud` region', () => {
|
|
20
|
-
setLocation('https://uis.region.pure.cloud/page');
|
|
21
|
-
expect(getAssetsOrigin()).toBe('https://app.region.pure.cloud');
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
it('Will return a default domain for non-genesys hosted pages', () => {
|
|
25
|
-
setLocation('https://www.example.com/page');
|
|
26
|
-
expect(getAssetsOrigin()).toBe('https://app.mypurecloud.com');
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
NON_STANDARD_DOMAINS.forEach(domain => {
|
|
30
|
-
const withSubDomain = `https://dummySubdomain.${domain}/page`;
|
|
31
|
-
it(`Will recognize ${withSubDomain} as a Genesys domain`, () => {
|
|
32
|
-
setLocation(withSubDomain);
|
|
33
|
-
expect(getAssetsOrigin()).toBe(`https://app.${domain}`);
|
|
34
|
-
});
|
|
35
|
-
});
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
describe('when determining font domains', () => {
|
|
39
|
-
it('Will return the correct domain for pages in a `pure.cloud` region', () => {
|
|
40
|
-
setLocation('https://uis.region.pure.cloud/page');
|
|
41
|
-
expect(getFontOrigin()).toBe('https://app.region.pure.cloud');
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it('Will return a default domain for non-genesys hosted pages', () => {
|
|
45
|
-
setLocation('https://www.example.com/page');
|
|
46
|
-
expect(getFontOrigin()).toBe('https://app.mypurecloud.com');
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
NON_STANDARD_DOMAINS.forEach(domain => {
|
|
50
|
-
const withSubDomain = `https://dummySubdomain.${domain}/page`;
|
|
51
|
-
it(`Will recognize ${withSubDomain} as a Genesys domain`, () => {
|
|
52
|
-
setLocation(withSubDomain);
|
|
53
|
-
expect(getFontOrigin()).toBe(`https://app.${domain}`);
|
|
54
|
-
});
|
|
55
|
-
});
|
|
56
|
-
});
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* This is an absolutely disgusting abdication of the type system, but it's a
|
|
61
|
-
* damn sight simpler than most ways to get dynamic per-test location updates.
|
|
62
|
-
* See: https://github.com/jestjs/jest/issues/5124
|
|
63
|
-
*/
|
|
64
|
-
function setLocation(location: string) {
|
|
65
|
-
//@ts-ignore
|
|
66
|
-
delete window.location;
|
|
67
|
-
//@ts-ignore
|
|
68
|
-
window.location = new URL(location);
|
|
69
|
-
}
|
package/test/intl.spec.ts
DELETED
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
determineDisplayLocale,
|
|
3
|
-
dateTimeFormat,
|
|
4
|
-
relativeTimeFormat
|
|
5
|
-
} from '../src/intl';
|
|
6
|
-
|
|
7
|
-
describe('The intl module', () => {
|
|
8
|
-
beforeEach(() => {
|
|
9
|
-
// Reset the Dom
|
|
10
|
-
document.documentElement.innerHTML = '<head></head><body></body>';
|
|
11
|
-
// Remove language attribute from <html>
|
|
12
|
-
document.documentElement.removeAttribute('lang');
|
|
13
|
-
// Reset/set navigator.language to a known value
|
|
14
|
-
Object.defineProperty(window.navigator, 'language', {
|
|
15
|
-
value: 'yy-YY',
|
|
16
|
-
configurable: true
|
|
17
|
-
});
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
describe('When determining what locale to use', () => {
|
|
21
|
-
test('It will determine the display locale from <body> if no locale is provided', () => {
|
|
22
|
-
document.body.setAttribute('lang', 'xx-XX');
|
|
23
|
-
expect(determineDisplayLocale()).toBe('xx-XX');
|
|
24
|
-
});
|
|
25
|
-
test('It will determine the display locale from <html> if no locale is provided', () => {
|
|
26
|
-
document.documentElement.setAttribute('lang', 'xx-XX');
|
|
27
|
-
expect(determineDisplayLocale()).toBe('xx-XX');
|
|
28
|
-
});
|
|
29
|
-
test('Given an element with a language attribute, it will determine the locale from that tag', () => {
|
|
30
|
-
const target = document.createElement('div');
|
|
31
|
-
target.setAttribute('lang', 'xx-XX');
|
|
32
|
-
expect(determineDisplayLocale(target)).toBe('xx-XX');
|
|
33
|
-
});
|
|
34
|
-
test("Given an element without a language attribute, it will check the element's ancestors for a language attribute", () => {
|
|
35
|
-
const localeOwner = document.createElement('div');
|
|
36
|
-
localeOwner.setAttribute('lang', 'xx-XX');
|
|
37
|
-
localeOwner.innerHTML =
|
|
38
|
-
'<div><div><span class="target"></span></div></div>';
|
|
39
|
-
const target =
|
|
40
|
-
localeOwner.querySelector<HTMLElement>('.target') || undefined;
|
|
41
|
-
expect(target).not.toBeUndefined();
|
|
42
|
-
expect(determineDisplayLocale(target)).toBe('xx-XX');
|
|
43
|
-
});
|
|
44
|
-
test("If no language tag is found, uses the browser's language", () => {
|
|
45
|
-
expect(determineDisplayLocale()).toBe('yy-YY');
|
|
46
|
-
});
|
|
47
|
-
test('If given a partial match to the browser language, it will pull the region from the browser', () => {
|
|
48
|
-
document.body.setAttribute('lang', 'yy');
|
|
49
|
-
expect(determineDisplayLocale()).toBe('yy-YY');
|
|
50
|
-
});
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
describe('When creating a DateTimeFormat', () => {
|
|
54
|
-
const formatOptions: Intl.DateTimeFormatOptions = {};
|
|
55
|
-
beforeEach(() => {
|
|
56
|
-
jest.resetAllMocks();
|
|
57
|
-
jest.spyOn(Intl, 'DateTimeFormat');
|
|
58
|
-
});
|
|
59
|
-
it('Calls through to the browser implementation', () => {
|
|
60
|
-
dateTimeFormat('xx-XX', formatOptions);
|
|
61
|
-
expect(Intl.DateTimeFormat).toHaveBeenCalledWith('xx-XX', formatOptions);
|
|
62
|
-
});
|
|
63
|
-
it('The locale is optional and will defer to `determineDisplayLocale`', () => {
|
|
64
|
-
document.body.setAttribute('lang', 'xx-XX');
|
|
65
|
-
dateTimeFormat(formatOptions);
|
|
66
|
-
expect(Intl.DateTimeFormat).toHaveBeenCalledWith('xx-XX', formatOptions);
|
|
67
|
-
});
|
|
68
|
-
it('Maintains optional format options', () => {
|
|
69
|
-
dateTimeFormat('en-US');
|
|
70
|
-
expect(Intl.DateTimeFormat).toHaveBeenCalledWith('en-US', undefined);
|
|
71
|
-
});
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
describe('When creating a RelativeTimeFormat', () => {
|
|
75
|
-
const formatOptions: Intl.RelativeTimeFormatOptions = {};
|
|
76
|
-
beforeEach(() => {
|
|
77
|
-
jest.resetAllMocks();
|
|
78
|
-
// This has to be different than the DateTimeFormat mock - probably because of some ES6 class
|
|
79
|
-
// thing that I can't be bothered to figure out.
|
|
80
|
-
Object.defineProperty(Intl, 'RelativeTimeFormat', {
|
|
81
|
-
value: jest.fn()
|
|
82
|
-
});
|
|
83
|
-
});
|
|
84
|
-
it('Calls through to the browser implementation', () => {
|
|
85
|
-
relativeTimeFormat('xx-XX', formatOptions);
|
|
86
|
-
expect(Intl.RelativeTimeFormat).toHaveBeenCalledWith(
|
|
87
|
-
'xx-XX',
|
|
88
|
-
formatOptions
|
|
89
|
-
);
|
|
90
|
-
});
|
|
91
|
-
it('The locale is optional and will defer to `determineDisplayLocale`', () => {
|
|
92
|
-
document.body.setAttribute('lang', 'xx-XX');
|
|
93
|
-
relativeTimeFormat(formatOptions);
|
|
94
|
-
expect(Intl.RelativeTimeFormat).toHaveBeenCalledWith(
|
|
95
|
-
'xx-XX',
|
|
96
|
-
formatOptions
|
|
97
|
-
);
|
|
98
|
-
});
|
|
99
|
-
it('Maintains optional format options', () => {
|
|
100
|
-
relativeTimeFormat('en-US');
|
|
101
|
-
expect(Intl.RelativeTimeFormat).toHaveBeenCalledWith('en-US', undefined);
|
|
102
|
-
});
|
|
103
|
-
});
|
|
104
|
-
});
|
package/test/loading.spec.ts
DELETED
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
checkAndLoadFonts,
|
|
3
|
-
checkAndLoadScript,
|
|
4
|
-
checkAndLoadStyle
|
|
5
|
-
} from '../src/loading';
|
|
6
|
-
|
|
7
|
-
const SCRIPT_URL = 'https://localhost/script.js';
|
|
8
|
-
const STYLE_URL = 'https://localhost/style.css';
|
|
9
|
-
const FONTS = {
|
|
10
|
-
Font1: `font1.css`,
|
|
11
|
-
Font2: `font2.css`
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
describe('The loading module', () => {
|
|
15
|
-
afterEach(() => {
|
|
16
|
-
document.head.innerHTML = '';
|
|
17
|
-
document.body.innerHTML = '';
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
describe('checkAndLoadScript', () => {
|
|
21
|
-
test('will create a new script tag and wait for it to load', async () => {
|
|
22
|
-
const promise = checkAndLoadScript(SCRIPT_URL);
|
|
23
|
-
const tag = document.querySelector(`script[src="${SCRIPT_URL}"]`);
|
|
24
|
-
|
|
25
|
-
expect(tag).not.toBeNull(); // The tag was created
|
|
26
|
-
tag?.dispatchEvent(new Event('load')); // The promise will resolve
|
|
27
|
-
|
|
28
|
-
await expect(promise).resolves.toBe(undefined);
|
|
29
|
-
});
|
|
30
|
-
test('will fail if the script fails to load', async () => {
|
|
31
|
-
const promise = checkAndLoadScript(SCRIPT_URL);
|
|
32
|
-
const tag = document.querySelector(`script[src="${SCRIPT_URL}"]`);
|
|
33
|
-
|
|
34
|
-
expect(tag).not.toBeNull(); // The tag was created
|
|
35
|
-
tag?.dispatchEvent(new Event('error')); // The promise will reject
|
|
36
|
-
|
|
37
|
-
await expect(promise).rejects.toContain(SCRIPT_URL);
|
|
38
|
-
});
|
|
39
|
-
test('will not create a tag if one already exists', async () => {
|
|
40
|
-
const script = document.createElement('script');
|
|
41
|
-
script.setAttribute('src', SCRIPT_URL);
|
|
42
|
-
document.body.appendChild(script);
|
|
43
|
-
|
|
44
|
-
await checkAndLoadScript(SCRIPT_URL); // Should resolve immediately
|
|
45
|
-
const tags = document.querySelectorAll(`script[src="${SCRIPT_URL}"]`);
|
|
46
|
-
expect(tags.length).toBe(1); // A second tag was not created
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
describe('checkAndLoadStyle', () => {
|
|
50
|
-
test('will create a new link tag and wait for it to load', async () => {
|
|
51
|
-
const promise = checkAndLoadStyle(STYLE_URL);
|
|
52
|
-
const tag = document.querySelector(
|
|
53
|
-
`link[href="${STYLE_URL}"][rel="stylesheet"]`
|
|
54
|
-
);
|
|
55
|
-
|
|
56
|
-
expect(tag).not.toBeNull(); // The tag was created
|
|
57
|
-
tag?.dispatchEvent(new Event('load')); // The promise will resolve
|
|
58
|
-
|
|
59
|
-
await expect(promise).resolves.toBe(undefined);
|
|
60
|
-
});
|
|
61
|
-
test('will fail if the style fails to load', async () => {
|
|
62
|
-
const promise = checkAndLoadStyle(STYLE_URL);
|
|
63
|
-
const tag = document.querySelector(
|
|
64
|
-
`link[href="${STYLE_URL}"][rel="stylesheet"]`
|
|
65
|
-
);
|
|
66
|
-
|
|
67
|
-
expect(tag).not.toBeNull(); // The tag was created
|
|
68
|
-
tag?.dispatchEvent(new Event('error')); // The promise will reject
|
|
69
|
-
|
|
70
|
-
await expect(promise).rejects.toContain(STYLE_URL);
|
|
71
|
-
});
|
|
72
|
-
test('will not create a tag if one already exists', async () => {
|
|
73
|
-
const link = document.createElement('link');
|
|
74
|
-
link.setAttribute('href', STYLE_URL);
|
|
75
|
-
link.setAttribute('rel', 'stylesheet');
|
|
76
|
-
document.body.appendChild(link);
|
|
77
|
-
|
|
78
|
-
await checkAndLoadStyle(STYLE_URL); // Should resolve immediately
|
|
79
|
-
const tags = document.querySelectorAll(`link[href="${STYLE_URL}"]`);
|
|
80
|
-
expect(tags.length).toBe(1); // A second tag was not created
|
|
81
|
-
});
|
|
82
|
-
});
|
|
83
|
-
describe('checkAndLoadFonts', () => {
|
|
84
|
-
let documentFonts: Array<any> = [];
|
|
85
|
-
|
|
86
|
-
beforeEach(() => {
|
|
87
|
-
documentFonts = [];
|
|
88
|
-
//@ts-ignore - needed to be able to stub out font API
|
|
89
|
-
document.fonts = documentFonts;
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
test('Will load missing fonts', async () => {
|
|
93
|
-
const promise = checkAndLoadFonts(FONTS);
|
|
94
|
-
const tag1 = document.querySelector(`link[href="font1.css"]`);
|
|
95
|
-
const tag2 = document.querySelector(`link[href="font2.css"]`);
|
|
96
|
-
|
|
97
|
-
expect(tag1).not.toBeNull();
|
|
98
|
-
expect(tag2).not.toBeNull();
|
|
99
|
-
|
|
100
|
-
tag1?.dispatchEvent(new Event('load'));
|
|
101
|
-
tag2?.dispatchEvent(new Event('load'));
|
|
102
|
-
|
|
103
|
-
await expect(promise).resolves.toBe(undefined);
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
test('Will not load fonts that are already loaded', async () => {
|
|
107
|
-
documentFonts.push({ family: 'Font2' });
|
|
108
|
-
|
|
109
|
-
const promise = checkAndLoadFonts(FONTS);
|
|
110
|
-
const tag1 = document.querySelector(`link[href="font1.css"]`);
|
|
111
|
-
const tag2 = document.querySelector(`link[href="font2.css"]`);
|
|
112
|
-
|
|
113
|
-
expect(tag1).not.toBeNull();
|
|
114
|
-
expect(tag2).toBeNull();
|
|
115
|
-
|
|
116
|
-
tag1?.dispatchEvent(new Event('load'));
|
|
117
|
-
|
|
118
|
-
await expect(promise).resolves.toBe(undefined);
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
test('Ignores quotes on the family when checking for loaded fonts', async () => {
|
|
122
|
-
documentFonts.push({ family: '"Font2"' });
|
|
123
|
-
|
|
124
|
-
const promise = checkAndLoadFonts(FONTS);
|
|
125
|
-
const tag1 = document.querySelector(`link[href="font1.css"]`);
|
|
126
|
-
const tag2 = document.querySelector(`link[href="font2.css"]`);
|
|
127
|
-
|
|
128
|
-
expect(tag1).not.toBeNull();
|
|
129
|
-
expect(tag2).toBeNull();
|
|
130
|
-
|
|
131
|
-
tag1?.dispatchEvent(new Event('load'));
|
|
132
|
-
|
|
133
|
-
await expect(promise).resolves.toBe(undefined);
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
test('Still resolves even if fonts fail to load', async () => {
|
|
137
|
-
const promise = checkAndLoadFonts(FONTS);
|
|
138
|
-
const tag1 = document.querySelector(`link[href="font1.css"]`);
|
|
139
|
-
const tag2 = document.querySelector(`link[href="font2.css"]`);
|
|
140
|
-
|
|
141
|
-
expect(tag1).not.toBeNull();
|
|
142
|
-
expect(tag2).not.toBeNull();
|
|
143
|
-
|
|
144
|
-
tag1?.dispatchEvent(new Event('error'));
|
|
145
|
-
tag2?.dispatchEvent(new Event('error'));
|
|
146
|
-
|
|
147
|
-
await expect(promise).resolves.toBe(undefined);
|
|
148
|
-
});
|
|
149
|
-
});
|
|
150
|
-
});
|