ixbrl-viewer 1.4.68__py3-none-any.whl → 1.4.70__py3-none-any.whl
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.
Potentially problematic release.
This version of ixbrl-viewer might be problematic. Click here for more details.
- iXBRLViewerPlugin/_version.py +2 -2
- iXBRLViewerPlugin/viewer/dist/ixbrlviewer.js +1 -1
- {ixbrl_viewer-1.4.68.dist-info → ixbrl_viewer-1.4.70.dist-info}/METADATA +3 -4
- {ixbrl_viewer-1.4.68.dist-info → ixbrl_viewer-1.4.70.dist-info}/RECORD +9 -29
- {ixbrl_viewer-1.4.68.dist-info → ixbrl_viewer-1.4.70.dist-info}/top_level.txt +0 -1
- tests/__init__.py +0 -0
- tests/puppeteer/framework/core_elements.js +0 -117
- tests/puppeteer/framework/page_objects/doc_frame.js +0 -105
- tests/puppeteer/framework/page_objects/fact_details_panel.js +0 -112
- tests/puppeteer/framework/page_objects/search_panel.js +0 -73
- tests/puppeteer/framework/page_objects/toolbar.js +0 -18
- tests/puppeteer/framework/utils.js +0 -3
- tests/puppeteer/framework/viewer_page.js +0 -103
- tests/puppeteer/puppeteer_test_run_via_intellij.jpg +0 -0
- tests/puppeteer/test_filings/filing_documents_smoke_test.zip +0 -0
- tests/puppeteer/test_filings/highlights.zip +0 -0
- tests/puppeteer/tests/fact_properties.test.js +0 -84
- tests/puppeteer/tests/highlight.test.js +0 -186
- tests/puppeteer/tests/search.test.js +0 -86
- tests/puppeteer/tools/generate.sh +0 -15
- tests/unit_tests/__init__.py +0 -0
- tests/unit_tests/iXBRLViewerPlugin/__init__.py +0 -0
- tests/unit_tests/iXBRLViewerPlugin/mock_arelle.py +0 -41
- tests/unit_tests/iXBRLViewerPlugin/test_iXBRLViewer.py +0 -801
- tests/unit_tests/iXBRLViewerPlugin/test_xhtmlserialize.py +0 -310
- {ixbrl_viewer-1.4.68.dist-info → ixbrl_viewer-1.4.70.dist-info}/WHEEL +0 -0
- {ixbrl_viewer-1.4.68.dist-info → ixbrl_viewer-1.4.70.dist-info}/entry_points.txt +0 -0
- {ixbrl_viewer-1.4.68.dist-info → ixbrl_viewer-1.4.70.dist-info}/licenses/LICENSE.md +0 -0
- {ixbrl_viewer-1.4.68.dist-info → ixbrl_viewer-1.4.70.dist-info}/licenses/NOTICE +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ixbrl-viewer
|
|
3
|
-
Version: 1.4.
|
|
3
|
+
Version: 1.4.70
|
|
4
4
|
Summary: The Arelle iXBRL Viewer allows iXBRL reports to be viewed interactively in a web browser.
|
|
5
5
|
Author-email: "arelle.org" <support@arelle.org>
|
|
6
|
-
License: Apache-2.0
|
|
6
|
+
License-Expression: Apache-2.0
|
|
7
7
|
Project-URL: Homepage, https://arelle.org/
|
|
8
8
|
Project-URL: Downloads, https://arelle.org/arelle/pub/
|
|
9
9
|
Project-URL: Documentation, https://arelle.org/arelle/documentation/
|
|
@@ -16,7 +16,6 @@ Platform: any
|
|
|
16
16
|
Classifier: Development Status :: 5 - Production/Stable
|
|
17
17
|
Classifier: Intended Audience :: End Users/Desktop
|
|
18
18
|
Classifier: Intended Audience :: Developers
|
|
19
|
-
Classifier: License :: OSI Approved :: Apache Software License
|
|
20
19
|
Classifier: Natural Language :: English
|
|
21
20
|
Classifier: Programming Language :: Python :: 3
|
|
22
21
|
Classifier: Programming Language :: Python :: 3.9
|
|
@@ -38,7 +37,7 @@ Requires-Dist: flake8==7.3.0; extra == "dev"
|
|
|
38
37
|
Requires-Dist: lxml-stubs==0.5.1; extra == "dev"
|
|
39
38
|
Requires-Dist: mypy==1.16.1; extra == "dev"
|
|
40
39
|
Requires-Dist: pytest==8.4.1; extra == "dev"
|
|
41
|
-
Requires-Dist: typing-extensions==4.14.
|
|
40
|
+
Requires-Dist: typing-extensions==4.14.1; extra == "dev"
|
|
42
41
|
Dynamic: license-file
|
|
43
42
|
|
|
44
43
|
# Arelle iXBRL Viewer
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
iXBRLViewerPlugin/__init__.py,sha256=263DtPKBXeTomSI-4SeE7gTX0ak16GiIB9DFOoxBK3k,16839
|
|
2
|
-
iXBRLViewerPlugin/_version.py,sha256=
|
|
2
|
+
iXBRLViewerPlugin/_version.py,sha256=J1zb3LOE18nHstPewQ6MOy7F1Vs0t7rwcIf22yoDXT8,513
|
|
3
3
|
iXBRLViewerPlugin/constants.py,sha256=DMyEKzoJZPN36vINiNF9wiP7PPofQg0ZLF0H2CP-ns4,3866
|
|
4
4
|
iXBRLViewerPlugin/featureConfig.py,sha256=551aLjiGAkoxsgFDMBJT8s3nVaF787kgbRjFumTTsBE,255
|
|
5
5
|
iXBRLViewerPlugin/iXBRLViewer.py,sha256=7IqbLuapCla5_c-lSM4KiKgSezbioKulJ5pa4ZEPejs,30643
|
|
@@ -12,7 +12,7 @@ iXBRLViewerPlugin/viewer/version.js,sha256=7b5si8UmaS7QqALQIP-wJ0I8onKFox8VyaAiU
|
|
|
12
12
|
iXBRLViewerPlugin/viewer/webpack.common.js,sha256=hpXufjShXAESQh8Ds7ViJ_tbr8XSb7EdqQLRvyu_mQg,1691
|
|
13
13
|
iXBRLViewerPlugin/viewer/webpack.dev.js,sha256=R9AwY_TBrg9otvXpDxT1UsGg5eMqi4aXW7C7EIsHzZk,551
|
|
14
14
|
iXBRLViewerPlugin/viewer/webpack.prod.js,sha256=vfLowWC1EOty0zbq9P-usGaJ3w-JoERpJrYaE9MfoSE,499
|
|
15
|
-
iXBRLViewerPlugin/viewer/dist/ixbrlviewer.js,sha256=
|
|
15
|
+
iXBRLViewerPlugin/viewer/dist/ixbrlviewer.js,sha256=bup-oQnni-Ja9mcivzP45YCmrtoV4eDtwdnJJbrfwW4,942420
|
|
16
16
|
iXBRLViewerPlugin/viewer/dist/ixbrlviewer.js.LICENSE.txt,sha256=LtlhXpjEorsiefz-usoFDNTZC_le1KyOKPWgb3RWIq8,1832
|
|
17
17
|
iXBRLViewerPlugin/viewer/src/data/utr.json,sha256=32ZuovUvTOiCiXnxQH1fyOlgLEbah8DhUInqxZNIjFc,20159
|
|
18
18
|
iXBRLViewerPlugin/viewer/src/html/fact-details.html,sha256=r3cPuOzcosgBkYl0jfmRUGjYnLgv6Eu0IeTMmUHY4Ak,2863
|
|
@@ -208,30 +208,10 @@ iXBRLViewerPlugin/viewer/src/less/text-mixins.less,sha256=pYeQjz6JptSyUTxoVwfNeb
|
|
|
208
208
|
iXBRLViewerPlugin/viewer/src/less/validation-report.less,sha256=HWkYEWxny-_zAYrwPXwAXuNCbThMRyomhu6KteUAjEg,351
|
|
209
209
|
iXBRLViewerPlugin/viewer/src/less/viewer.less,sha256=piLepb_LJUQw0iDfpXtrHI1Wts9L4tbKvAc3ZDtZi-Q,5598
|
|
210
210
|
iXBRLViewerPlugin/viewer/tools/build-font.js,sha256=Q-Uh0o6Uj2fkKd0zjS7rbVV2mkNydmEWGpkg0uWVir4,818
|
|
211
|
-
ixbrl_viewer-1.4.
|
|
212
|
-
ixbrl_viewer-1.4.
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
tests/puppeteer/framework/page_objects/doc_frame.js,sha256=PkWK_mRdrWcBAZA0H_ySjrtVxHnQJy5g6ribPwtONns,4196
|
|
219
|
-
tests/puppeteer/framework/page_objects/fact_details_panel.js,sha256=nqZWbkhSoYG-h_sVknD0MI-lQwAytDMb_UxOtX3omZg,5151
|
|
220
|
-
tests/puppeteer/framework/page_objects/search_panel.js,sha256=lLHX3yYr242A-bFZqgUq6Srp6REsmwO7JddmKE2732Q,2566
|
|
221
|
-
tests/puppeteer/framework/page_objects/toolbar.js,sha256=5rtdWMdkxp1n5mTiA4ajBemtf76Ypk4nNtLOscunK5M,779
|
|
222
|
-
tests/puppeteer/test_filings/filing_documents_smoke_test.zip,sha256=XThNTiVR23jeej8RAys7s928FitPbiHD6hXfbMdEg-U,21469
|
|
223
|
-
tests/puppeteer/test_filings/highlights.zip,sha256=7CM_GjUcPrJdBiSisPmCyuq1rnWRkyRTfvtpNHIagiQ,18766
|
|
224
|
-
tests/puppeteer/tests/fact_properties.test.js,sha256=_WsW29v4yh_Q6vLqHJUJyQSkoJgTxBMabBfaKCql1AA,3855
|
|
225
|
-
tests/puppeteer/tests/highlight.test.js,sha256=ivTf3MOfDUcNBaukR-CTgNJRbvhXegW-ZH1f4v_1mvc,6130
|
|
226
|
-
tests/puppeteer/tests/search.test.js,sha256=N6EDeok_PQct4C8fIFfUeZi-9Wdnc1c1D3YrOJli5pM,3567
|
|
227
|
-
tests/puppeteer/tools/generate.sh,sha256=jZ1Jac6KsuRo2nHuA2B9g9mZqr7tLjPJKJAUf0oNArs,524
|
|
228
|
-
tests/unit_tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
229
|
-
tests/unit_tests/iXBRLViewerPlugin/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
230
|
-
tests/unit_tests/iXBRLViewerPlugin/mock_arelle.py,sha256=fQKJGwceaEVaxvqV9tyuvT6DJB5s1X3-oSMjbFMKlS8,1301
|
|
231
|
-
tests/unit_tests/iXBRLViewerPlugin/test_iXBRLViewer.py,sha256=2beDs9kf0wRZFw9NM-dYm4j1zlxRybFj7f1JpwUTttQ,33756
|
|
232
|
-
tests/unit_tests/iXBRLViewerPlugin/test_xhtmlserialize.py,sha256=kdrg4i-YXnYAGl9TLWEACPQF0V69OAMXdXLYJwefsH0,12521
|
|
233
|
-
ixbrl_viewer-1.4.68.dist-info/METADATA,sha256=5s9CIgTyZD-qlJQ3-Rh3m9d7CGg_Uv5fUFrrFoYpNHQ,18542
|
|
234
|
-
ixbrl_viewer-1.4.68.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
235
|
-
ixbrl_viewer-1.4.68.dist-info/entry_points.txt,sha256=2XUzP20WGwxdvnugdBybUBwAB3xf_zvrOR5W_smFz_4,65
|
|
236
|
-
ixbrl_viewer-1.4.68.dist-info/top_level.txt,sha256=h8MkrMhC_t2-KbfS1oxJVnFAbn5NZJH8END_BL40mv8,24
|
|
237
|
-
ixbrl_viewer-1.4.68.dist-info/RECORD,,
|
|
211
|
+
ixbrl_viewer-1.4.70.dist-info/licenses/LICENSE.md,sha256=FOClHlBMDtwKei0tOJ7pv8WZ_HuQJMNYD1az3NpK73o,16647
|
|
212
|
+
ixbrl_viewer-1.4.70.dist-info/licenses/NOTICE,sha256=-SHDY0OY7s4gm4Rdk5AB3nbnUsrdHEHPdJuGFR_vuM4,566
|
|
213
|
+
ixbrl_viewer-1.4.70.dist-info/METADATA,sha256=vvHUsB-Ju8Llu_Sd0uy-zj9vCiJcec95oQuU8GvuL40,18490
|
|
214
|
+
ixbrl_viewer-1.4.70.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
215
|
+
ixbrl_viewer-1.4.70.dist-info/entry_points.txt,sha256=2XUzP20WGwxdvnugdBybUBwAB3xf_zvrOR5W_smFz_4,65
|
|
216
|
+
ixbrl_viewer-1.4.70.dist-info/top_level.txt,sha256=iQGm144gaJwCPqPDGQHi8umPFRQAvQShVKDk3JQv3JE,18
|
|
217
|
+
ixbrl_viewer-1.4.70.dist-info/RECORD,,
|
tests/__init__.py
DELETED
|
File without changes
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
import { getTextContent } from './utils.js';
|
|
2
|
-
|
|
3
|
-
export class Text {
|
|
4
|
-
#viewerPage;
|
|
5
|
-
#xpathSelector;
|
|
6
|
-
#name;
|
|
7
|
-
|
|
8
|
-
constructor(viewerPage, xpathSelector, name) {
|
|
9
|
-
this.#viewerPage = viewerPage;
|
|
10
|
-
this.#xpathSelector = xpathSelector;
|
|
11
|
-
this.#name = name;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
async assertText(expectedText) {
|
|
15
|
-
this.#viewerPage.log(
|
|
16
|
-
`Asserting text content of ${this.#name} equals "${expectedText}"`);
|
|
17
|
-
const elem = await this.#viewerPage.page.waitForSelector(
|
|
18
|
-
'xpath/' + this.#xpathSelector, { visible: true });
|
|
19
|
-
const text = await getTextContent(elem);
|
|
20
|
-
expect(text).toEqual(expectedText);
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export class Button {
|
|
25
|
-
#viewerPage;
|
|
26
|
-
#xpathSelector;
|
|
27
|
-
#name;
|
|
28
|
-
|
|
29
|
-
constructor(viewerPage, xpathSelector, name) {
|
|
30
|
-
this.#viewerPage = viewerPage;
|
|
31
|
-
this.#xpathSelector = xpathSelector;
|
|
32
|
-
this.#name = name;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
async hover() {
|
|
36
|
-
this.#viewerPage.log(`Hovering ${this.#name}`);
|
|
37
|
-
const button = await this.getButtonElement();
|
|
38
|
-
await button.hover();
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
async select() {
|
|
42
|
-
this.#viewerPage.log(`Select ${this.#name}`);
|
|
43
|
-
const button = await this.getButtonElement();
|
|
44
|
-
await button.click();
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
async getButtonElement() {
|
|
48
|
-
return await this.#viewerPage.page.waitForSelector(
|
|
49
|
-
'xpath/' + this.#xpathSelector, { visible: true });
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export class Checkbox {
|
|
54
|
-
#viewerPage;
|
|
55
|
-
#xpathSelector;
|
|
56
|
-
#name;
|
|
57
|
-
|
|
58
|
-
constructor(viewerPage, xpathSelector, name) {
|
|
59
|
-
this.#viewerPage = viewerPage;
|
|
60
|
-
this.#xpathSelector = xpathSelector;
|
|
61
|
-
this.#name = name;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
async getInput() {
|
|
65
|
-
return await this.#viewerPage.page.waitForSelector(
|
|
66
|
-
'xpath/' + this.#xpathSelector, { visible: true });
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
async isChecked() {
|
|
70
|
-
const checkbox = await this.getInput();
|
|
71
|
-
return await (await checkbox.getProperty('checked')).jsonValue();
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
async toggleOff() {
|
|
75
|
-
if (await this.isChecked() === true) {
|
|
76
|
-
this.#viewerPage.log(`Toggling off ${this.#name}`);
|
|
77
|
-
const toggle = await this.getInput();
|
|
78
|
-
await toggle.click();
|
|
79
|
-
} else {
|
|
80
|
-
this.#viewerPage.log(`${this.#name} was already toggled off`);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
async toggleOn() {
|
|
85
|
-
if (await this.isChecked() === false) {
|
|
86
|
-
this.#viewerPage.log(`Toggling on ${this.#name}`);
|
|
87
|
-
const toggle = await this.getInput();
|
|
88
|
-
await toggle.click();
|
|
89
|
-
} else {
|
|
90
|
-
this.#viewerPage.log(`${this.#name} already toggled on`);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export class TextInput {
|
|
96
|
-
#viewerPage;
|
|
97
|
-
#xpathSelector;
|
|
98
|
-
#name;
|
|
99
|
-
|
|
100
|
-
constructor(viewerPage, xpathSelector, name) {
|
|
101
|
-
this.#viewerPage = viewerPage;
|
|
102
|
-
this.#xpathSelector = xpathSelector;
|
|
103
|
-
this.#name = name;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
async enterText(text, pressEnter = false) {
|
|
107
|
-
this.#viewerPage.log(`Entering "${text}" into ${this.#name}`);
|
|
108
|
-
const input = await this.getInput();
|
|
109
|
-
await input.type(text);
|
|
110
|
-
await input.press('Enter');
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
async getInput() {
|
|
114
|
-
return await this.#viewerPage.page.waitForSelector(
|
|
115
|
-
'xpath/' + this.#xpathSelector, { visible: true });
|
|
116
|
-
}
|
|
117
|
-
}
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
export class DocFrame {
|
|
2
|
-
#viewerPage;
|
|
3
|
-
|
|
4
|
-
constructor(viewerPage) {
|
|
5
|
-
this.#viewerPage = viewerPage;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
async getDocumentIframe() {
|
|
9
|
-
const iframe = await this.#viewerPage.page.waitForSelector(
|
|
10
|
-
'xpath/' + '//iframe[@title="iXBRL document view"]');
|
|
11
|
-
return iframe.contentFrame();
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
async getSelectedFact() {
|
|
15
|
-
const iframe = await this.getDocumentIframe();
|
|
16
|
-
return iframe.waitForSelector(
|
|
17
|
-
'xpath/' + '//*[contains(@class,"ixbrl-selected")]');
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
// Selects a fact in the document based on name
|
|
21
|
-
// Ex: "dei:DocumentType"
|
|
22
|
-
async selectFact(name) {
|
|
23
|
-
this.#viewerPage.log(`Selecting fact ${name}`);
|
|
24
|
-
const iframe = await this.getDocumentIframe();
|
|
25
|
-
const fact = await iframe.waitForSelector(
|
|
26
|
-
'xpath/' + `//*[@name="${name}"]`);
|
|
27
|
-
return fact.click();
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/// Asserts the highlight color matches for the selected document values
|
|
31
|
-
async assertHighlights(highlights) {
|
|
32
|
-
const contentFrame = await this.getDocumentIframe();
|
|
33
|
-
for (const highlight of highlights) {
|
|
34
|
-
this.#viewerPage.log(
|
|
35
|
-
`Asserting the value "${highlight.docContent}" to be highlighted with ${highlight.property} ${highlight.color}`);
|
|
36
|
-
const element = await contentFrame.waitForSelector(
|
|
37
|
-
'xpath/' + highlight.locator);
|
|
38
|
-
const style = await contentFrame.evaluate(
|
|
39
|
-
(element, property) => getComputedStyle(element)
|
|
40
|
-
.getPropertyValue(property), element, highlight.property);
|
|
41
|
-
expect(style).toEqual(highlight.color);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export class Highlight {
|
|
47
|
-
|
|
48
|
-
static darkBlue = 'rgb(2, 109, 206)';
|
|
49
|
-
static green = 'rgb(190, 234, 143)';
|
|
50
|
-
static lightBlue = 'rgb(0, 148, 255)';
|
|
51
|
-
static yellow = 'rgb(255, 172, 41)';
|
|
52
|
-
static purple = 'rgb(221, 115, 255)';
|
|
53
|
-
static propBgColor = 'background-color';
|
|
54
|
-
static propOutline = 'outline';
|
|
55
|
-
static transparent = 'rgba(0, 0, 0, 0)';
|
|
56
|
-
|
|
57
|
-
constructor(color, locator, property, docContent) {
|
|
58
|
-
this.color = color;
|
|
59
|
-
this.docContent = docContent;
|
|
60
|
-
this.locator = locator;
|
|
61
|
-
this.property = property;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
static fact(docContent, active = true) {
|
|
65
|
-
const color = active ? this.green : this.transparent;
|
|
66
|
-
const locator = `//*[contains(text(),"${docContent}")]//ancestor::*[contains(@class,"ixbrl-element")]`;
|
|
67
|
-
return new Highlight(color, locator, this.propBgColor, docContent);
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
static factNamespace2(docContent, active = true) {
|
|
71
|
-
const color = active ? this.purple : this.transparent;
|
|
72
|
-
const locator = `//*[contains(text(),"${docContent}")]//ancestor::*[contains(@class,"ixbrl-element")]`;
|
|
73
|
-
return new Highlight(color, locator, this.propBgColor, docContent);
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
static searchHover(docContent, active = true) {
|
|
77
|
-
const color = active
|
|
78
|
-
? `${this.darkBlue} dashed 2px`
|
|
79
|
-
: 'rgb(0, 0, 0) none 0px';
|
|
80
|
-
const locator = `//*[contains(text(),"${docContent}")]//ancestor::*[contains(@class,"ixbrl-element")]`;
|
|
81
|
-
return new Highlight(color, locator, this.propOutline, docContent);
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
static selectedFact(docContent, active = true) {
|
|
85
|
-
const color = active
|
|
86
|
-
? `${this.lightBlue} solid 2px`
|
|
87
|
-
: 'rgb(0, 0, 0) none 0px';
|
|
88
|
-
const locator = `//*[contains(text(),"${docContent}")]//ancestor::*[contains(@class,"ixbrl-element")]`;
|
|
89
|
-
return new Highlight(color, locator, this.propOutline, docContent);
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
static untaggedDate(docContent, active = true) {
|
|
93
|
-
const color = active ? this.yellow : this.transparent;
|
|
94
|
-
const locator =
|
|
95
|
-
`//*[contains(@class,"review-untagged-date") and contains(text(),"${docContent}")]`;
|
|
96
|
-
return new Highlight(color, locator, this.propBgColor, docContent)
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
static untaggedNumber(docContent, active = true) {
|
|
100
|
-
const color = active ? this.purple : this.transparent;
|
|
101
|
-
const locator =
|
|
102
|
-
`//*[contains(@class,"review-untagged-number") and contains(text(),"${docContent}")]`;
|
|
103
|
-
return new Highlight(color, locator, this.propBgColor, docContent);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
import { Button, Text } from '../core_elements.js';
|
|
2
|
-
import { getTextContent } from '../utils.js';
|
|
3
|
-
|
|
4
|
-
export class FactDetailsPanel {
|
|
5
|
-
#viewerPage;
|
|
6
|
-
|
|
7
|
-
constructor(viewerPage) {
|
|
8
|
-
this.#viewerPage = viewerPage;
|
|
9
|
-
|
|
10
|
-
this.accuracy = new Text(
|
|
11
|
-
this.#viewerPage,
|
|
12
|
-
'//*[@data-i18n="factDetails.accuracy"]//ancestor::tr//td',
|
|
13
|
-
'Fact Accuracy');
|
|
14
|
-
this.concept = new Text(
|
|
15
|
-
this.#viewerPage,
|
|
16
|
-
'//*[contains(@class, "fact-details")]//*[contains(@class,"std-label")]',
|
|
17
|
-
'Fact Concept');
|
|
18
|
-
this.date = new Text(
|
|
19
|
-
this.#viewerPage,
|
|
20
|
-
'//*[@data-i18n="factDetails.date"]//ancestor::tr//td',
|
|
21
|
-
'Fact Date');
|
|
22
|
-
this.duplicateNext = new Button(
|
|
23
|
-
this.#viewerPage,
|
|
24
|
-
'//*[contains(@class,"duplicates")]//*[contains(@class,"next")]',
|
|
25
|
-
'Duplicate next');
|
|
26
|
-
this.duplicateText = new Text(
|
|
27
|
-
this.#viewerPage,
|
|
28
|
-
'//*[contains(@class,"duplicates")]//*[contains(@class,"text")]',
|
|
29
|
-
'Duplicate Count');
|
|
30
|
-
this.factValue = new Text(
|
|
31
|
-
this.#viewerPage,
|
|
32
|
-
'//*[@data-i18n="factDetails.factValue"]//ancestor::tr//*[contains(concat(" ",@class," "), " value ")]',
|
|
33
|
-
'Fact Value');
|
|
34
|
-
this.entity = new Text(
|
|
35
|
-
this.#viewerPage,
|
|
36
|
-
'//*[@data-i18n="factDetails.entity"]//ancestor::tr//td',
|
|
37
|
-
'Fact Entity');
|
|
38
|
-
this.nextFact = new Button(this.#viewerPage,
|
|
39
|
-
'//*[contains(@class, "ixbrl-next-tag")]', 'Next Fact');
|
|
40
|
-
this.previousFact = new Button(this.#viewerPage,
|
|
41
|
-
'//*[contains(@class, "ixbrl-prev-tag")]', 'Previous Fact');
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Asserts the calculation contributors listed in the fact details panel
|
|
45
|
-
// under the [sectionTitle]. [expectedCalculation] is a map of concept to
|
|
46
|
-
// weight ex: {'cash':'+ '}
|
|
47
|
-
async assertCalculation(sectionTitle, expectedCalculations) {
|
|
48
|
-
this.#viewerPage.log(`Asserting Calculations for section ${sectionTitle}`);
|
|
49
|
-
|
|
50
|
-
// Pull the title elements and assert the section exists
|
|
51
|
-
const titleElems = await this.#viewerPage.page.$$('.calculations .title');
|
|
52
|
-
|
|
53
|
-
const titles = await Promise.all(titleElems.map(async (e) => await getTextContent(e)));
|
|
54
|
-
expect(titles).toContain(sectionTitle)
|
|
55
|
-
|
|
56
|
-
// Pull the concepts from the expected section
|
|
57
|
-
const calc = await this.#viewerPage.page.evaluate((sectionTitle) => {
|
|
58
|
-
const conceptElemsXPath = `
|
|
59
|
-
//*[contains(@class,"calculations")]//*[text()="${sectionTitle}"]
|
|
60
|
-
//ancestor::*[contains(@class,"card")]//*[contains(@class,"item")]
|
|
61
|
-
`;
|
|
62
|
-
const conceptElems = document.evaluate(conceptElemsXPath, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
|
|
63
|
-
|
|
64
|
-
let calculations = {};
|
|
65
|
-
for (let i = 0; i < conceptElems.snapshotLength; i++) {
|
|
66
|
-
let elem = conceptElems.snapshotItem(i);
|
|
67
|
-
const nameElem = document.evaluate('.//*[contains(@class,"concept-name")]', elem, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
|
|
68
|
-
const weightElem = document.evaluate('.//*[contains(@class,"weight")]', elem, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
|
|
69
|
-
calculations[nameElem.textContent] = weightElem.textContent;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
return calculations;
|
|
73
|
-
}, sectionTitle);
|
|
74
|
-
|
|
75
|
-
expect(calc).toEqual(expectedCalculations);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
async assertFootnotes(expectedFootnotes) {
|
|
79
|
-
this.#viewerPage.log('Asserting footnotes');
|
|
80
|
-
const conceptElems = await this.#viewerPage.page.$$('.footnotes .block-list-item');
|
|
81
|
-
const footnotes = await Promise.all(conceptElems.map(async (e) => await getTextContent(e)));
|
|
82
|
-
expect(footnotes).toEqual(expectedFootnotes);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Returns the width of the fact details panel
|
|
86
|
-
async getPanelWidth() {
|
|
87
|
-
const inspectorPanel = await this.#viewerPage.page.waitForSelector('#inspector');
|
|
88
|
-
const boundingBox = await inspectorPanel.boundingBox();
|
|
89
|
-
return boundingBox.width;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Resizes the panels based on the provided horizontalMovement value.
|
|
93
|
-
// positive values will move the panel to the right, negative values will
|
|
94
|
-
// move left
|
|
95
|
-
async resizePanel(horizontalMovement) {
|
|
96
|
-
this.#viewerPage.log('Resizing fact details panel by ' + horizontalMovement + ' pixels');
|
|
97
|
-
const resizer = await this.#viewerPage.page
|
|
98
|
-
.waitForSelector('#viewer-resize-handle', { visible: true });
|
|
99
|
-
const box = await resizer.boundingBox();
|
|
100
|
-
|
|
101
|
-
// Start the drag action
|
|
102
|
-
const startX = box.x + box.width / 2;
|
|
103
|
-
const startY = box.y + box.height / 2;
|
|
104
|
-
await this.#viewerPage.page.mouse.move(startX, startY);
|
|
105
|
-
await this.#viewerPage.page.mouse.down();
|
|
106
|
-
|
|
107
|
-
// Drag horizontally
|
|
108
|
-
await this.#viewerPage.page.mouse.move(startX + horizontalMovement, startY, { steps: 10 });
|
|
109
|
-
await this.#viewerPage.page.mouse.up();
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
}
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import { Button, TextInput } from '../core_elements.js';
|
|
2
|
-
import { getTextContent } from '../utils.js';
|
|
3
|
-
|
|
4
|
-
export class Search {
|
|
5
|
-
#viewerPage;
|
|
6
|
-
|
|
7
|
-
constructor(viewerPage) {
|
|
8
|
-
this.#viewerPage = viewerPage;
|
|
9
|
-
this.filterToggle = new Button(this.#viewerPage,
|
|
10
|
-
'//*[contains(@class,"filter-toggle")]', 'Filter Toggle');
|
|
11
|
-
this.reset = new Button(this.#viewerPage,
|
|
12
|
-
'//*[contains(@data-i18n,"inspector.reset")]', 'Reset');
|
|
13
|
-
this.searchButton = new Button(this.#viewerPage,
|
|
14
|
-
'//*[contains(@class,"search-button")]', 'Search');
|
|
15
|
-
this.searchInput = new TextInput(this.#viewerPage,
|
|
16
|
-
'//*[@id="ixbrl-search"]', 'Search Input');
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
async getSearchResults() {
|
|
20
|
-
const elements = await this.#viewerPage.page.$$('.search-results .fact-list-item .title');
|
|
21
|
-
return Promise.all(elements.map(async (e) => {
|
|
22
|
-
return getTextContent(e);
|
|
23
|
-
}));
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
async assertSearchResultsContain(concepts) {
|
|
27
|
-
this.#viewerPage.log(`Asserting search results contain ${concepts}`);
|
|
28
|
-
const results = await this.getSearchResults();
|
|
29
|
-
for (const concept of concepts) {
|
|
30
|
-
expect(results).toContain(concept);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
async assertSearchResultsDoNotContain(concepts) {
|
|
35
|
-
this.#viewerPage.log(
|
|
36
|
-
`Asserting search results do not contain ${concepts}`);
|
|
37
|
-
const results = await this.getSearchResults();
|
|
38
|
-
for (const concept of concepts) {
|
|
39
|
-
expect(results).not.toContain(concept);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
async filterConceptType(option) {
|
|
44
|
-
const dropdown = await this.#viewerPage.page
|
|
45
|
-
.waitForSelector('#search-filter-concept-type');
|
|
46
|
-
await dropdown.select(option);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
async filterPeriod(option) {
|
|
50
|
-
const dropdown = await this.#viewerPage.page
|
|
51
|
-
.waitForSelector('#search-filter-period select');
|
|
52
|
-
await dropdown.select(option);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
getSearchResultCard(conceptName) {
|
|
56
|
-
return new SearchResultCard(this.#viewerPage, conceptName);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export class SearchResultCard {
|
|
61
|
-
#viewerPage;
|
|
62
|
-
|
|
63
|
-
constructor(viewerPage, conceptName) {
|
|
64
|
-
this.conceptName = conceptName;
|
|
65
|
-
this.#viewerPage = viewerPage;
|
|
66
|
-
this.locator = `//*[contains(@class,"title")
|
|
67
|
-
and contains(text(),"${this.conceptName}")]
|
|
68
|
-
//ancestor::*[contains(@class,"fact-list-item")]`;
|
|
69
|
-
this.selectButton = new Button(this.#viewerPage,
|
|
70
|
-
`${this.locator}//*[contains(@class,"select-icon")]`,
|
|
71
|
-
'Select Button');
|
|
72
|
-
}
|
|
73
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { Checkbox } from '../core_elements.js';
|
|
2
|
-
|
|
3
|
-
export class Toolbar {
|
|
4
|
-
#viewerPage;
|
|
5
|
-
|
|
6
|
-
constructor(viewerPage) {
|
|
7
|
-
this.#viewerPage = viewerPage;
|
|
8
|
-
this.xbrlElementHighlight = new Checkbox(this.#viewerPage,
|
|
9
|
-
'//*[contains(@class,"top-bar-controls")]//*[contains(text(),"XBRL Elements")]//input',
|
|
10
|
-
'XBRL Element Highlight');
|
|
11
|
-
this.unTaggedDateHighlight = new Checkbox(this.#viewerPage,
|
|
12
|
-
'//*[contains(@class,"top-bar-controls")]//*[contains(text(),"Untagged Dates")]//input',
|
|
13
|
-
'Untagged Dates');
|
|
14
|
-
this.unTaggedNumberHighlight = new Checkbox(this.#viewerPage,
|
|
15
|
-
'//*[contains(@class,"top-bar-controls")]//*[contains(text(),"Untagged Numbers")]//input',
|
|
16
|
-
'Untagged Numbers');
|
|
17
|
-
}
|
|
18
|
-
}
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import puppeteer from 'puppeteer';
|
|
3
|
-
import { DocFrame } from './page_objects/doc_frame.js';
|
|
4
|
-
import { FactDetailsPanel } from './page_objects/fact_details_panel.js';
|
|
5
|
-
import { PuppeteerScreenRecorder } from 'puppeteer-screen-recorder';
|
|
6
|
-
import { Search } from './page_objects/search_panel.js';
|
|
7
|
-
import { Toolbar } from './page_objects/toolbar.js';
|
|
8
|
-
|
|
9
|
-
export class ViewerPage {
|
|
10
|
-
browser;
|
|
11
|
-
page;
|
|
12
|
-
docFrame;
|
|
13
|
-
factDetailsPanel;
|
|
14
|
-
search;
|
|
15
|
-
toolbar;
|
|
16
|
-
|
|
17
|
-
#artifactDirectory = './tests/puppeteer/artifacts';
|
|
18
|
-
#cleanedTestName = expect.getState()
|
|
19
|
-
.currentTestName
|
|
20
|
-
.replaceAll(/[^a-zA-Z0-9-]/g, '_');
|
|
21
|
-
#isCi = process.env.CI === 'true';
|
|
22
|
-
#logMsgs = [];
|
|
23
|
-
#recorder;
|
|
24
|
-
|
|
25
|
-
async buildPage() {
|
|
26
|
-
// Launch the browser
|
|
27
|
-
this.browser = await puppeteer.launch({
|
|
28
|
-
headless: this.#isCi ? 'new' : false,
|
|
29
|
-
args: [`--window-size=1440,900`],
|
|
30
|
-
defaultViewport: { width: 1440, height: 821 },
|
|
31
|
-
});
|
|
32
|
-
this.page = await this.browser.newPage();
|
|
33
|
-
this.docFrame = new DocFrame(this);
|
|
34
|
-
this.factDetailsPanel = new FactDetailsPanel(this);
|
|
35
|
-
this.search = new Search(this);
|
|
36
|
-
this.toolbar = new Toolbar(this);
|
|
37
|
-
this.#recorder = new PuppeteerScreenRecorder(this.page);
|
|
38
|
-
|
|
39
|
-
// Set up the video recording
|
|
40
|
-
const videoDir = `${this.#artifactDirectory}/video`;
|
|
41
|
-
const videoPath = `${videoDir}/${this.#cleanedTestName}.mp4`;
|
|
42
|
-
await this.#createDirectory(videoDir);
|
|
43
|
-
await this.#recorder.start(videoPath);
|
|
44
|
-
|
|
45
|
-
this.streamLogsToFile(
|
|
46
|
-
`${this.#artifactDirectory}/${this.#cleanedTestName}_chrome_debug.log`);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
log(message) {
|
|
50
|
-
this.#logMsgs.push(message);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
async navigateToViewer(filingZipName, args = '') {
|
|
54
|
-
|
|
55
|
-
const filingName = filingZipName.replace('.zip', '');
|
|
56
|
-
const url = `http://localhost:8080/tests/puppeteer/artifacts/generated_output/${filingName}.htm${args}`;
|
|
57
|
-
this.log(`Navigating to ${url}`);
|
|
58
|
-
await this.page.goto(url, { waitUntil: 'networkidle0' });
|
|
59
|
-
await this.page.waitForSelector(
|
|
60
|
-
'xpath/' + '//*[contains(@class, "loading")]',
|
|
61
|
-
{ visible: false, hidden: true });
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
async tearDown() {
|
|
65
|
-
console.log(this.#logMsgs.join('\n'));
|
|
66
|
-
await this.#recorder.stop();
|
|
67
|
-
await this.browser.close();
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
async #createDirectory(path) {
|
|
71
|
-
if (!fs.existsSync(path)) {
|
|
72
|
-
fs.mkdirSync(path, { recursive: true });
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
streamLogsToFile(filename) {
|
|
77
|
-
const append = (content) => fs.appendFile(filename, `${content}\n`,
|
|
78
|
-
function(err) {
|
|
79
|
-
if (err) throw err;
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
// Delete the file if it already exists
|
|
83
|
-
fs.rmSync(filename, { force: true });
|
|
84
|
-
|
|
85
|
-
// Pipe the log messages to the file
|
|
86
|
-
this.page.on('console', msg => append(
|
|
87
|
-
`${msg.type().substr(0, 3).toUpperCase()} ${msg.text()}`))
|
|
88
|
-
.on('pageerror', function(err) {
|
|
89
|
-
let value = err.toString();
|
|
90
|
-
append(value);
|
|
91
|
-
})
|
|
92
|
-
.on('response', response => append(
|
|
93
|
-
`${response.status()} ${response.url()}`))
|
|
94
|
-
.on('requestfailed', request => append(
|
|
95
|
-
`${request.failure().errorText} ${request.url()}`));
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
async waitMilliseconds(milliseconds) {
|
|
99
|
-
return new Promise(function(resolve) {
|
|
100
|
-
setTimeout(resolve, milliseconds);
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|