diff --git a/.github/workflows/wdi5-tests_auth.yml b/.github/workflows/wdi5-tests_auth.yml index e6bd38ac..644bb4b6 100644 --- a/.github/workflows/wdi5-tests_auth.yml +++ b/.github/workflows/wdi5-tests_auth.yml @@ -49,6 +49,7 @@ jobs: uses: actions/checkout@v3 with: ref: ${{ github.event.pull_request.head.sha || github.ref }} + submodules: recursive - name: use node ${{ matrix.node-version }} uses: actions/setup-node@v3 @@ -67,6 +68,11 @@ jobs: - name: build run: npm run build + # this against the submodule cloned from https://github.com/SAP-samples/cap-bookshop-wdi5 + - name: local CAP authentication + run: npm run test:capAuth + + # this against deployed TS sample app on BTP - name: (browserstack) btp/sap cloud id, basic auth, office 365, custom auth run: BROWSERSTACK=true npm run test:auth diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..f6ab8c17 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "examples/cap-bookshop-wdi5"] + path = examples/cap-bookshop-wdi5 + url = git@github.com:SAP-samples/cap-bookshop-wdi5.git + branch = wdi5-tests diff --git a/CHANGELOG.md b/CHANGELOG.md index a3b31bde..51db7e1d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,19 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [2.0.0-rc.0](https://github.com/ui5-community/wdi5/compare/v2.0.0-beta.1...v2.0.0-rc.0) (2023-09-08) + + +### Features + +* support cap basic auth ([fd7b99e](https://github.com/ui5-community/wdi5/commit/fd7b99e8945742ea3cf106f948e3f83e5d97064d)), closes [#502](https://github.com/ui5-community/wdi5/issues/502) +* wdi5^2 treats ([#535](https://github.com/ui5-community/wdi5/issues/535)) ([a186d14](https://github.com/ui5-community/wdi5/commit/a186d143201568d57d6ab57a3379196de84cdf08)) + + ### Bug Fixes -* safeguard devtools protocol stale web element ref ([#524](https://github.com/ui5-community/wdi5/issues/524)) ([ece986f](https://github.com/ui5-community/wdi5/commit/ece986f16f10426370fad4eb6d79b5f983262ab6)) +* remove it.only from stale element tests ([7bb89f1](https://github.com/ui5-community/wdi5/commit/7bb89f12f83ecc96acfee2f4fe55773cf72856c5)) + ## [2.0.0-beta.1](https://github.com/ui5-community/wdi5/compare/v2.0.0-beta.0...v2.0.0-beta.1) (2023-08-29) diff --git a/client-side-js/injectUI5.cjs b/client-side-js/injectUI5.cjs index d4e5006c..0474c578 100644 --- a/client-side-js/injectUI5.cjs +++ b/client-side-js/injectUI5.cjs @@ -252,7 +252,7 @@ async function clientSide_injectUI5(config, waitForUI5Timeout, browserInstance) } while ((currentObj = Object.getPrototypeOf(currentObj))) // filter for: - // @ts-ignore + // @ts-expect-error - TS doesn't know that the keys are strings let controlMethodsToProxy = [...properties.keys()].filter((item) => { if (typeof control[item] === "function") { // function diff --git a/docs/authentication.md b/docs/authentication.md index 7748c628..479d11b4 100644 --- a/docs/authentication.md +++ b/docs/authentication.md @@ -10,17 +10,17 @@ 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. -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. +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. Basic Authentication prepends username and password in encoded form to the URL, resulting in an `HTTP` `GET` in the form of `https://username:encoded-pwd@your-deployed-UI5.app`. !> 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 -For you as users, authentication is done at design-time, meaning: **by configuration only, not programmatically**. +For you as users, authentication is done at design-time, meaning: **by configuration only, not programmatically**. This especially means that no changes in the test code are needed for using authentication in `wdi5` tests! ?> 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. -!> Credentials can only be supplied via environment variables, not in any configuration file. +!> Credentials can only be supplied via environment variables, not in any configuration file. More on the [how and why below](#credentials) :point_down: ## Configuration @@ -127,8 +127,8 @@ The `BTP` authenticator will automatically detect whether the login process is a ?> only available in `wdi5` >= 2 -Using the 'Identity Authentication Service (IAS) Authenticator' in `wdi5` is a subset of the [above BTP Authentication](#sap-cloud-idp-default-btp-identity-provider). -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. +Using the 'Identity Authentication Service (IAS) Authenticator' in `wdi5` is a subset of the [above BTP Authentication](#sap-cloud-idp-default-btp-identity-provider). +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. Set `idpDomain` to the _domain-only_ part of your IAS tenant URL, e.g. `weiruhg.accounts.ondemand.com`, _omitting_ the protocol prefix (`https://`). !> 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", capabilities: { // browserName: "..." "wdi5:authentication": { - provider: "BasicAuth" //> mandatory + provider: "BasicAuth", //> mandatory + basicAuthUrls: ["https://your-custom-basic-auth-endpoint"] //> optional: default is the configured `baseUrl` } } ``` @@ -325,7 +326,8 @@ capabilities: { capabilities: { // browserName: "..." "wdi5:authentication": { - provider: "BasicAuth" //> mandatory + provider: "BasicAuth", //> mandatory + basicAuthUrls: ["https://your-custom-basic-auth-endpoint"] //> optional: default is the configured `baseUrl` } } }, @@ -334,15 +336,31 @@ capabilities: { capabilities: { // browserName: "..." "wdi5:authentication": { - provider: "BasicAuth" //> mandatory + provider: "BasicAuth", //> mandatory + basicAuthUrls: ["https://your-custom-basic-auth-endpoint"] //> optional: default is the configured `baseUrl` } } } } ``` +#### CAP Authentication (only during development!) +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`. + +```js +capabilities: { + // browserName: "..." + "wdi5:authentication": { + provider: "BasicAuth", //> mandatory + basicAuthUrls: ["http://localhost:4004/odata/v4/myEndpoint/$metadata", "http://localhost:4004/odata/v4/myOtherEndpoint/$metadata"] + } +} +``` +If you have multiple OData endpoints you have to declare every single OData endpoint in the `basicAuthUrls` array. + + ## Credentials 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 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. -In single browser scenarios, `wdi5_username` and `wdi5_password` need to be provided. +In single browser scenarios, `wdi5_username` and `wdi5_password` need to be provided. In multiremote scenarios, credential keys in the environment adhere to `wdi5_$browserInstanceName_username` and `wdi5_$browserInstanceName_password`. @@ -381,5 +399,5 @@ wdi5_nix_password='dmac' ## Miscellaneous -Why the `wdi5:...` prefix? +Why the `wdi5:...` prefix? Because the W3C standard for providing options in the WebDriver protocol asks for any vendor-specfic setting to have a unique prefix. diff --git a/examples/cap-bookshop-wdi5 b/examples/cap-bookshop-wdi5 new file mode 160000 index 00000000..ae62c547 --- /dev/null +++ b/examples/cap-bookshop-wdi5 @@ -0,0 +1 @@ +Subproject commit ae62c547f6e4e42c93578b204fd3b48d043f68a5 diff --git a/examples/ui5-ts-app/test/e2e/Basic.test.ts b/examples/ui5-ts-app/test/e2e/Basic.test.ts index 26a0ad66..afb6b175 100644 --- a/examples/ui5-ts-app/test/e2e/Basic.test.ts +++ b/examples/ui5-ts-app/test/e2e/Basic.test.ts @@ -31,7 +31,7 @@ describe("Basic", async () => { const view = await (browser.asControl(selector) as unknown as Page).getParent() const controller: Controller = await (view as View).getController() - // @ts-ignore this async fn lives in an not properly typed controller + // @ts-expect-error this async fn lives in an not properly typed controller const number = await controller.asyncFn() expect(number).toEqual(10) }) @@ -49,7 +49,7 @@ describe("Basic", async () => { const view: unknown = await (browser.asControl(selector) as unknown as Page).getParent() const controller: Controller = await (view as View).getController() - // @ts-ignore this async fn lives in an not properly typed controller + // @ts-expect-error this async fn lives in an not properly typed controller await controller.asyncRejectFn() expect( (Logger.error as sinon.SinonSpy).calledWith('call of asyncRejectFn failed because of: "meh"') diff --git a/examples/ui5-ts-app/test/e2e/Input.test.ts b/examples/ui5-ts-app/test/e2e/Input.test.ts index eae03a25..4925e131 100644 --- a/examples/ui5-ts-app/test/e2e/Input.test.ts +++ b/examples/ui5-ts-app/test/e2e/Input.test.ts @@ -25,7 +25,7 @@ describe("Input", async () => { it("should retrieve the webcomponent's bound path via a managed object", async () => { const control = await browser.asControl(inputSelector) const bindingInfo = await control.getBindingInfo("value") - // @ts-ignore + // @ts-expect-error "parts" is not part of the type definition const parts = await bindingInfo.parts expect(parts[0].path).toEqual("/Customers('TRAIH')/ContactName") }) diff --git a/examples/ui5-ts-app/test/e2e/MultiInput.test.ts b/examples/ui5-ts-app/test/e2e/MultiInput.test.ts index fca73081..2d67accb 100644 --- a/examples/ui5-ts-app/test/e2e/MultiInput.test.ts +++ b/examples/ui5-ts-app/test/e2e/MultiInput.test.ts @@ -11,7 +11,7 @@ describe("MultiInput", async () => { interaction: "root" } } - // @ts-ignore + // @ts-expect-error we'd need to properly type the multi input control await (browser.asControl(multiInputSelector) as unknown as MultiInput).enterText("123") const multiInput = await browser.asControl(multiInputSelector) diff --git a/examples/ui5-ts-app/test/e2e/multiremote.test.ts b/examples/ui5-ts-app/test/e2e/multiremote.test.ts index 35d793cc..55df4a96 100644 --- a/examples/ui5-ts-app/test/e2e/multiremote.test.ts +++ b/examples/ui5-ts-app/test/e2e/multiremote.test.ts @@ -9,9 +9,9 @@ describe("Basic", async () => { viewName: "test.Sample.tsapp.view.Main" } } - // @ts-ignore + // @ts-expect-error browser ref ("two") is not properly typed const allButtonsTwo = await browser.two.allControls