Skip to content

Commit fd7b99e

Browse files
feat: support cap basic auth
intended for local dev! closes #502
1 parent 0f77e3b commit fd7b99e

18 files changed

+6734
-895
lines changed

.gitmodules

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
[submodule "examples/cap-bookshop-wdi5"]
2+
path = examples/cap-bookshop-wdi5
3+
url = [email protected]:SAP-samples/cap-bookshop-wdi5.git
4+
branch = wdi5-tests

client-side-js/injectUI5.cjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ async function clientSide_injectUI5(config, waitForUI5Timeout, browserInstance)
251251
} while ((currentObj = Object.getPrototypeOf(currentObj)))
252252

253253
// filter for:
254-
// @ts-ignore
254+
// @ts-expect-error - TS doesn't know that the keys are strings
255255
let controlMethodsToProxy = [...properties.keys()].filter((item) => {
256256
if (typeof control[item] === "function") {
257257
// function

docs/authentication.md

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,17 @@
1010

1111
Generally speaking, the authentication behavior mimicks that of a regular user session: first, the `baseUrl` (from the `wdio.conf.(j|t)s`-file) is opened in the configured browser. Then, the redirect to the Authentication provider is awaited and [the credentials](#credentials) are supplied.
1212

13-
BTP-, IAS-, Office365- and custom IdP all supply credentials as a user would, meaning they're literally typed into the respective input fields on each login screen.
13+
BTP-, IAS-, Office365- and custom IdP all supply credentials as a user would, meaning they're literally typed into the respective input fields on each login screen.
1414
Basic Authentication prepends username and password in encoded form to the URL, resulting in an `HTTP` `GET` in the form of `https://username:[email protected]`.
1515

1616
!> Multi-Factor Authentication is not supported as it's nearly impossible to manage any media break (e.g. browser ↔ mobile) in authentication flows out of the box
1717

18-
For you as users, authentication is done at design-time, meaning: **by configuration only, not programmatically**.
18+
For you as users, authentication is done at design-time, meaning: **by configuration only, not programmatically**.
1919
This especially means that no changes in the test code are needed for using authentication in `wdi5` tests!
2020

2121
?> No [skipping of the UI5 injection](configuration#skipinjectui5onstart) is necessary, `wdi5` takes care of the correct order of operation (first authentication, then injecting UI5) itself.
2222

23-
!> Credentials can only be supplied via environment variables, not in any configuration file.
23+
!> Credentials can only be supplied via environment variables, not in any configuration file.
2424
More on the [how and why below](#credentials) :point_down:
2525

2626
## Configuration
@@ -127,8 +127,8 @@ The `BTP` authenticator will automatically detect whether the login process is a
127127

128128
?> only available in `wdi5` >= 2
129129

130-
Using the 'Identity Authentication Service (IAS) Authenticator' in `wdi5` is a subset of the [above BTP Authentication](#sap-cloud-idp-default-btp-identity-provider).
131-
It takes the same configuration options, plus `disableBiometricAuth` (default: `true`, which you want in almost all cases) and `idpDomain`. The latter is necessary to satisfy cookie conditions in the remote-controlled browser.
130+
Using the 'Identity Authentication Service (IAS) Authenticator' in `wdi5` is a subset of the [above BTP Authentication](#sap-cloud-idp-default-btp-identity-provider).
131+
It takes the same configuration options, plus `disableBiometricAuth` (default: `true`, which you want in almost all cases) and `idpDomain`. The latter is necessary to satisfy cookie conditions in the remote-controlled browser.
132132
Set `idpDomain` to the _domain-only_ part of your IAS tenant URL, e.g. `weiruhg.accounts.ondemand.com`, _omitting_ the protocol prefix (`https://`).
133133

134134
!> If `disableBiometricAuth` is set to `true`, `idpDomain` must be set as well!
@@ -310,7 +310,8 @@ baseUrl: "https://caution_your-deployed-ui5-with-basic-auth.app",
310310
capabilities: {
311311
// browserName: "..."
312312
"wdi5:authentication": {
313-
provider: "BasicAuth" //> mandatory
313+
provider: "BasicAuth", //> mandatory
314+
basicAuthUrls: ["https://your-custom-basic-auth-endpoint"] //> optional: default is the configured `baseUrl`
314315
}
315316
}
316317
```
@@ -325,7 +326,8 @@ capabilities: {
325326
capabilities: {
326327
// browserName: "..."
327328
"wdi5:authentication": {
328-
provider: "BasicAuth" //> mandatory
329+
provider: "BasicAuth", //> mandatory
330+
basicAuthUrls: ["https://your-custom-basic-auth-endpoint"] //> optional: default is the configured `baseUrl`
329331
}
330332
}
331333
},
@@ -334,15 +336,31 @@ capabilities: {
334336
capabilities: {
335337
// browserName: "..."
336338
"wdi5:authentication": {
337-
provider: "BasicAuth" //> mandatory
339+
provider: "BasicAuth", //> mandatory
340+
basicAuthUrls: ["https://your-custom-basic-auth-endpoint"] //> optional: default is the configured `baseUrl`
338341
}
339342
}
340343
}
341344
}
342345
```
343346

347+
#### CAP Authentication (only during development!)
348+
During development it is common to use basic authentication to mock users. In contrast to deployed applications we have to authenticate ourself directly to the exposed OData enpoints and not to the application itself. For that reason you have to configure the `basicAuthUrls` which should point to the OData `$metadata`.
349+
350+
```js
351+
capabilities: {
352+
// browserName: "..."
353+
"wdi5:authentication": {
354+
provider: "BasicAuth", //> mandatory
355+
basicAuthUrls: ["http://localhost:4004/odata/v4/myEndpoint/$metadata", "http://localhost:4004/odata/v4/myOtherEndpoint/$metadata"]
356+
}
357+
}
358+
```
359+
If you have multiple OData endpoints you have to declare every single OData endpoint in the `basicAuthUrls` array.
360+
344361
<!-- tabs:end -->
345362

363+
346364
## Credentials
347365

348366
Exposing credentials in configuration files that were accidentally checked into version control is one of the most common causes of data leaks. That's why `wdi5` only allows providing credentials through environment variables at runtime.
@@ -351,7 +369,7 @@ Exposing credentials in configuration files that were accidentally checked into
351369

352370
There are multiple ways to achieve that in Node.js, with [using the `dotenv`-module](https://www.npmjs.com/package/dotenv) being one of the most popular: `dotenv` automatically transfers all variables from a `.env`-file into the environment of the app at runtime.
353371

354-
In single browser scenarios, `wdi5_username` and `wdi5_password` need to be provided.
372+
In single browser scenarios, `wdi5_username` and `wdi5_password` need to be provided.
355373
In multiremote scenarios, credential keys in the environment adhere to `wdi5_$browserInstanceName_username` and `wdi5_$browserInstanceName_password`.
356374

357375
<!-- tabs:start -->
@@ -381,5 +399,5 @@ wdi5_nix_password='dmac'
381399

382400
## Miscellaneous
383401

384-
Why the `wdi5:...` prefix?
402+
Why the `wdi5:...` prefix?
385403
Because the W3C standard for providing options in the WebDriver protocol asks for any vendor-specfic setting to have a unique prefix.

examples/cap-bookshop-wdi5

Submodule cap-bookshop-wdi5 added at ae62c54

examples/ui5-ts-app/test/e2e/Basic.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ describe("Basic", async () => {
3131
const view = await (browser.asControl(selector) as unknown as Page).getParent()
3232
const controller: Controller = await (view as View).getController()
3333

34-
// @ts-ignore this async fn lives in an not properly typed controller
34+
// @ts-expect-error this async fn lives in an not properly typed controller
3535
const number = await controller.asyncFn()
3636
expect(number).toEqual(10)
3737
})
@@ -49,7 +49,7 @@ describe("Basic", async () => {
4949
const view: unknown = await (browser.asControl(selector) as unknown as Page).getParent()
5050
const controller: Controller = await (view as View).getController()
5151

52-
// @ts-ignore this async fn lives in an not properly typed controller
52+
// @ts-expect-error this async fn lives in an not properly typed controller
5353
await controller.asyncRejectFn()
5454
expect(
5555
(Logger.error as sinon.SinonSpy).calledWith('call of asyncRejectFn failed because of: "meh"')

examples/ui5-ts-app/test/e2e/Input.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ describe("Input", async () => {
2525
it("should retrieve the webcomponent's bound path via a managed object", async () => {
2626
const control = await browser.asControl(inputSelector)
2727
const bindingInfo = await control.getBindingInfo("value")
28-
// @ts-ignore
28+
// @ts-expect-error "parts" is not part of the type definition
2929
const parts = await bindingInfo.parts
3030
expect(parts[0].path).toEqual("/Customers('TRAIH')/ContactName")
3131
})

examples/ui5-ts-app/test/e2e/MultiInput.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ describe("MultiInput", async () => {
1111
interaction: "root"
1212
}
1313
}
14-
// @ts-ignore
14+
// @ts-expect-error we'd need to properly type the multi input control
1515
await (browser.asControl(multiInputSelector) as unknown as MultiInput).enterText("123")
1616

1717
const multiInput = await browser.asControl<MultiInput>(multiInputSelector)

examples/ui5-ts-app/test/e2e/multiremote.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ describe("Basic", async () => {
99
viewName: "test.Sample.tsapp.view.Main"
1010
}
1111
}
12-
// @ts-ignore
12+
// @ts-expect-error browser ref ("two") is not properly typed
1313
const allButtonsTwo = await browser.two.allControls<Button>(allButtonsSelector)
14-
// @ts-ignore
14+
// @ts-expect-error browser ref ("one") is not properly typed
1515
const allButtonsOne = await browser.one.allControls<Button>(allButtonsSelector)
1616
expect(allButtonsTwo.length).toEqual(1)
1717
expect(allButtonsOne.length).toEqual(1)

examples/ui5-ts-app/test/e2e/protocol/wdio-ui5-devtools.conf.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ export const config: wdi5Config = {
77
services: ["ui5"],
88
specs: [resolve("test/e2e/protocol/*.test.ts")],
99
wdi5: {
10-
logLevel: "verbose"
10+
logLevel: "verbose",
11+
waitForUI5Timeout: 25000
1112
},
1213
automationProtocol: "devtools",
1314
capabilities: [

examples/ui5-ts-app/test/e2e/protocol/wdio-ui5-webdriver.conf.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ export const config: wdi5Config = {
55
baseUrl: "https://wdi5-sample-app.cfapps.eu20.hana.ondemand.com/no-auth/",
66
services: ["ui5"],
77
wdi5: {
8-
logLevel: "verbose"
8+
logLevel: "verbose",
9+
waitForUI5Timeout: 25000
910
},
1011
specs: [resolve("test/e2e/protocol/*.test.ts")],
1112
capabilities: [

examples/ui5-ts-app/webapp/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<!DOCTYPE html>
1+
<!doctype html>
22
<html>
33
<head>
44
<meta http-equiv="Cache-control" content="no-cache, no-store, must-revalidate" />

0 commit comments

Comments
 (0)