rlint 0.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/README.md +220 -0
- package/dist/browser.d.ts +8 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/browser.js +122 -0
- package/dist/browser.js.map +1 -0
- package/dist/checks/clickability.d.ts +3 -0
- package/dist/checks/clickability.d.ts.map +1 -0
- package/dist/checks/clickability.js +125 -0
- package/dist/checks/clickability.js.map +1 -0
- package/dist/checks/index.d.ts +9 -0
- package/dist/checks/index.d.ts.map +1 -0
- package/dist/checks/index.js +27 -0
- package/dist/checks/index.js.map +1 -0
- package/dist/checks/overflow.d.ts +3 -0
- package/dist/checks/overflow.d.ts.map +1 -0
- package/dist/checks/overflow.js +107 -0
- package/dist/checks/overflow.js.map +1 -0
- package/dist/checks/text-overflow.d.ts +3 -0
- package/dist/checks/text-overflow.d.ts.map +1 -0
- package/dist/checks/text-overflow.js +136 -0
- package/dist/checks/text-overflow.js.map +1 -0
- package/dist/checks/touch-targets.d.ts +3 -0
- package/dist/checks/touch-targets.d.ts.map +1 -0
- package/dist/checks/touch-targets.js +118 -0
- package/dist/checks/touch-targets.js.map +1 -0
- package/dist/checks/visibility.d.ts +3 -0
- package/dist/checks/visibility.d.ts.map +1 -0
- package/dist/checks/visibility.js +132 -0
- package/dist/checks/visibility.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +270 -0
- package/dist/cli.js.map +1 -0
- package/dist/frameworks/detector.d.ts +19 -0
- package/dist/frameworks/detector.d.ts.map +1 -0
- package/dist/frameworks/detector.js +132 -0
- package/dist/frameworks/detector.js.map +1 -0
- package/dist/frameworks/index.d.ts +44 -0
- package/dist/frameworks/index.d.ts.map +1 -0
- package/dist/frameworks/index.js +138 -0
- package/dist/frameworks/index.js.map +1 -0
- package/dist/frameworks/next.d.ts +34 -0
- package/dist/frameworks/next.d.ts.map +1 -0
- package/dist/frameworks/next.js +160 -0
- package/dist/frameworks/next.js.map +1 -0
- package/dist/frameworks/sveltekit.d.ts +34 -0
- package/dist/frameworks/sveltekit.d.ts.map +1 -0
- package/dist/frameworks/sveltekit.js +150 -0
- package/dist/frameworks/sveltekit.js.map +1 -0
- package/dist/frameworks/vite.d.ts +40 -0
- package/dist/frameworks/vite.d.ts.map +1 -0
- package/dist/frameworks/vite.js +211 -0
- package/dist/frameworks/vite.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp-server.d.ts +3 -0
- package/dist/mcp-server.d.ts.map +1 -0
- package/dist/mcp-server.js +402 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/reporter.d.ts +4 -0
- package/dist/reporter.d.ts.map +1 -0
- package/dist/reporter.js +103 -0
- package/dist/reporter.js.map +1 -0
- package/dist/runner.d.ts +8 -0
- package/dist/runner.d.ts.map +1 -0
- package/dist/runner.js +63 -0
- package/dist/runner.js.map +1 -0
- package/dist/types.d.ts +96 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +69 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"overflow.js","sourceRoot":"","sources":["../../src/checks/overflow.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text-overflow.d.ts","sourceRoot":"","sources":["../../src/checks/text-overflow.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAuB,MAAM,aAAa,CAAC;AAwB9D,eAAO,MAAM,iBAAiB,EAAE,KA6J/B,CAAC"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
export const textOverflowCheck = {
|
|
2
|
+
name: 'text-overflow',
|
|
3
|
+
description: 'Detects text that is clipped or overflowing its container',
|
|
4
|
+
async run(ctx) {
|
|
5
|
+
const { page, config } = ctx;
|
|
6
|
+
const checkConfig = typeof config.checks?.textOverflow === 'object'
|
|
7
|
+
? config.checks.textOverflow
|
|
8
|
+
: {};
|
|
9
|
+
const allowEllipsis = checkConfig.allowEllipsis ?? true;
|
|
10
|
+
const ignoreSelectors = [
|
|
11
|
+
...(config.ignore || []),
|
|
12
|
+
...(checkConfig.ignore || []),
|
|
13
|
+
'[data-rlint-ignore]',
|
|
14
|
+
'.truncate',
|
|
15
|
+
'[data-truncate]',
|
|
16
|
+
'.line-clamp',
|
|
17
|
+
];
|
|
18
|
+
const overflowingElements = await page.evaluate(({ ignoreSelectors, allowEllipsis }) => {
|
|
19
|
+
const results = [];
|
|
20
|
+
// Check common text elements
|
|
21
|
+
const textElements = document.querySelectorAll('p, span, h1, h2, h3, h4, h5, h6, label, li, td, th, div, a');
|
|
22
|
+
for (const el of textElements) {
|
|
23
|
+
// Skip ignored elements
|
|
24
|
+
const shouldIgnore = ignoreSelectors.some(sel => {
|
|
25
|
+
try {
|
|
26
|
+
return el.matches(sel);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
if (shouldIgnore)
|
|
33
|
+
continue;
|
|
34
|
+
// Skip hidden elements
|
|
35
|
+
const style = getComputedStyle(el);
|
|
36
|
+
if (style.display === 'none' || style.visibility === 'hidden') {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
// Skip elements without text content
|
|
40
|
+
if (!el.textContent?.trim())
|
|
41
|
+
continue;
|
|
42
|
+
// Check for overflow
|
|
43
|
+
const hasHorizontalOverflow = el.scrollWidth > el.clientWidth + 1;
|
|
44
|
+
const hasVerticalOverflow = el.scrollHeight > el.clientHeight + 1;
|
|
45
|
+
if (!hasHorizontalOverflow && !hasVerticalOverflow)
|
|
46
|
+
continue;
|
|
47
|
+
// Check if overflow is visible (intentional) or hidden (clipping)
|
|
48
|
+
const isClipping = style.overflow === 'hidden' ||
|
|
49
|
+
style.overflowX === 'hidden' ||
|
|
50
|
+
style.overflowY === 'hidden';
|
|
51
|
+
// Skip if overflow is set to scroll/auto (intentional scrolling)
|
|
52
|
+
const hasScroll = style.overflow === 'scroll' ||
|
|
53
|
+
style.overflow === 'auto' ||
|
|
54
|
+
style.overflowX === 'scroll' ||
|
|
55
|
+
style.overflowX === 'auto' ||
|
|
56
|
+
style.overflowY === 'scroll' ||
|
|
57
|
+
style.overflowY === 'auto';
|
|
58
|
+
if (hasScroll)
|
|
59
|
+
continue;
|
|
60
|
+
// Skip if it's intentional ellipsis
|
|
61
|
+
if (allowEllipsis && style.textOverflow === 'ellipsis') {
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
// Skip if it has -webkit-line-clamp (intentional truncation)
|
|
65
|
+
if (style.webkitLineClamp) {
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
// Only report if it's actually clipping content
|
|
69
|
+
if (isClipping || (!hasScroll && (hasHorizontalOverflow || hasVerticalOverflow))) {
|
|
70
|
+
const rect = el.getBoundingClientRect();
|
|
71
|
+
let selector = el.tagName.toLowerCase();
|
|
72
|
+
if (el.id)
|
|
73
|
+
selector += `#${el.id}`;
|
|
74
|
+
if (el.className && typeof el.className === 'string') {
|
|
75
|
+
selector += '.' + el.className.trim().split(/\s+/).join('.');
|
|
76
|
+
}
|
|
77
|
+
results.push({
|
|
78
|
+
selector,
|
|
79
|
+
tagName: el.tagName,
|
|
80
|
+
className: typeof el.className === 'string' ? el.className : '',
|
|
81
|
+
id: el.id || null,
|
|
82
|
+
textContent: el.textContent?.slice(0, 50)?.trim() || null,
|
|
83
|
+
rect: {
|
|
84
|
+
width: rect.width,
|
|
85
|
+
height: rect.height,
|
|
86
|
+
left: rect.left,
|
|
87
|
+
top: rect.top,
|
|
88
|
+
right: rect.right,
|
|
89
|
+
bottom: rect.bottom,
|
|
90
|
+
},
|
|
91
|
+
scrollWidth: el.scrollWidth,
|
|
92
|
+
clientWidth: el.clientWidth,
|
|
93
|
+
scrollHeight: el.scrollHeight,
|
|
94
|
+
clientHeight: el.clientHeight,
|
|
95
|
+
isHorizontal: hasHorizontalOverflow,
|
|
96
|
+
isVertical: hasVerticalOverflow,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return results.slice(0, 20); // Limit results
|
|
101
|
+
}, { ignoreSelectors, allowEllipsis });
|
|
102
|
+
const issues = overflowingElements.map(el => {
|
|
103
|
+
const direction = el.isHorizontal && el.isVertical
|
|
104
|
+
? 'both directions'
|
|
105
|
+
: el.isHorizontal
|
|
106
|
+
? 'horizontally'
|
|
107
|
+
: 'vertically';
|
|
108
|
+
return {
|
|
109
|
+
check: 'text-overflow',
|
|
110
|
+
severity: 'warning',
|
|
111
|
+
message: `Text overflows container ${direction}`,
|
|
112
|
+
element: {
|
|
113
|
+
selector: el.selector,
|
|
114
|
+
tagName: el.tagName,
|
|
115
|
+
className: el.className,
|
|
116
|
+
id: el.id,
|
|
117
|
+
textContent: el.textContent,
|
|
118
|
+
rect: el.rect,
|
|
119
|
+
},
|
|
120
|
+
details: {
|
|
121
|
+
scrollWidth: el.scrollWidth,
|
|
122
|
+
clientWidth: el.clientWidth,
|
|
123
|
+
scrollHeight: el.scrollHeight,
|
|
124
|
+
clientHeight: el.clientHeight,
|
|
125
|
+
},
|
|
126
|
+
fixHint: `Add overflow: auto for scrolling, text-overflow: ellipsis for truncation, or increase container size`,
|
|
127
|
+
};
|
|
128
|
+
});
|
|
129
|
+
return {
|
|
130
|
+
check: 'text-overflow',
|
|
131
|
+
passed: 0, // Can't easily count "passed" for this check
|
|
132
|
+
issues,
|
|
133
|
+
};
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
//# sourceMappingURL=text-overflow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text-overflow.js","sourceRoot":"","sources":["../../src/checks/text-overflow.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"touch-targets.d.ts","sourceRoot":"","sources":["../../src/checks/touch-targets.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAuB,MAAM,aAAa,CAAC;AAoB9D,eAAO,MAAM,iBAAiB,EAAE,KAsI/B,CAAC"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
export const touchTargetsCheck = {
|
|
2
|
+
name: 'touch-targets',
|
|
3
|
+
description: 'Ensures interactive elements meet minimum touch target size (44x44px)',
|
|
4
|
+
async run(ctx) {
|
|
5
|
+
const { page, config } = ctx;
|
|
6
|
+
const checkConfig = typeof config.checks?.touchTargets === 'object'
|
|
7
|
+
? config.checks.touchTargets
|
|
8
|
+
: {};
|
|
9
|
+
const minWidth = checkConfig.minWidth ?? 44;
|
|
10
|
+
const minHeight = checkConfig.minHeight ?? 44;
|
|
11
|
+
const selectors = checkConfig.selectors || [
|
|
12
|
+
'button',
|
|
13
|
+
'a',
|
|
14
|
+
'input:not([type="hidden"])',
|
|
15
|
+
'select',
|
|
16
|
+
'textarea',
|
|
17
|
+
'[role="button"]',
|
|
18
|
+
'[role="link"]',
|
|
19
|
+
'[role="checkbox"]',
|
|
20
|
+
'[role="radio"]',
|
|
21
|
+
'[tabindex="0"]',
|
|
22
|
+
];
|
|
23
|
+
const ignoreSelectors = [
|
|
24
|
+
...(config.ignore || []),
|
|
25
|
+
...(checkConfig.ignore || []),
|
|
26
|
+
'[data-rlint-ignore]',
|
|
27
|
+
];
|
|
28
|
+
const smallElements = await page.evaluate(({ selectors, ignoreSelectors, minWidth, minHeight }) => {
|
|
29
|
+
const results = [];
|
|
30
|
+
const selectorString = selectors.join(', ');
|
|
31
|
+
const elements = document.querySelectorAll(selectorString);
|
|
32
|
+
for (const el of elements) {
|
|
33
|
+
// Skip ignored elements
|
|
34
|
+
const shouldIgnore = ignoreSelectors.some(sel => {
|
|
35
|
+
try {
|
|
36
|
+
return el.matches(sel);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
if (shouldIgnore)
|
|
43
|
+
continue;
|
|
44
|
+
// Skip hidden elements
|
|
45
|
+
const style = getComputedStyle(el);
|
|
46
|
+
if (style.display === 'none' ||
|
|
47
|
+
style.visibility === 'hidden' ||
|
|
48
|
+
style.opacity === '0') {
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
const rect = el.getBoundingClientRect();
|
|
52
|
+
// Skip zero-size elements (likely hidden)
|
|
53
|
+
if (rect.width === 0 && rect.height === 0)
|
|
54
|
+
continue;
|
|
55
|
+
// Skip off-screen elements
|
|
56
|
+
if (rect.right < 0 ||
|
|
57
|
+
rect.bottom < 0 ||
|
|
58
|
+
rect.left > window.innerWidth ||
|
|
59
|
+
rect.top > window.innerHeight) {
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
// Check if too small
|
|
63
|
+
if (rect.width < minWidth || rect.height < minHeight) {
|
|
64
|
+
let selector = el.tagName.toLowerCase();
|
|
65
|
+
if (el.id)
|
|
66
|
+
selector += `#${el.id}`;
|
|
67
|
+
if (el.className && typeof el.className === 'string') {
|
|
68
|
+
selector += '.' + el.className.trim().split(/\s+/).join('.');
|
|
69
|
+
}
|
|
70
|
+
results.push({
|
|
71
|
+
selector,
|
|
72
|
+
tagName: el.tagName,
|
|
73
|
+
className: typeof el.className === 'string' ? el.className : '',
|
|
74
|
+
id: el.id || null,
|
|
75
|
+
textContent: el.textContent?.slice(0, 30)?.trim() || null,
|
|
76
|
+
rect: {
|
|
77
|
+
width: rect.width,
|
|
78
|
+
height: rect.height,
|
|
79
|
+
left: rect.left,
|
|
80
|
+
top: rect.top,
|
|
81
|
+
right: rect.right,
|
|
82
|
+
bottom: rect.bottom,
|
|
83
|
+
},
|
|
84
|
+
actualWidth: rect.width,
|
|
85
|
+
actualHeight: rect.height,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return results;
|
|
90
|
+
}, { selectors, ignoreSelectors, minWidth, minHeight });
|
|
91
|
+
const issues = smallElements.map(el => ({
|
|
92
|
+
check: 'touch-targets',
|
|
93
|
+
severity: 'warning',
|
|
94
|
+
message: `Touch target too small: ${Math.round(el.actualWidth)}x${Math.round(el.actualHeight)}px (min: ${minWidth}x${minHeight}px)`,
|
|
95
|
+
element: {
|
|
96
|
+
selector: el.selector,
|
|
97
|
+
tagName: el.tagName,
|
|
98
|
+
className: el.className,
|
|
99
|
+
id: el.id,
|
|
100
|
+
textContent: el.textContent,
|
|
101
|
+
rect: el.rect,
|
|
102
|
+
},
|
|
103
|
+
details: {
|
|
104
|
+
actualWidth: el.actualWidth,
|
|
105
|
+
actualHeight: el.actualHeight,
|
|
106
|
+
minWidth,
|
|
107
|
+
minHeight,
|
|
108
|
+
},
|
|
109
|
+
fixHint: `Add min-width: ${minWidth}px and min-height: ${minHeight}px, or increase padding`,
|
|
110
|
+
}));
|
|
111
|
+
return {
|
|
112
|
+
check: 'touch-targets',
|
|
113
|
+
passed: Math.max(0, (await page.$$(selectors.join(', '))).length - issues.length),
|
|
114
|
+
issues,
|
|
115
|
+
};
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
//# sourceMappingURL=touch-targets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"touch-targets.js","sourceRoot":"","sources":["../../src/checks/touch-targets.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"visibility.d.ts","sourceRoot":"","sources":["../../src/checks/visibility.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAuB,MAAM,aAAa,CAAC;AAmB9D,eAAO,MAAM,eAAe,EAAE,KAwI7B,CAAC"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
export const visibilityCheck = {
|
|
2
|
+
name: 'visibility',
|
|
3
|
+
description: 'Detects interactive elements that are invisible or off-screen',
|
|
4
|
+
async run(ctx) {
|
|
5
|
+
const { page, config } = ctx;
|
|
6
|
+
const checkConfig = typeof config.checks?.visibility === 'object'
|
|
7
|
+
? config.checks.visibility
|
|
8
|
+
: {};
|
|
9
|
+
const selectors = checkConfig.selectors || [
|
|
10
|
+
'button',
|
|
11
|
+
'a[href]',
|
|
12
|
+
'input:not([type="hidden"])',
|
|
13
|
+
'select',
|
|
14
|
+
'textarea',
|
|
15
|
+
'[role="button"]',
|
|
16
|
+
];
|
|
17
|
+
const ignoreSelectors = [
|
|
18
|
+
...(config.ignore || []),
|
|
19
|
+
...(checkConfig.ignore || []),
|
|
20
|
+
// Common intentionally hidden elements
|
|
21
|
+
'[aria-hidden="true"]',
|
|
22
|
+
'.sr-only',
|
|
23
|
+
'.visually-hidden',
|
|
24
|
+
'[hidden]',
|
|
25
|
+
];
|
|
26
|
+
const invisibleElements = await page.evaluate(({ selectors, ignoreSelectors }) => {
|
|
27
|
+
const results = [];
|
|
28
|
+
const selectorString = selectors.join(', ');
|
|
29
|
+
const elements = document.querySelectorAll(selectorString);
|
|
30
|
+
for (const el of elements) {
|
|
31
|
+
// Skip ignored elements
|
|
32
|
+
const shouldIgnore = ignoreSelectors.some(sel => {
|
|
33
|
+
try {
|
|
34
|
+
return el.matches(sel);
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
if (shouldIgnore)
|
|
41
|
+
continue;
|
|
42
|
+
const style = getComputedStyle(el);
|
|
43
|
+
const rect = el.getBoundingClientRect();
|
|
44
|
+
let reason = null;
|
|
45
|
+
// Check for various invisibility reasons
|
|
46
|
+
if (style.display === 'none') {
|
|
47
|
+
reason = 'display: none';
|
|
48
|
+
}
|
|
49
|
+
else if (style.visibility === 'hidden') {
|
|
50
|
+
reason = 'visibility: hidden';
|
|
51
|
+
}
|
|
52
|
+
else if (style.opacity === '0') {
|
|
53
|
+
reason = 'opacity: 0';
|
|
54
|
+
}
|
|
55
|
+
else if (rect.width === 0 && rect.height === 0) {
|
|
56
|
+
reason = 'zero size (0x0)';
|
|
57
|
+
}
|
|
58
|
+
else if (rect.right < 0) {
|
|
59
|
+
reason = 'off-screen left';
|
|
60
|
+
}
|
|
61
|
+
else if (rect.bottom < 0) {
|
|
62
|
+
reason = 'off-screen top';
|
|
63
|
+
}
|
|
64
|
+
else if (rect.left > window.innerWidth) {
|
|
65
|
+
reason = 'off-screen right';
|
|
66
|
+
}
|
|
67
|
+
else if (rect.top > window.innerHeight) {
|
|
68
|
+
reason = 'off-screen bottom';
|
|
69
|
+
}
|
|
70
|
+
else if (parseInt(style.clipPath?.includes('inset(100%)') ? '1' : '0')) {
|
|
71
|
+
reason = 'clipped (clip-path)';
|
|
72
|
+
}
|
|
73
|
+
if (reason) {
|
|
74
|
+
// Skip if it's likely intentional (e.g., inside a hidden parent)
|
|
75
|
+
const parent = el.parentElement;
|
|
76
|
+
if (parent) {
|
|
77
|
+
const parentStyle = getComputedStyle(parent);
|
|
78
|
+
if (parentStyle.display === 'none' || parentStyle.visibility === 'hidden') {
|
|
79
|
+
continue; // Parent is hidden, child being hidden is expected
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
let selector = el.tagName.toLowerCase();
|
|
83
|
+
if (el.id)
|
|
84
|
+
selector += `#${el.id}`;
|
|
85
|
+
if (el.className && typeof el.className === 'string') {
|
|
86
|
+
selector += '.' + el.className.trim().split(/\s+/).join('.');
|
|
87
|
+
}
|
|
88
|
+
results.push({
|
|
89
|
+
selector,
|
|
90
|
+
tagName: el.tagName,
|
|
91
|
+
className: typeof el.className === 'string' ? el.className : '',
|
|
92
|
+
id: el.id || null,
|
|
93
|
+
textContent: el.textContent?.slice(0, 30)?.trim() || null,
|
|
94
|
+
rect: {
|
|
95
|
+
width: rect.width,
|
|
96
|
+
height: rect.height,
|
|
97
|
+
left: rect.left,
|
|
98
|
+
top: rect.top,
|
|
99
|
+
right: rect.right,
|
|
100
|
+
bottom: rect.bottom,
|
|
101
|
+
},
|
|
102
|
+
reason,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return results;
|
|
107
|
+
}, { selectors, ignoreSelectors });
|
|
108
|
+
const issues = invisibleElements.map(el => ({
|
|
109
|
+
check: 'visibility',
|
|
110
|
+
severity: 'warning',
|
|
111
|
+
message: `Interactive element is not visible: ${el.reason}`,
|
|
112
|
+
element: {
|
|
113
|
+
selector: el.selector,
|
|
114
|
+
tagName: el.tagName,
|
|
115
|
+
className: el.className,
|
|
116
|
+
id: el.id,
|
|
117
|
+
textContent: el.textContent,
|
|
118
|
+
rect: el.rect,
|
|
119
|
+
},
|
|
120
|
+
details: {
|
|
121
|
+
reason: el.reason,
|
|
122
|
+
},
|
|
123
|
+
fixHint: `Check if this element should be visible. If intentionally hidden, add [data-rlint-ignore] or .sr-only`,
|
|
124
|
+
}));
|
|
125
|
+
return {
|
|
126
|
+
check: 'visibility',
|
|
127
|
+
passed: Math.max(0, (await page.$$(selectors.join(', '))).length - issues.length),
|
|
128
|
+
issues,
|
|
129
|
+
};
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
//# sourceMappingURL=visibility.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"visibility.js","sourceRoot":"","sources":["../../src/checks/visibility.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|