@sap_oss/wdio-qmate-service 1.0.0 → 1.0.2

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.
Files changed (144) hide show
  1. package/README.md +2 -1
  2. package/{test/core/package-lock.json → docs/bestPractices/spec.md} +0 -0
  3. package/docs/doc.md +176 -353
  4. package/docs/gettingStarted/setup.md +2 -2
  5. package/docs/index.md +1 -1
  6. package/docs/sections/bestPractices/authentication.md +151 -0
  7. package/docs/sections/bestPractices/dataHandling.md +61 -0
  8. package/docs/sections/bestPractices/selectors.md +44 -0
  9. package/docs/{contact.md → sections/contact.md} +0 -0
  10. package/docs/sections/features/advancedDataHandling.md +252 -0
  11. package/docs/sections/features/config.md +171 -0
  12. package/docs/sections/features/dataHandling.md +83 -0
  13. package/docs/{gettingStarted → sections/features}/selectors.md +0 -0
  14. package/docs/sections/features/spec.md +0 -0
  15. package/docs/sections/gettingStarted/config.md +18 -0
  16. package/docs/sections/gettingStarted/execution.md +7 -0
  17. package/docs/sections/gettingStarted/setup.md +9 -0
  18. package/docs/sections/gettingStarted/spec.md +31 -0
  19. package/docs/sections/gettingStarted/specs.md +19 -0
  20. package/docs/sections/support/bugReporting.md +19 -0
  21. package/docs/sections/support/knownIssuesAndLimitations.md +0 -0
  22. package/docs/sections/support/troubleshooting.md +16 -0
  23. package/docs/sources/images/fiori_form.PNG +0 -0
  24. package/docs/sources/images/sapCloud_form.PNG +0 -0
  25. package/lib/index.js +59 -82
  26. package/lib/index.js.map +1 -1
  27. package/lib/reuse/authenticator/authHandler.js +12 -23
  28. package/lib/reuse/authenticator/authHandler.js.map +1 -1
  29. package/lib/reuse/authenticator/basicUrlAuthenticator.js +21 -32
  30. package/lib/reuse/authenticator/basicUrlAuthenticator.js.map +1 -1
  31. package/lib/reuse/authenticator/customAuthenticator.js +42 -55
  32. package/lib/reuse/authenticator/customAuthenticator.js.map +1 -1
  33. package/lib/reuse/authenticator/formAuthenticator.js +37 -50
  34. package/lib/reuse/authenticator/formAuthenticator.js.map +1 -1
  35. package/lib/reuse/authenticator/plainAuthenticator.js +5 -16
  36. package/lib/reuse/authenticator/plainAuthenticator.js.map +1 -1
  37. package/lib/reuse/helper/jsDocGen.js +4 -12
  38. package/lib/reuse/helper/jsDocGen.js.map +1 -1
  39. package/lib/reuse/index.js +20 -5
  40. package/lib/reuse/index.js.map +1 -1
  41. package/lib/reuse/modules/common/navigation.js +10 -23
  42. package/lib/reuse/modules/common/navigation.js.map +1 -1
  43. package/lib/reuse/modules/common/userInteraction.js +40 -73
  44. package/lib/reuse/modules/common/userInteraction.js.map +1 -1
  45. package/lib/reuse/modules/nonUi5/assertion.js +31 -52
  46. package/lib/reuse/modules/nonUi5/assertion.js.map +1 -1
  47. package/lib/reuse/modules/nonUi5/element.js +266 -331
  48. package/lib/reuse/modules/nonUi5/element.js.map +1 -1
  49. package/lib/reuse/modules/nonUi5/navigation.js +10 -21
  50. package/lib/reuse/modules/nonUi5/navigation.js.map +1 -1
  51. package/lib/reuse/modules/nonUi5/userInteraction.js +176 -215
  52. package/lib/reuse/modules/nonUi5/userInteraction.js.map +1 -1
  53. package/lib/reuse/modules/service/odata.d.ts +106 -94
  54. package/lib/reuse/modules/service/odata.js +209 -204
  55. package/lib/reuse/modules/service/odata.js.map +1 -1
  56. package/lib/reuse/modules/service/rest.js +64 -81
  57. package/lib/reuse/modules/service/rest.js.map +1 -1
  58. package/lib/reuse/modules/ui5/assertion.js +217 -268
  59. package/lib/reuse/modules/ui5/assertion.js.map +1 -1
  60. package/lib/reuse/modules/ui5/confirmationDialog.js +17 -42
  61. package/lib/reuse/modules/ui5/confirmationDialog.js.map +1 -1
  62. package/lib/reuse/modules/ui5/control.js +12 -33
  63. package/lib/reuse/modules/ui5/control.js.map +1 -1
  64. package/lib/reuse/modules/ui5/date.js +70 -89
  65. package/lib/reuse/modules/ui5/date.js.map +1 -1
  66. package/lib/reuse/modules/ui5/element.js +121 -156
  67. package/lib/reuse/modules/ui5/element.js.map +1 -1
  68. package/lib/reuse/modules/ui5/errorDialog.js +4 -17
  69. package/lib/reuse/modules/ui5/errorDialog.js.map +1 -1
  70. package/lib/reuse/modules/ui5/footerBar.js +28 -63
  71. package/lib/reuse/modules/ui5/footerBar.js.map +1 -1
  72. package/lib/reuse/modules/ui5/mockserver.js +390 -427
  73. package/lib/reuse/modules/ui5/mockserver.js.map +1 -1
  74. package/lib/reuse/modules/ui5/navigation.js +105 -132
  75. package/lib/reuse/modules/ui5/navigation.js.map +1 -1
  76. package/lib/reuse/modules/ui5/navigationBar.js +58 -77
  77. package/lib/reuse/modules/ui5/navigationBar.js.map +1 -1
  78. package/lib/reuse/modules/ui5/qunit.js +16 -27
  79. package/lib/reuse/modules/ui5/qunit.js.map +1 -1
  80. package/lib/reuse/modules/ui5/session.js +200 -236
  81. package/lib/reuse/modules/ui5/session.js.map +1 -1
  82. package/lib/reuse/modules/ui5/table.js +79 -98
  83. package/lib/reuse/modules/ui5/table.js.map +1 -1
  84. package/lib/reuse/modules/ui5/userInteraction.js +264 -335
  85. package/lib/reuse/modules/ui5/userInteraction.js.map +1 -1
  86. package/lib/reuse/modules/util/browser.js +131 -178
  87. package/lib/reuse/modules/util/browser.js.map +1 -1
  88. package/lib/reuse/modules/util/data.js +3 -2
  89. package/lib/reuse/modules/util/data.js.map +1 -1
  90. package/lib/reuse/modules/util/file.js +71 -90
  91. package/lib/reuse/modules/util/file.js.map +1 -1
  92. package/lib/reuse/modules/util/function.js +68 -87
  93. package/lib/reuse/modules/util/function.js.map +1 -1
  94. package/lib/reuse/modules/util/system.js +22 -33
  95. package/lib/reuse/modules/util/system.js.map +1 -1
  96. package/lib/scripts/dataExchange/dataExchange.js +145 -160
  97. package/lib/scripts/dataExchange/dataExchange.js.map +1 -1
  98. package/lib/scripts/dataExchange/dataExchangeUtil.js +93 -114
  99. package/lib/scripts/dataExchange/dataExchangeUtil.js.map +1 -1
  100. package/lib/scripts/hooks/after.js +3 -14
  101. package/lib/scripts/hooks/after.js.map +1 -1
  102. package/lib/scripts/hooks/before.js +9 -20
  103. package/lib/scripts/hooks/before.js.map +1 -1
  104. package/lib/scripts/hooks/beforeSession.js +7 -18
  105. package/lib/scripts/hooks/beforeSession.js.map +1 -1
  106. package/lib/scripts/hooks/onComplete.js +2 -13
  107. package/lib/scripts/hooks/onComplete.js.map +1 -1
  108. package/lib/scripts/hooks/onPrepare.js +9 -20
  109. package/lib/scripts/hooks/onPrepare.js.map +1 -1
  110. package/lib/scripts/hooks/utils/addLocatorCommands.js +109 -172
  111. package/lib/scripts/hooks/utils/addLocatorCommands.js.map +1 -1
  112. package/lib/scripts/hooks/utils/browserLog.js +18 -31
  113. package/lib/scripts/hooks/utils/browserLog.js.map +1 -1
  114. package/lib/scripts/hooks/utils/dataExchangeCommands.js +13 -24
  115. package/lib/scripts/hooks/utils/dataExchangeCommands.js.map +1 -1
  116. package/lib/scripts/hooks/utils/decryption.d.ts +1 -1
  117. package/lib/scripts/hooks/utils/decryption.js +33 -14
  118. package/lib/scripts/hooks/utils/decryption.js.map +1 -1
  119. package/lib/scripts/hooks/utils/lib.js +249 -272
  120. package/lib/scripts/hooks/utils/lib.js.map +1 -1
  121. package/lib/scripts/hooks/utils/locatorCommands.js +154 -183
  122. package/lib/scripts/hooks/utils/locatorCommands.js.map +1 -1
  123. package/mkdocs.yml +16 -3
  124. package/package.json +14 -11
  125. package/test/authenticator/staticLogin/specs/custom.spec.js +30 -1
  126. package/test/reuse/nonUi5/element/getById.spec.js +17 -0
  127. package/test/reuse/service/odata/get.spec.js +61 -0
  128. package/test/reuse/service/odata/getEntitySet.spec.js +87 -0
  129. package/test/reuse/service/odata/test.odata.conf.js +15 -0
  130. package/test/reuse/ui5/confirmationDialog/clickButton.spec.js +87 -0
  131. package/test/reuse/ui5/confirmationDialog/test.confirmationDialog.conf.js +2 -1
  132. package/test/reuse/ui5/element/test.element.conf.js +2 -1
  133. package/test/reuse/ui5/element/waitForAll.spec.js +46 -0
  134. package/test/reuse/ui5/userInteraction/clearAndFillSmartFieldInputAndRetry.spec.js +28 -0
  135. package/test/reuse/ui5/userInteraction/test.userInteraction.conf.js +1 -0
  136. package/test/reuse/util/browser/switchToNewWindow.spec.js +2 -2
  137. package/test/reuse/util/console/console.spec.js +27 -0
  138. package/test/reuse/util/console/test.console.conf.js +12 -0
  139. package/test/reuse/util/data/data/test.secure.json +7 -3
  140. package/test/reuse/util/data/getSecureData.spec.js +9 -3
  141. package/test/reuse/util/file/pdfParser.spec.js +33 -8
  142. package/test/reuse/util/system/system.spec.js +16 -0
  143. package/test/reuse/util/system/test.system.conf.js +12 -0
  144. package/tsconfig.json +1 -1
@@ -4,5 +4,5 @@ Make sure [Node.js](https://nodejs.org/en/download/) is installed on your comput
4
4
  ## Installation
5
5
  To install the latest version of the service, simply execute the following command:
6
6
  ```bash
7
- npm install @wdio/qmate-service
8
- ```
7
+ npm install @sap_oss/wdio-qmate-service
8
+ ```
package/docs/index.md CHANGED
@@ -1,4 +1,4 @@
1
- # Qmate Service
1
+ # **Qmate** Service
2
2
  **Qmate Service** is a custom [WebdriverIO](https://webdriver.io/) service built in TypeScript for testing web applications and APIs.
3
3
 
4
4
  ## Main features
@@ -0,0 +1,151 @@
1
+ # Best Practices - Authentication
2
+ For the standard Fiori and Cloud login Pages, we provide some configuration parameters and reuse methods to enable you an easy way of system authentication.
3
+
4
+ There are two different approaches to log into the system:
5
+
6
+ - [Via reuse methods](#via-reuse-methods) (recommended)
7
+ - [Via configuration file](#via-configuration-file)
8
+
9
+ Which way to choose depends on your needs and preferences. If you are testing a scenario which goes through various systems (especially non UI5 systems) it is not recommended/possible to use a global authentication via the configuration file. If there is only one login during the script execution, it is up to you, to do the authentication via the configuration file.
10
+ !!! info
11
+ For consistency reasons we recommend to use the login via reuse methods only.
12
+
13
+
14
+ ## Via reuse methods
15
+ The other way of system authentication, is to use the specific [reuse method](https://sap.github.io/wdio-qmate-service/doc/#ui5.session).
16
+ This is commonly recommended, especially if you are switching between multiple users or systems during the test execution.
17
+ With this approach, you handle the login within a step of your script/s.
18
+ The benefit of this approach is, to have more control over the login as an actual step inside your test process.
19
+ With that, you are able to use the login/logout multiple times during the test execution if you need to switch users or systems during runtime.
20
+
21
+ You can use one of the following methods:
22
+
23
+ === "Generic Login"
24
+ There is a generic login function which can be used for both types of forms (fiori & sap-cloud) to authenticate. This function will automatically determine the right login form and call the proper function.
25
+
26
+ ```js
27
+ it("Step 01: login generic", async function () {
28
+ await ui5.session.login("JOHNDOE", "abc123");
29
+ });
30
+ ```
31
+
32
+ === "Fiori Login"
33
+ Use this configuration for the [Fiori Login Screen](#Fiori-Login-Screen).
34
+ Define the step (on top of your script) and pass at least a valid user to the method.
35
+ If the password for this user differs from the default password *Welcome1!*, you can additionally pass the password as second argument.
36
+
37
+ ```js
38
+ it("Step 01: login fiori", async function () {
39
+ await ui5.session.loginFiori("JOHNDOE", "abc123");
40
+ });
41
+ ```
42
+
43
+ === "SAP Cloud Login"
44
+ Use this configuration for the [SAP Cloud Login Screen](#SAP-Cloud-Login-Screen).
45
+ Define the step (on top of your script) and pass at least a valid user to the method.
46
+ If the password for this user differs from the default password *Welcome1!*, you can additionally pass the password as second argument.
47
+
48
+ ```js
49
+ it("Step 01: login sap cloud", async function () {
50
+ await ui5.session.loginSapCloud("JOHNDOE", "abc123");
51
+ });
52
+ ```
53
+
54
+ === "Custom Login"
55
+ If you want to define your own custom form, you can use the following method.
56
+
57
+ Define the step (on top of your script) and pass the ```username```, ```password```, ```usernameFieldSelector```, ```passwordFieldSelector```
58
+ and ```logonButtonSelector```. Therefore, you need to define the CSS selectors of the corresponding elements.
59
+
60
+ ```js
61
+ it("Step 01: login Custom", async function () {
62
+ const username = "JOHNDOE";
63
+ const password = "Greetings1!";
64
+ const usernameSelector = "#USERNAME_BLOCK input";
65
+ const passwordSelector = "#PASSWORD_BLOCK input";
66
+ const loginButtonSelector = "#LOGIN_LINK";
67
+ await ui5.session.loginCustom(username, password, usernameSelector, passwordSelector, loginButtonSelector);
68
+ });
69
+ ```
70
+
71
+ !!! tip
72
+ You can disable the login centrally without touching your specs by setting the `formType` to `"skip"` inside your configuration file.
73
+
74
+ ## Via configuration file
75
+ Inside the *conf.js* file You can specify how the script will automatically log into the system. This will only be executed once before the first script gets executed. Hence, we recommend to use this only if you are not switching between multiple users or systems during the test execution.
76
+
77
+ There are multiple options, based on the login screen to set the ```formType``` of the [Configuration](<todo-add-configuration-md>).
78
+
79
+ === "Fiori Login"
80
+ Use this configuration for the [Fiori Login Screen](#Fiori-Login-Screen).
81
+ You only need to specify the ```username``` and ```password```.
82
+ ```js
83
+ auth: {
84
+ formType: "fiori-form",
85
+ username: "JOHNDOE",
86
+ password: "Greetings1!"
87
+ },
88
+ ```
89
+
90
+ === "SAP Cloud Login"
91
+ Use this configuration for the [SAP Cloud Login Screen](#SAP-Cloud-Login-Screen).
92
+ You only need to specify the ```username``` and ```password```.
93
+ ```js
94
+ auth: {
95
+ formType: "sapcloud-form",
96
+ username: "JOHNDOE",
97
+ password: "Greetings1!"
98
+ },
99
+ ```
100
+
101
+ === "Others"
102
+ The following options for ```formType``` are also available. You need to specify the ```username``` and ```password``` for this configuration.
103
+
104
+ - ```XSA-form```
105
+ - ```POT-form```
106
+ - ```MIA-form```
107
+ - ```UI5-Form```
108
+
109
+ === "Custom Login"
110
+ If you want to define your own custom form, you can use the following configuration.
111
+ Additionally to the ```username``` and ```password```, you need to specify the ```usernameFieldSelector```, ```passwordFieldSelector``` and ```logonButtonSelector```. Therefore set the CSS selectors of the corresponding elements.
112
+
113
+ ```js
114
+ auth: {
115
+ formType: "custom-form",
116
+ username: "JOHNDOE",
117
+ password: "Greetings1!",
118
+ usernameFieldSelector: "#USERNAME_BLOCK input",
119
+ passwordFieldSelector: "#PASSWORD_BLOCK input",
120
+ logonButtonSelector: "#LOGIN_LINK"
121
+ },
122
+ ```
123
+
124
+ === "Skip Login"
125
+ Use this configuration to skip the login and logout inside the specs.
126
+ ```js
127
+ auth: {
128
+ formType: "skip"
129
+ },
130
+ ```
131
+
132
+ ## Logout
133
+ To logout from a S/4 system, please use the following method.
134
+ ```js
135
+ it("Step XX: logout", async function () {
136
+ await ui5.session.logout();
137
+ });
138
+ ```
139
+
140
+ !!! info
141
+ Please note, if `formType` is set to `"skip"`, the logout will be skipped as well.
142
+
143
+ ## Finding the right login form
144
+ Please use the screenshots to find the proper login form.
145
+ === "Fiori Login Screen"
146
+ ![fiori-form](../../sources/images/fiori_form.PNG)
147
+
148
+ === "SAP Cloud Login Screen"
149
+ ![sapcloud-form](../../sources/images/sapCloud_form.PNG)
150
+
151
+
@@ -0,0 +1,61 @@
1
+ # Best Practices - Data Handling
2
+ > Related Topics: [Data Handling](../features/dataHandling.md) | [Advanced Data Handling](../features/advancedDataHandling.md)
3
+
4
+ - Always decouple the data from the spec. The maintenance of data will become very handy also more transparent.
5
+
6
+ - Store your data files inside the *data* folder.
7
+
8
+ - Create a separate *.json* file for every script. Unless specifically needed by the test scenario to use data from multiple files, it is recommended to keep things simple, and use only one import prefix (that maps to a JSON file) per script.
9
+
10
+ - Name the file exactly like the script which the data belongs to.
11
+
12
+ - Structure your data according to the related section of the UI.
13
+
14
+ - To pass data between different specs, use the [*references.json*](../features/dataHandling.md#reference_data) file and [group](<todo-add-link-to-config-grouped-and-file>) the specs inside the config.
15
+
16
+ - Use meaningful key names for import and export parameters, and use meaningful names for data folders, subfolders, and *JSON* files.
17
+ ```js title="config.js"
18
+ params: {
19
+
20
+ import: {
21
+ purchaseOrder: "./data/purchaseOrder.json",
22
+ ordersFolder: "./data/orders"
23
+ },
24
+ export: {
25
+ supplierInvoice: "./data/export/supplierInvoice.json"
26
+ }
27
+
28
+ },
29
+ ```
30
+
31
+ - Structure your data.
32
+ ```json title="data.json"
33
+ {
34
+ "session": {
35
+ "username": "JOHNDOE",
36
+ "password": "abc123!!"
37
+ },
38
+ "userData": {
39
+ "firstName": "John",
40
+ "lastName": "Doe",
41
+ "address": {
42
+ "street": "Main Road",
43
+ "number": "42",
44
+ "zipCode": "12345",
45
+ "city": "Metropolis"
46
+ }
47
+ }
48
+ }
49
+ ```
50
+
51
+ - You can check if data has been imported correctly by using assertions before accessing it.
52
+ ```js
53
+ const data = browser.config.params.import.yourPrefixSpecName;
54
+ // make an assertion to ensure data was loaded
55
+ await common.assertion.expectDefined(data);
56
+ await common.assertion.expectDefined(data.email);
57
+ ```
58
+
59
+ (Only for data export)
60
+
61
+ - Each script should write to different export files, unless the test scenario needs to do otherwise. If different scripts write to same export file, make sure to merge and not overwrite the export data.
@@ -0,0 +1,44 @@
1
+ # Best Practices - Selectors
2
+ > Related Topics: [Selectors](../features/selectors.md)
3
+
4
+ - Try to avoid text dependencies in selectors. Texts are more likely to change on the UI and if you want to enable your specs for LAT (Language Acceptance Tests) it will break your test.
5
+
6
+ !!! danger "avoid"
7
+ === "UI5"
8
+ ```js
9
+ const selector = {
10
+ "elementProperties": {
11
+ "viewName": "sap.suite.ui.generic.template.ObjectPage.view.Details",
12
+ "metadata": "sap.m.Button",
13
+ "text": "Create"
14
+ }
15
+ };
16
+ ```
17
+
18
+ === "non UI5"
19
+ ```js
20
+ const saveButton = await nonUi5.element.getElementByCss("BUTTON[text='Save']");
21
+ ```
22
+
23
+ - Avoid *xPath* in non_ui5 selectors: *xPath* is a complex construct which includes multiple nodes. Due to the intransparent dependencies of other elements it may brake your test in case of any change of the corresponding elements.
24
+
25
+ !!! danger "avoid"
26
+ ```js
27
+ const elem1 = await nonUi5.element.getElementByXPath(".//*[@role='toolbar']/span[3]/span[2]/input");
28
+ ```
29
+
30
+ - Avoid screen size specific and style dependent attributes of elements.
31
+
32
+ !!! danger "avoid"
33
+ ```JavaScript
34
+ [@style='width:100%;']
35
+ ```
36
+
37
+ - Refrain from building manual xPaths at all costs. Indexes can change very easy if the path changes etc.
38
+
39
+ !!! danger "avoid"
40
+ ```JavaScript
41
+ const SupplierPartyId = childrenArray[5].getElementsByTagName("SPAN")[1].innerText;
42
+ ```
43
+
44
+ - Always check for a stable IDs or for other unique properties/attributes. Even if spotlight doesn't offer it to you.
File without changes
@@ -0,0 +1,252 @@
1
+ Data can be imported from *JSON* files and used in the tests. Data obtained during the test run can also be saved or exported to *JSON* files.
2
+
3
+ ## Importing Data
4
+
5
+ ### Test configuration for import
6
+ Data to be imported may be in a specific *JSON* file, or in a directory, and sub-directories under that directory. In the *config* file, under ``params`` add ``import``, and within it specify the import data.
7
+ ```js title="config.js"
8
+ params: {
9
+
10
+ import: {
11
+ myFolder1: "./data/my/folder/data/<systemName>",
12
+ myFolder2: "data/another/folder/data/anotherFolder",
13
+ myFolder3: "C:/Users/C1234567/data/testFolder",
14
+ yourPrefixSpecName: "./data/my/folder/data/<systemName>/data.json",
15
+ uiUser: "./data/my/folder/data/<systemName>/webUser.json",
16
+ }
17
+
18
+ },
19
+ ```
20
+
21
+ !!! info
22
+ The folders or *JSON* files are specified as key-value pair. In this sample, ``yourPrefixSpecName`` is the key which points to a *JSON* file ``./data/my/folder/data/qs9/data.json``. Similarly, the key ``myFolder2`` points to a folder ``data/another/folder/data/anotherFolder``.
23
+
24
+
25
+ !!! tip
26
+ File paths may be relative or absolute. If relative, they are resolved relative to the config file directory.
27
+
28
+
29
+
30
+ ### Data import from *JSON* file
31
+ Data from a *JSON* file will be added under the specified key. Data from ``./data/my/folder/data/<systemName>/data.json`` will be available in ``browser.config.params.import.yourPrefixSpecName``.
32
+
33
+ Similarly data from ``./data/my/folder/data/<systemName>/webUser.json`` will be available in ``browser.config.params.import.uiUser``.
34
+
35
+ ```js title="config.js"
36
+ params: {
37
+
38
+ import: {
39
+ yourPrefixSpecName: "./data/my/folder/data/<systemName>/data.json",
40
+ uiUser: "./data/my/folder/data/<systemName>/webUser.json",
41
+ }
42
+
43
+ },
44
+ ```
45
+
46
+ Let's assume the file *./data/my/folder/data/<systemName>/data.json* has the following data:
47
+ ```json title="data.json"
48
+ {
49
+ "name": "Joe Doe",
50
+ "email": "joe.doe@example.com",
51
+ "telephone": "333-000-111-222",
52
+ "amount": 4.79,
53
+ "website": "http://www.userdataexport.test"
54
+ }
55
+ ```
56
+ Since ``yourPrefixSpecName`` is the key, to access the ``email`` value in the test spec, we use
57
+ ``browser.config.params.import.yourPrefixSpecName.email``.
58
+
59
+ ```js
60
+ // load entire data from "./data/my/folder/data/<systemName>/data.json"
61
+ const userData = browser.config.params.import.yourPrefixSpecName;
62
+
63
+ // make an assertion to ensure data was loaded
64
+ await common.assertion.expectDefined(userData);
65
+ await common.assertion.expectDefined(userData.email);
66
+
67
+ // enter data to input field
68
+ await ui5.userInteraction.fill(emailSelector, userData.email);
69
+
70
+ // define and enter specific data value
71
+ const email = browser.config.params.import.yourPrefixSpecName.email;
72
+ await ui5.userInteraction.fill(emailSelector, email);
73
+ ```
74
+
75
+ ### Data import from folders and subfolders
76
+ ```js title="config.js"
77
+ params: {
78
+
79
+ import: {
80
+ myFolder1: "./data/my/folder/data/<systemName>",
81
+ myFolder2: "./data/another/folder/data/anotherFolder",
82
+ }
83
+
84
+ },
85
+ ```
86
+ Let us assume that the folders have the following files:
87
+ ```code
88
+ ./data/my/folder/data/<systemName>/data.json
89
+ ./data/my/folder/data/<systemName>/purchaseRequisition.json
90
+ ./data/another/folder/data/anotherFolder/purchaseRequisition_HI.json
91
+ ./data/another/folder/data/anotherFolder/purchaseOrders/ServicePurchaseOrder.json
92
+ ./data/another/folder/data/anotherFolder/purchaseOrders/LimitPurchaseOrder.json
93
+ ./data/another/folder/data/anotherFolder/my-po-set/Limit-Purchase-Order.json
94
+ ./data/another/folder/data/anotherFolder/my-po-set/ServicePurchaseOrder.2Items.json
95
+ ```
96
+
97
+ - Data in *./data/my/folder/data/<systemName>/data.json* can be accessed using the folder key ``myFolder1``, and the file name prefix, which is ``data`` in this case.
98
+ ```js
99
+ const myData = browser.config.params.import.myFolder1.data;
100
+ ```
101
+
102
+ - Data in *./data/my/folder/data/<systemName>/purchaseRequisition.json* can be accessed using the folder key ``myFolder1``, and
103
+ the file name prefix ``purchaseRequisition``.
104
+ ```js
105
+ const prData = browser.config.params.import.myFolder1.purchaseRequisition;
106
+ ```
107
+
108
+ - Similarly data in *data/another/folder/data/anotherFolder* can be accessed using the folder key ``myFolder2``, subfolder name and file prefix as keys.
109
+ ```js title="data/another/folder/data/anotherFolder/purchaseRequisition_HI.json"
110
+ const prDataHI = browser.config.params.import.myFolder2.purchaseRequisition_HI;
111
+ ```
112
+ ```js title="data/another/folder/data/anotherFolder/purchaseOrders/ServicePurchaseOrder.json"
113
+ const servicePO = browser.config.params.import.myFolder2.purchaseOrders.ServicePurchaseOrder;
114
+ ```
115
+ ```js title="data/another/folder/data/anotherFolder/purchaseOrders/LimitPurchaseOrder.json"
116
+ const limitPO = browser.config.params.import.myFolder2.purchaseOrders.LimitPurchaseOrder;
117
+ ```
118
+
119
+ !!! tip
120
+ It is recommended that the subfolder names and *JSON* file names used to import data do not have special characters.
121
+
122
+ If there are special characters in the file name or subfolder name, then the dot notation can't be used. In that case, you can use the following syntax:
123
+ ```js title="data/another/folder/data/anotherFolder/my-po-set/Limit-Purchase-Order.json"
124
+ const limitPO = browser.config.params.import.myFolder2["my-po-set"]["Limit-Purchase-Order"];
125
+ ```
126
+ ```js title="data/another/folder/data/anotherFolder/my-po-set/ServicePurchaseOrder.2Items.json"
127
+ const servicePO = browser.config.params.import.myFolder2["my-po-set"]["ServicePurchaseOrder.2Items"];
128
+ ```
129
+
130
+ !!! warning
131
+ Qmate will not fail if import files and folders are missing, not readable, or have an invalid *JSON*. In such cases, warnings are logged on the console, and the data corresponding to those files and folders will remain undefined.
132
+
133
+
134
+ ## Exporting Data
135
+
136
+ ### Test configuration for export
137
+ Data can be exported persistently to one or more files. In the sample configuration file under ``params``, add ``export`` with a list of file names as shown below.
138
+ ```js title="config.js"
139
+ params: {
140
+
141
+ export: {
142
+ exportData: "./data/my/folder/path/in/exportFile.json",
143
+ exportMoreData: "./data/my/folder/path/in/moreDataFile.json",
144
+ exportArrayData: "./data/my/folder/path/in/arrayDataFile.json"
145
+ }
146
+
147
+ },
148
+ ```
149
+
150
+ ### Exporting data to *JSON* file
151
+ The data in ``browser.config.params.export`` is written to output files at the end of the test run. The export file is created or overwritten if already present. Any intermediate folders are also created if necessary.
152
+ ```js
153
+ it("Step 01: export data to 'exportData' file", async function () {
154
+ // exportData: "./data/my/folder/path/in/exportFile.json",
155
+ browser.config.params.export.exportData = {
156
+ name: "joe",
157
+ description: "exported into exportFile.json"
158
+ };
159
+ });
160
+
161
+ it("Step 02: export data to 'exportMoreData' file", async function () {
162
+ // exportMoreData: "./data/my/folder/path/in/moreDataFile.json",
163
+ browser.config.params.export.exportMoreData = {
164
+ name: "jack",
165
+ description: "exported into moreDataFile.json"
166
+ };
167
+ });
168
+
169
+ it("Step 03: export array data to 'exportArrayData' file", async function () {
170
+ // exportArrayData: "./data/my/folder/path/in/arrayDataFile.json",
171
+ browser.config.params.export.exportArrayData = [
172
+ {
173
+ itemNo: "1",
174
+ description: "exported into arrayDataFile.json",
175
+ type: "material"
176
+ },
177
+ {
178
+ itemNo: "2",
179
+ description: "Service item",
180
+ type: "service"
181
+ }
182
+ ];
183
+ await common.assertion.expectEqual(Array.isArray(browser.config.params.export.exportArrayData), true);
184
+
185
+ // file "./data/my/folder/path/in/arrayDataFile.json" will have the array data
186
+ // check manually after test run is complete
187
+ });
188
+
189
+ ```
190
+ At the end of the test run, the file *./data/my/folder/path/in/exportFile.json* will look like:
191
+ ```json
192
+ {
193
+ name: "joe",
194
+ description: "exported into exportFile.json"
195
+ }
196
+ ```
197
+
198
+ File *./data/my/folder/path/in/moreDataFile.json* will look like:
199
+ ```json
200
+ {
201
+ name: "jack",
202
+ description: "exported into moreDataFile.json"
203
+ }
204
+ ```
205
+
206
+ File *./data/my/folder/path/in/moreDataFile.json* will look like:
207
+ ```json
208
+ [
209
+ {
210
+ itemNo: "1",
211
+ description: "exported into arrayDataFile.json",
212
+ type: "material"
213
+ },
214
+ {
215
+ itemNo: "2",
216
+ description: "Service item",
217
+ type: "service"
218
+ }
219
+ ]
220
+ ```
221
+
222
+ !!! warning
223
+ If you are adding export data to the same export key, make sure you don't overwrite previous data.
224
+
225
+ ```js
226
+ // browser.config.params.export.exportData has data assigned to it from previous steps
227
+ browser.config.params.export.exportData["name"] = "joe";
228
+
229
+ // or if browser.config.params.export.exportData is an Array
230
+ if(Array.isArray(browser.config.params.export.exportData)) {
231
+ // add an element to an array
232
+ browser.config.params.export.exportData.push({name: "Joe"});
233
+ }
234
+ ```
235
+
236
+ !!! info
237
+ If there is no data assigned to an export key, then the export file corresponding to that key will be empty. Doing any of the following things will create an empty *JSON* file.
238
+
239
+ ```js
240
+ // "./data/my/folder/path/in/exportFile.json" will be a blank file at end of test run
241
+ browser.config.params.export.exportData = null;
242
+ // or
243
+ browser.config.params.export.exportData = {};
244
+ // or
245
+ browser.config.params.export.exportData = [];
246
+ ```
247
+
248
+ ### Parallel test runs and exporting data
249
+ If tests are running in parallel, there will be multiple browser instances and each test run will have it's own browser instance. If these parallel instances write to the same export file, then the export data from parallel test instances is merged together and written out to the file.
250
+
251
+ !!! warning
252
+ This module does not fail if export files are not writable or if the export folder is inaccessible. In such cases, warnings are logged on the console, and export data will not get written out.