testaro 18.2.0 → 18.4.0
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/aslint/utils/aria.test.ts +12 -0
- package/aslint/utils/aria.ts +120 -0
- package/aslint/utils/async.test.ts +25 -0
- package/aslint/utils/async.ts +22 -0
- package/aslint/utils/common.test.ts +239 -0
- package/aslint/utils/common.ts +168 -0
- package/aslint/utils/console.test.ts +85 -0
- package/aslint/utils/console.ts +89 -0
- package/aslint/utils/css.test.ts +153 -0
- package/aslint/utils/css.ts +191 -0
- package/aslint/utils/dom.test.ts +627 -0
- package/aslint/utils/dom.ts +1051 -0
- package/aslint/utils/env.test.ts +14 -0
- package/aslint/utils/env.ts +8 -0
- package/aslint/utils/func.test.ts +160 -0
- package/aslint/utils/func.ts +70 -0
- package/aslint/utils/global.test.ts +12 -0
- package/aslint/utils/global.ts +25 -0
- package/aslint/utils/object.test.ts +524 -0
- package/aslint/utils/object.ts +278 -0
- package/aslint/utils/report.test.ts +56 -0
- package/aslint/utils/report.ts +36 -0
- package/aslint/utils/text.test.ts +270 -0
- package/aslint/utils/text.ts +165 -0
- package/aslint/utils/time.test.ts +43 -0
- package/aslint/utils/time.ts +33 -0
- package/package.json +1 -1
- package/procs/getSource.js +35 -0
- package/testaro/dupAtt.js +86 -108
- package/testaro/headEl.js +83 -0
- package/testaro/hrRisk.js +67 -0
- package/tests/testaro.js +2 -0
- package/validation/tests/jobs/headEl.json +67 -0
- package/validation/tests/jobs/hrRisk.json +134 -0
- package/validation/tests/targets/headEl/index.html +16 -0
- package/validation/tests/targets/hrRisk/index.html +18 -0
- /package/aslint/app/rules/aslint/{aria-role-dialog → redundant/aria-role-dialog}/aria-role-dialog.documentation.md +0 -0
- /package/aslint/app/rules/aslint/{aria-role-dialog → redundant/aria-role-dialog}/aria-role-dialog.test.ts +0 -0
- /package/aslint/app/rules/aslint/{aria-role-dialog → redundant/aria-role-dialog}/aria-role-dialog.ts +0 -0
- /package/aslint/app/rules/aslint/{capital-letters-words → redundant/capital-letters-words}/capital-letters-words.documentation.md +0 -0
- /package/aslint/app/rules/aslint/{capital-letters-words → redundant/capital-letters-words}/capital-letters-words.test.ts +0 -0
- /package/aslint/app/rules/aslint/{capital-letters-words → redundant/capital-letters-words}/capital-letters-words.ts +0 -0
- /package/aslint/app/rules/aslint/{contentinfo-landmark-only-one → redundant/contentinfo-landmark-only-one}/contentinfo-landmark-only-one.documentation.md +0 -0
- /package/aslint/app/rules/aslint/{contentinfo-landmark-only-one → redundant/contentinfo-landmark-only-one}/contentinfo-landmark-only-one.test.ts +0 -0
- /package/aslint/app/rules/aslint/{contentinfo-landmark-only-one → redundant/contentinfo-landmark-only-one}/contentinfo-landmark-only-one.ts +0 -0
- /package/aslint/app/rules/aslint/{empty-title-attribute → redundant/empty-title-attribute}/empty-title-attribute.documentation.md +0 -0
- /package/aslint/app/rules/aslint/{empty-title-attribute → redundant/empty-title-attribute}/empty-title-attribute.test.ts +0 -0
- /package/aslint/app/rules/aslint/{empty-title-attribute → redundant/empty-title-attribute}/empty-title-attribute.ts +0 -0
- /package/aslint/app/rules/aslint/{flash-content → redundant/flash-content}/flash-content.documentation.md +0 -0
- /package/aslint/app/rules/aslint/{flash-content → redundant/flash-content}/flash-content.test.ts +0 -0
- /package/aslint/app/rules/aslint/{flash-content → redundant/flash-content}/flash-content.ts +0 -0
- /package/aslint/app/rules/aslint/{font-style-italic → redundant/font-style-italic}/font-style-italic.documentation.md +0 -0
- /package/aslint/app/rules/aslint/{font-style-italic → redundant/font-style-italic}/font-style-italic.test.ts +0 -0
- /package/aslint/app/rules/aslint/{font-style-italic → redundant/font-style-italic}/font-style-italic.ts +0 -0
- /package/aslint/app/rules/aslint/{h1-must-be → redundant/h1-must-be}/h1-must-be.documentation.md +0 -0
- /package/aslint/app/rules/aslint/{h1-must-be → redundant/h1-must-be}/h1-must-be.test.ts +0 -0
- /package/aslint/app/rules/aslint/{h1-must-be → redundant/h1-must-be}/h1-must-be.ts +0 -0
- /package/aslint/app/rules/aslint/{aria-hidden-false → unimportant/aria-hidden-false}/aria-hidden-false.test.ts +0 -0
- /package/aslint/app/rules/aslint/{aria-hidden-false → unimportant/aria-hidden-false}/aria-hidden-false.ts +0 -0
- /package/aslint/app/rules/aslint/{aria-hidden-false → unimportant/aria-hidden-false}/aria-hidden.documentation.md +0 -0
- /package/aslint/app/rules/aslint/{content-editable-missing-attributes → unimportant/content-editable-missing-attributes}/content-editable-missing-attributes.docmentation.md +0 -0
- /package/aslint/app/rules/aslint/{content-editable-missing-attributes → unimportant/content-editable-missing-attributes}/content-editable-missing-attributes.test.ts +0 -0
- /package/aslint/app/rules/aslint/{content-editable-missing-attributes → unimportant/content-editable-missing-attributes}/content-editable-missing-attributes.ts +0 -0
- /package/aslint/app/rules/aslint/{elements-not-allowed-in-head → used/elements-not-allowed-in-head}/elements-not-allowed-in-head.documentation.md +0 -0
- /package/aslint/app/rules/aslint/{elements-not-allowed-in-head → used/elements-not-allowed-in-head}/elements-not-allowed-in-head.test.ts +0 -0
- /package/aslint/app/rules/aslint/{elements-not-allowed-in-head → used/elements-not-allowed-in-head}/elements-not-allowed-in-head.ts +0 -0
- /package/aslint/app/rules/aslint/{headings-sibling-unique → used/headings-sibling-unique}/headings-sibling-unique.documentation.md +0 -0
- /package/aslint/app/rules/aslint/{headings-sibling-unique → used/headings-sibling-unique}/headings-sibling-unique.test.ts +0 -0
- /package/aslint/app/rules/aslint/{headings-sibling-unique → used/headings-sibling-unique}/headings-sibling-unique.ts +0 -0
- /package/aslint/app/rules/aslint/{horizontal-rule → used/horizontal-rule}/horizontal-rule.documentation.md +0 -0
- /package/aslint/app/rules/aslint/{horizontal-rule → used/horizontal-rule}/horizontal-rule.test.ts +0 -0
- /package/aslint/app/rules/aslint/{horizontal-rule → used/horizontal-rule}/horizontal-rule.ts +0 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { ROLES, TAG_TO_IMPLICIT_SEMANTIC_INFO } from '../constants/aria';
|
|
2
|
+
import { IElementRoles } from '../interfaces/aria.interface';
|
|
3
|
+
import { CommonUtility } from './common';
|
|
4
|
+
import { ObjectUtility } from './object';
|
|
5
|
+
|
|
6
|
+
export class Aria {
|
|
7
|
+
|
|
8
|
+
public static getHtmlInfo(element: Element): any {
|
|
9
|
+
let htmlInfo: any;
|
|
10
|
+
let defaultInfo: any; // will contain the info with no specific selector if no others match;
|
|
11
|
+
|
|
12
|
+
if (ObjectUtility.isHtmlElement(element) === false) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (typeof element.tagName !== 'string') {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const tagName: string = element.tagName.toUpperCase();
|
|
21
|
+
const infos: string[] = TAG_TO_IMPLICIT_SEMANTIC_INFO[tagName as keyof typeof TAG_TO_IMPLICIT_SEMANTIC_INFO];
|
|
22
|
+
|
|
23
|
+
if (typeof infos === 'undefined') {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const len: number = infos.length;
|
|
28
|
+
|
|
29
|
+
defaultInfo = null; // will contain the info with no specific selector if no others match
|
|
30
|
+
|
|
31
|
+
for (let i: number = 0; i < len; i += 1) {
|
|
32
|
+
htmlInfo = infos[i];
|
|
33
|
+
|
|
34
|
+
if (htmlInfo.selector) {
|
|
35
|
+
if (element.matches(htmlInfo.selector)) {
|
|
36
|
+
return htmlInfo;
|
|
37
|
+
}
|
|
38
|
+
} else {
|
|
39
|
+
defaultInfo = htmlInfo;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return defaultInfo;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* @param {Element} element
|
|
48
|
+
* @return {string} role
|
|
49
|
+
*/
|
|
50
|
+
|
|
51
|
+
public static getImplicitRole(element: Element): string {
|
|
52
|
+
const htmlInfo: any = Aria.getHtmlInfo(element);
|
|
53
|
+
|
|
54
|
+
return htmlInfo ? htmlInfo.role : '';
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
*
|
|
59
|
+
* Gets role details from an element.
|
|
60
|
+
* @param {Element} element The DOM element whose role we want.
|
|
61
|
+
* @param {boolean=} implicit if true then implicit semantics will be considered if there is no role attribute.
|
|
62
|
+
*
|
|
63
|
+
* @return {Object}
|
|
64
|
+
*/
|
|
65
|
+
public static getRoles(element: Element, implicit?: boolean | undefined): IElementRoles | null {
|
|
66
|
+
let roleValue: string | null;
|
|
67
|
+
let role: any;
|
|
68
|
+
let ariaRole: any;
|
|
69
|
+
let roleObject: any;
|
|
70
|
+
|
|
71
|
+
if (typeof element === 'undefined' || element.nodeType !== Node.ELEMENT_NODE || element.hasAttribute('role') === false && (typeof implicit === 'boolean' && implicit === false)) {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
roleValue = element.getAttribute('role');
|
|
76
|
+
|
|
77
|
+
if (roleValue === null && implicit) {
|
|
78
|
+
roleValue = Aria.getImplicitRole(element);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (typeof roleValue === 'string' && roleValue.length === 0) { // role='' or implicit role came up empty
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const roleNames: string[] = roleValue!.split(' ');
|
|
86
|
+
|
|
87
|
+
const result: IElementRoles = {
|
|
88
|
+
applied: {},
|
|
89
|
+
roles: [],
|
|
90
|
+
valid: false
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
for (let i: number = 0, len: number = roleNames.length; i < len; i += 1) {
|
|
94
|
+
role = roleNames[i];
|
|
95
|
+
ariaRole = ROLES[role];
|
|
96
|
+
|
|
97
|
+
roleObject = {
|
|
98
|
+
name: role
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
if (ariaRole && ariaRole.abstract === false) {
|
|
102
|
+
roleObject.details = ariaRole;
|
|
103
|
+
|
|
104
|
+
if (CommonUtility.hasKey(result, 'applied.name') === false) {
|
|
105
|
+
result.applied = roleObject;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
roleObject.valid = result.valid = true;
|
|
109
|
+
|
|
110
|
+
} else {
|
|
111
|
+
roleObject.valid = false;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
result.roles.push(roleObject);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return result;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Async } from './async';
|
|
2
|
+
|
|
3
|
+
describe('Utils', () => {
|
|
4
|
+
|
|
5
|
+
describe('Async', () => {
|
|
6
|
+
|
|
7
|
+
describe('#run', () => {
|
|
8
|
+
jest.useFakeTimers();
|
|
9
|
+
|
|
10
|
+
it('should call function after specified time', () => {
|
|
11
|
+
const timerCallback = jest.fn();
|
|
12
|
+
|
|
13
|
+
Async.run(timerCallback, 500);
|
|
14
|
+
|
|
15
|
+
expect(timerCallback).not.toHaveBeenCalled();
|
|
16
|
+
|
|
17
|
+
jest.advanceTimersByTime(501);
|
|
18
|
+
|
|
19
|
+
expect(timerCallback).toHaveBeenCalled();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
});
|
|
25
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export class Async {
|
|
2
|
+
private static DEFAULT_TIMEOUT: number = 10;
|
|
3
|
+
private static DEFAULT_INTERVAL: number = 10;
|
|
4
|
+
|
|
5
|
+
constructor() { }
|
|
6
|
+
|
|
7
|
+
public static run(fn: any, context?: any, timeout?: number): number {
|
|
8
|
+
const callBack: any = context ? fn.bind(context) : fn;
|
|
9
|
+
|
|
10
|
+
return window.setTimeout(callBack, timeout || Async.DEFAULT_TIMEOUT);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
public static interval(fn: any, context: any, intervalId: number): number {
|
|
14
|
+
const callBack: any = context ? fn.bind(context) : fn;
|
|
15
|
+
|
|
16
|
+
return window.setInterval(callBack, intervalId || Async.DEFAULT_INTERVAL);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
public static stopInterval(id: number): void {
|
|
20
|
+
window.clearInterval(id);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { CommonUtility } from './common';
|
|
2
|
+
|
|
3
|
+
describe('Common', () => {
|
|
4
|
+
it('should indicate that class exists', () => {
|
|
5
|
+
const sharedCommonUtility = new CommonUtility();
|
|
6
|
+
|
|
7
|
+
expect(sharedCommonUtility).toBeDefined();
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
describe('#hasKey', (): void => {
|
|
11
|
+
it('should indicate that specified object has defined key', (): void => {
|
|
12
|
+
let obj;
|
|
13
|
+
|
|
14
|
+
let result;
|
|
15
|
+
|
|
16
|
+
obj = {
|
|
17
|
+
test: {
|
|
18
|
+
example: {
|
|
19
|
+
p: 1
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
result = CommonUtility.hasKey(obj, 'test.example.p');
|
|
25
|
+
|
|
26
|
+
expect(result).toBe(true);
|
|
27
|
+
|
|
28
|
+
obj = {
|
|
29
|
+
'a.b': 1
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
result = CommonUtility.hasKey(obj, ['a.b']);
|
|
33
|
+
|
|
34
|
+
expect(result).toBe(true);
|
|
35
|
+
expect(obj['a.b']).toBe(1);
|
|
36
|
+
|
|
37
|
+
obj = {
|
|
38
|
+
'': 1
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
result = CommonUtility.hasKey(obj, ['']);
|
|
42
|
+
|
|
43
|
+
expect(result).toBe(true);
|
|
44
|
+
expect(obj['']).toBe(1);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('should indicate that specified object has no defined key', (): void => {
|
|
48
|
+
let obj = {};
|
|
49
|
+
let result = CommonUtility.hasKey(obj, 'test.example.p');
|
|
50
|
+
|
|
51
|
+
expect(result).toBe(false);
|
|
52
|
+
|
|
53
|
+
obj = null;
|
|
54
|
+
result = CommonUtility.hasKey(obj, 'test.example.p');
|
|
55
|
+
|
|
56
|
+
expect(result).toBe(false);
|
|
57
|
+
|
|
58
|
+
obj = [];
|
|
59
|
+
result = CommonUtility.hasKey(obj, 'test.example.p');
|
|
60
|
+
|
|
61
|
+
expect(result).toBe(false);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('should indicate that specified object has defined key and the value under that property exists', () => {
|
|
65
|
+
const obj = {
|
|
66
|
+
foo: {
|
|
67
|
+
bar: 'baz'
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const result = CommonUtility.hasKey(obj, 'foo.bar');
|
|
72
|
+
|
|
73
|
+
expect(result).toBe(true);
|
|
74
|
+
expect(obj.foo.bar).toBe('baz');
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
describe('#setKey', (): void => {
|
|
79
|
+
it('should create a property and set a value to an empty object', () => {
|
|
80
|
+
expect(CommonUtility.setKey({}, 'test.of.properties', null)).toEqual({
|
|
81
|
+
test: {
|
|
82
|
+
of: {
|
|
83
|
+
properties: null
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('should create a property and set a value to a non-empty object', () => {
|
|
90
|
+
expect(CommonUtility.setKey({
|
|
91
|
+
test: {}
|
|
92
|
+
}, 'test.of.properties', '6')).toEqual({
|
|
93
|
+
test: {
|
|
94
|
+
of: {
|
|
95
|
+
properties: '6'
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
describe('#pick', () => {
|
|
103
|
+
it('should pick values from object', () => {
|
|
104
|
+
const result: any = CommonUtility.pick(
|
|
105
|
+
{
|
|
106
|
+
ignoreThat: 'ignore',
|
|
107
|
+
object: {
|
|
108
|
+
shouldIgnoreThis: 'ignored',
|
|
109
|
+
some: 'value'
|
|
110
|
+
},
|
|
111
|
+
rootValue: 'root'
|
|
112
|
+
},
|
|
113
|
+
'rootValue',
|
|
114
|
+
'object.some'
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
expect(result).toEqual({
|
|
118
|
+
object: {
|
|
119
|
+
some: 'value'
|
|
120
|
+
},
|
|
121
|
+
rootValue: 'root'
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
describe('#pickArray', () => {
|
|
127
|
+
it('should pick values from objects in array', () => {
|
|
128
|
+
const result: any[] = CommonUtility.pickArray(
|
|
129
|
+
[
|
|
130
|
+
{
|
|
131
|
+
ignoreThat: 'ignore',
|
|
132
|
+
object: {
|
|
133
|
+
shouldIgnoreThis: 'ignored',
|
|
134
|
+
some: 'value'
|
|
135
|
+
},
|
|
136
|
+
rootValue: 'root'
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
ignore2: 'ignore',
|
|
140
|
+
rootValue: 'root 2'
|
|
141
|
+
}
|
|
142
|
+
],
|
|
143
|
+
'rootValue',
|
|
144
|
+
'object.some'
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
expect(result).toEqual([{
|
|
148
|
+
object: {
|
|
149
|
+
some: 'value'
|
|
150
|
+
},
|
|
151
|
+
rootValue: 'root'
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
rootValue: 'root 2'
|
|
155
|
+
}
|
|
156
|
+
]);
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
describe('#arrayToMap', () => {
|
|
161
|
+
it('should convert array of objects to map by property', () => {
|
|
162
|
+
type ValueStringType = { value: string };
|
|
163
|
+
const values: ValueStringType[] = [{
|
|
164
|
+
value: '1'
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
value: '2'
|
|
168
|
+
}];
|
|
169
|
+
const result: Map<string, ValueStringType> = CommonUtility.arrayToMap(values, 'value');
|
|
170
|
+
|
|
171
|
+
expect(Array.from(result.entries())).toEqual([
|
|
172
|
+
['1', {
|
|
173
|
+
value: '1'
|
|
174
|
+
}],
|
|
175
|
+
['2', {
|
|
176
|
+
value: '2'
|
|
177
|
+
}]
|
|
178
|
+
]);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it('should convert array of objects to map by method return value', () => {
|
|
182
|
+
type ValueMethodType = { value: () => number };
|
|
183
|
+
const values: ValueMethodType[] = [{
|
|
184
|
+
value: () => {
|
|
185
|
+
return 1;
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
value: () => {
|
|
190
|
+
return 2;
|
|
191
|
+
}
|
|
192
|
+
}];
|
|
193
|
+
const result: Map<number, ValueMethodType> = CommonUtility.arrayToMap(values, 'value');
|
|
194
|
+
|
|
195
|
+
expect(Array.from(result.entries())).toEqual([
|
|
196
|
+
[1, values[0]],
|
|
197
|
+
[2, values[1]]
|
|
198
|
+
]);
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
describe('#isNativeMethod', () => {
|
|
203
|
+
|
|
204
|
+
it('should determine that function method is a native, built-in method', () => {
|
|
205
|
+
expect(CommonUtility.isNativeMethod(String.prototype.trim)).toBeTruthy();
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
it('should determine that tested method is not a native, built-in method', () => {
|
|
209
|
+
const s: string = 'test';
|
|
210
|
+
|
|
211
|
+
expect(CommonUtility.isNativeMethod(s)).toBeFalsy();
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it('should determine that object method is not a native, built-in method', () => {
|
|
215
|
+
const obj: object = {
|
|
216
|
+
a: 1,
|
|
217
|
+
b: 2
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
expect(CommonUtility.isNativeMethod(obj)).toBeFalsy();
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
describe('#isEventSupported', () => {
|
|
226
|
+
it('should indicate than "click" event is supported', () => {
|
|
227
|
+
const isClickEventSupported = CommonUtility.isEventSupported('click');
|
|
228
|
+
|
|
229
|
+
expect(isClickEventSupported).toBe(true);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it('should indicate than "foo" event is not supported', () => {
|
|
233
|
+
const isClickEventSupported = CommonUtility.isEventSupported('foo');
|
|
234
|
+
|
|
235
|
+
expect(isClickEventSupported).toBe(false);
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
});
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { ObjectUtility } from './object';
|
|
2
|
+
|
|
3
|
+
type TypeOfKey<Type, Key extends keyof Type> = Type[Key] extends (...args: any[]) => any ? ReturnType<Type[Key]> : Type[Key];
|
|
4
|
+
|
|
5
|
+
export class CommonUtility {
|
|
6
|
+
|
|
7
|
+
public static hasKey(obj: Object, key: string | string[]): boolean {
|
|
8
|
+
const keys: string[] = Array.isArray(key) ? key : key.split('.');
|
|
9
|
+
const len: number = keys.length;
|
|
10
|
+
let o: Object = obj;
|
|
11
|
+
let result: boolean = true;
|
|
12
|
+
|
|
13
|
+
if (o === null) {
|
|
14
|
+
result = false;
|
|
15
|
+
|
|
16
|
+
return result;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
for (let i: number = 0; i < len; i += 1) {
|
|
20
|
+
if (typeof o !== 'object' || o === null || Object.prototype.hasOwnProperty.call(o, keys[i]) === false) {
|
|
21
|
+
result = false;
|
|
22
|
+
break;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
o = o[keys[i] as keyof typeof o];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return result;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public static setKey(obj: Object, path: string, value: any): Object {
|
|
32
|
+
const pList: string[] = path.split('.');
|
|
33
|
+
const key: string | undefined = pList.pop();
|
|
34
|
+
|
|
35
|
+
const createPropertyWithValue = (accumulator: Object, currentValue: string): Object => {
|
|
36
|
+
if (typeof accumulator[currentValue as keyof typeof accumulator] === 'undefined') {
|
|
37
|
+
Object.defineProperty(accumulator, currentValue, {
|
|
38
|
+
configurable: true,
|
|
39
|
+
enumerable: true,
|
|
40
|
+
value: {},
|
|
41
|
+
writable: true
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return accumulator[currentValue as keyof typeof accumulator];
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
if (typeof key === 'undefined') {
|
|
49
|
+
throw new Error('[Common.setKey] Invalid type of key. Should be stringm provided undefined');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const pointer: Object = pList.reduce(createPropertyWithValue, obj);
|
|
53
|
+
|
|
54
|
+
pointer[key as keyof typeof pointer] = value;
|
|
55
|
+
|
|
56
|
+
return obj;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
public static pick<T, K extends keyof T>(value: T, ...props: Array<K | string>): Pick<T, K> | any {
|
|
60
|
+
const result: Pick<T, K> = {} as any;
|
|
61
|
+
|
|
62
|
+
for (const prop of props) {
|
|
63
|
+
if (prop as any in value) {
|
|
64
|
+
result[prop as K] = value[prop as K];
|
|
65
|
+
} else {
|
|
66
|
+
const path: string = prop as string;
|
|
67
|
+
|
|
68
|
+
if (CommonUtility.hasKey(value, path)) {
|
|
69
|
+
const deepValue: any = path.split('.').reduce((prev: any, current: string) => {
|
|
70
|
+
return prev[current];
|
|
71
|
+
}, value);
|
|
72
|
+
|
|
73
|
+
CommonUtility.setKey(result, path, deepValue);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return result;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
public static pickArray<T, K extends keyof T>(values: T[], ...props: Array<K | string>): Array<Pick<T, K> | any> {
|
|
82
|
+
return values.map((value: T) => {
|
|
83
|
+
return this.pick(value, ...props);
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
public static arrayToMap<T, K extends keyof T>(objects: T[], key: K): Map<TypeOfKey<T, K>, T> {
|
|
88
|
+
const getKey = (object: T): TypeOfKey<T, K> => {
|
|
89
|
+
return typeof object[key] === 'function' ? ((object[key] as unknown) as Function).call(object) : object[key];
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
return new Map<TypeOfKey<T, K>, T>(objects.map((object: T) => {
|
|
93
|
+
return [getKey(object), object];
|
|
94
|
+
}));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
public static isHtmlDocument(el: any): boolean {
|
|
98
|
+
return Boolean(el) && el.nodeType === Node.DOCUMENT_NODE;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
public static isNativeMethod(methodName: any): boolean {
|
|
102
|
+
const regEx: RegExp = new RegExp('^(function|object)$', 'i');
|
|
103
|
+
const argumentType: string = typeof methodName;
|
|
104
|
+
let str: string;
|
|
105
|
+
|
|
106
|
+
if (regEx.test(argumentType)) {
|
|
107
|
+
// Returns a string representing the object and check for [native code] string
|
|
108
|
+
str = String(methodName);
|
|
109
|
+
|
|
110
|
+
return str.indexOf('[native code]') !== -1;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
public static randomRange(minVal: number = 0, max: number = 1000000): number {
|
|
117
|
+
const min: number = Math.ceil(minVal);
|
|
118
|
+
|
|
119
|
+
return Math.floor(Math.random() * (Math.floor(max) - min)) + min;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
public static isEventSupported(event: string, htmlElement?: HTMLElement): boolean {
|
|
123
|
+
const TAGNAMES: { [key: string]: string } = {
|
|
124
|
+
abort: 'img',
|
|
125
|
+
change: 'input',
|
|
126
|
+
error: 'img',
|
|
127
|
+
load: 'img',
|
|
128
|
+
reset: 'form',
|
|
129
|
+
select: 'input',
|
|
130
|
+
submit: 'form'
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
let element: HTMLElement = htmlElement || document.createElement(TAGNAMES[event] || 'div');
|
|
134
|
+
const eventName: string = `on${event}`;
|
|
135
|
+
|
|
136
|
+
// When using `setAttribute`, IE skips "unload", WebKit skips "unload" and "resize", whereas `in` "catches" those
|
|
137
|
+
let isSupported: boolean = eventName in document;
|
|
138
|
+
|
|
139
|
+
if (isSupported === false) {
|
|
140
|
+
if (ObjectUtility.isHostMethod(element, 'setAttribute') === false) {
|
|
141
|
+
element = document.createElement('div');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (ObjectUtility.isHostMethod(element, 'setAttribute') && ObjectUtility.isHostMethod(element, 'removeAttribute')) {
|
|
145
|
+
element.setAttribute(eventName, '');
|
|
146
|
+
isSupported = typeof element[eventName as keyof HTMLElement] === 'function';
|
|
147
|
+
|
|
148
|
+
if (typeof element[eventName as keyof HTMLElement] !== 'undefined') {
|
|
149
|
+
const descriptor: PropertyDescriptor | undefined = Object.getOwnPropertyDescriptor(element, eventName);
|
|
150
|
+
|
|
151
|
+
if (descriptor?.writable) {
|
|
152
|
+
Object.defineProperty(element, eventName, {
|
|
153
|
+
configurable: true,
|
|
154
|
+
enumerable: true,
|
|
155
|
+
value: undefined,
|
|
156
|
+
writable: true
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
element.removeAttribute(eventName);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return isSupported;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { Console } from './console';
|
|
2
|
+
import { Global } from './global';
|
|
3
|
+
|
|
4
|
+
describe('Utils', () => {
|
|
5
|
+
|
|
6
|
+
describe('Console', () => {
|
|
7
|
+
|
|
8
|
+
let consoleObject: any, global: any;
|
|
9
|
+
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
global = {
|
|
12
|
+
console: {}
|
|
13
|
+
};
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
afterEach(() => {
|
|
17
|
+
consoleObject = undefined;
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('assigns global object when not provided in argument of console init', () => {
|
|
21
|
+
consoleObject = Console.init();
|
|
22
|
+
expect(consoleObject).toBe(Global.context.console);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('checks, if custom method are applied (replaced native method)', () => {
|
|
26
|
+
consoleObject = Console.init(global);
|
|
27
|
+
|
|
28
|
+
Object.keys(consoleObject.config).forEach((key) => {
|
|
29
|
+
expect(typeof consoleObject[key]).toBe('function');
|
|
30
|
+
expect(consoleObject[key].toString().indexOf('[native code]')).toBeLessThan(0);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('polyfill console.count when it does not exists', () => {
|
|
35
|
+
|
|
36
|
+
let consoleCountData: { test: any };
|
|
37
|
+
|
|
38
|
+
consoleObject = Console.init({});
|
|
39
|
+
consoleCountData = consoleObject.count('test');
|
|
40
|
+
|
|
41
|
+
expect(consoleObject.count).toBeDefined();
|
|
42
|
+
expect(consoleCountData.test).toBe(1);
|
|
43
|
+
|
|
44
|
+
consoleCountData = consoleObject.count('test');
|
|
45
|
+
expect(consoleCountData.test).toBe(2);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('polyfill console.countReset should be created when it does not exists', () => {
|
|
49
|
+
consoleObject = Console.init({});
|
|
50
|
+
consoleObject.count('test');
|
|
51
|
+
|
|
52
|
+
const consoleCountData = consoleObject.countReset('test');
|
|
53
|
+
|
|
54
|
+
expect(typeof consoleObject.countReset).toBe('function');
|
|
55
|
+
expect(consoleCountData.test).toBe(0);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('assign no-operation function when specified method does not exists in current console object', () => {
|
|
59
|
+
consoleObject = Console.init({});
|
|
60
|
+
|
|
61
|
+
expect(typeof consoleObject.assert).toBe('function');
|
|
62
|
+
expect(typeof consoleObject.clear).toBe('function');
|
|
63
|
+
expect(typeof consoleObject.count).toBe('function');
|
|
64
|
+
expect(typeof consoleObject.debug).toBe('function');
|
|
65
|
+
expect(typeof consoleObject.dir).toBe('function');
|
|
66
|
+
expect(typeof consoleObject.dirxml).toBe('function');
|
|
67
|
+
expect(typeof consoleObject.error).toBe('function');
|
|
68
|
+
expect(typeof consoleObject.group).toBe('function');
|
|
69
|
+
expect(typeof consoleObject.groupCollapsed).toBe('function');
|
|
70
|
+
expect(typeof consoleObject.groupEnd).toBe('function');
|
|
71
|
+
expect(typeof consoleObject.info).toBe('function');
|
|
72
|
+
expect(typeof consoleObject.log).toBe('function');
|
|
73
|
+
expect(typeof consoleObject.msIsIndependentlyComposed).toBe('function');
|
|
74
|
+
expect(typeof consoleObject.profile).toBe('function');
|
|
75
|
+
expect(typeof consoleObject.profileEnd).toBe('function');
|
|
76
|
+
expect(typeof consoleObject.select).toBe('function');
|
|
77
|
+
expect(typeof consoleObject.time).toBe('function');
|
|
78
|
+
expect(typeof consoleObject.timeEnd).toBe('function');
|
|
79
|
+
expect(typeof consoleObject.trace).toBe('function');
|
|
80
|
+
expect(typeof consoleObject.warn).toBe('function');
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
});
|