├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ └── bug_report.md
└── workflows
│ ├── build-docker-standalone-images.yml
│ ├── close-inactive-issues.yml
│ ├── codeql-analysis.yml
│ ├── gh-release.yml
│ ├── lint.yml
│ ├── npm-publish.yml
│ ├── publish-btp.yml
│ ├── wdi5-tests_auth.yml
│ ├── wdi5-tests_core.yml
│ ├── wdi5-tests_fe-app.yml
│ ├── wdi5-tests_js-app.yml
│ └── wdi5-tests_ts-app.yml
├── .gitignore
├── .gitmodules
├── .husky
├── .gitignore
├── commit-msg
└── pre-commit
├── .mocharc.json
├── .nvmrc
├── .prettierignore
├── .prettierrc
├── .retrofit-pkg-json.js
├── .vscode
├── launch.json
└── settings.json
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── SUPPORT.md
├── client-side-js
├── _checkForUI5Ready.cjs
├── _getAggregation.cjs
├── _navTo.cjs
├── allControls.cjs
├── executeControlMethod.cjs
├── executeObjectMethod.cjs
├── fireEvent.cjs
├── getControl.cjs
├── getObject.cjs
├── getSelectorForElement.cjs
├── getUI5Version.cjs
├── injectTools.cjs
├── injectUI5.cjs
├── injectXHRPatch.cjs
├── interactWithControl.cjs
└── testLibrary.cjs
├── commitlint.config.cjs
├── docker
├── Dockerfile.standalone
├── Dockerfile.wdi5
├── docker-compose-standalone.yaml
├── docker-compose.yaml
├── package-app.json
├── package-standalone.json
├── package-wdi5-dev.json
└── package-wdi5.json
├── docs
├── .gitignore
├── .nojekyll
├── README.md
├── _sidebar.md
├── android-chrome-192x192.png
├── android-chrome-512x512.png
├── apple-touch-icon.png
├── authentication.md
├── configuration.md
├── contributing.md
├── debugging.md
├── docker.md
├── favicon-16x16.png
├── favicon-32x32.png
├── favicon.ico
├── fe-testlib.md
├── img
│ ├── 01_installation.png
│ ├── 05_installation.png
│ ├── 07_installation.png
│ ├── 09_installation.png
│ ├── 20_running.png
│ ├── 23_running.png
│ ├── auto-open-dev-tools.png
│ ├── jsdoc-type-cast-codecompletion.png
│ ├── social-media-wb.png
│ ├── social-media.png
│ ├── vscode-breakpoint.png
│ ├── vscode-debugger-halted.png
│ ├── vscode-js-debug-terminal.png
│ └── wdi5-logo-small.png
├── index.html
├── installation.md
├── locators.md
├── migration.md
├── pageObjects.md
├── presentation
│ ├── wdi5-img
│ │ ├── idea01.png
│ │ ├── idea02.png
│ │ ├── idea03.png
│ │ ├── principle.png
│ │ └── scope.png
│ ├── wdi5-presentation.html
│ ├── wdi5.css
│ └── wdi5.less
├── recipes.md
├── resources.md
├── running.md
├── site.webmanifest
├── test-pyramid.png
├── ui5.md
├── usage.md
├── wdi5-control-instantiation.png
├── wdi5-testing-framework.png
└── wdio.md
├── examples
├── cucumber
│ ├── features
│ │ ├── login.feature
│ │ ├── pageobjects
│ │ │ ├── login.page.js
│ │ │ ├── page.js
│ │ │ └── secure.page.js
│ │ └── step-definitions
│ │ │ └── steps.js
│ ├── package.json
│ └── wdio.conf.js
├── fe-app
│ ├── .eslintrc.cjs
│ ├── .gitignore
│ ├── jsconfig.json
│ ├── karma.conf.js
│ ├── package.json
│ ├── ui5.yaml
│ ├── wdio.conf.js
│ └── webapp
│ │ ├── Component.js
│ │ ├── ext
│ │ ├── controller
│ │ │ ├── ProcessFlow.controller.js
│ │ │ └── ProcessFlowLanesAndNodes.json
│ │ ├── fragment
│ │ │ ├── CustomSectionGantt.fragment.xml
│ │ │ └── CustomSectionGantt.js
│ │ └── view
│ │ │ └── ProcessFlow.view.xml
│ │ ├── i18n
│ │ └── i18n.properties
│ │ ├── index.html
│ │ ├── localService
│ │ ├── metadata.xml
│ │ └── mockdata
│ │ │ ├── Category.json
│ │ │ ├── IncidentFlow.json
│ │ │ ├── IncidentProcessTimeline.json
│ │ │ ├── IncidentStatus.json
│ │ │ ├── Incidents.json
│ │ │ ├── Individual.json
│ │ │ └── Priority.json
│ │ ├── manifest.json
│ │ ├── test
│ │ ├── flpSandbox.html
│ │ ├── integration
│ │ │ ├── Opa.qunit.js
│ │ │ ├── OpaJourney.js
│ │ │ ├── opaTests.qunit.html
│ │ │ └── pages
│ │ │ │ ├── MainListReport.js
│ │ │ │ └── MainObjectPage.js
│ │ ├── testsuite.qunit.html
│ │ └── testsuite.qunit.js
│ │ └── wdi5-test
│ │ └── Journey.test.js
├── ui5-js-app-esm
│ ├── jsconfig.json
│ ├── package.json
│ ├── ui5.yaml
│ └── webapp
│ │ ├── Component.js
│ │ ├── controller
│ │ ├── App.controller.js
│ │ ├── BaseController.js
│ │ ├── Calendar.controller.js
│ │ ├── Main.controller.js
│ │ └── Other.controller.js
│ │ ├── css
│ │ └── style.css
│ │ ├── i18n
│ │ ├── i18n.properties
│ │ └── i18n_en.properties
│ │ ├── index.html
│ │ ├── manifest.json
│ │ ├── model
│ │ ├── countries.json
│ │ └── models.js
│ │ ├── proxyrc.json
│ │ ├── test
│ │ └── e2e
│ │ │ ├── esm.test.js
│ │ │ └── wdio.conf.js
│ │ └── view
│ │ ├── App.view.xml
│ │ ├── Calendar.view.xml
│ │ ├── Dialog.fragment.xml
│ │ ├── Main.view.xml
│ │ └── Other.view.xml
├── ui5-js-app
│ ├── e2e-test-config
│ │ ├── wdi5-multiversion.js
│ │ ├── wdi5-urlDeprecation.conf.js
│ │ ├── wdio-docker-selenium.conf.js
│ │ ├── wdio-docker-standalone.conf.js
│ │ ├── wdio-docker.conf.js
│ │ ├── wdio-multiremote.conf.js
│ │ ├── wdio-selenium-service.conf.js
│ │ ├── wdio-ui5-late.conf.js
│ │ ├── wdio-ui5tooling.conf.js
│ │ ├── wdio-webserver.conf.js
│ │ └── wdio.base.conf.js
│ ├── jsconfig.json
│ ├── package.json
│ ├── scripts
│ │ ├── delayedMockServer.js
│ │ └── getBrowsers.js
│ ├── ui5.yaml
│ └── webapp
│ │ ├── Component.js
│ │ ├── controller
│ │ ├── App.controller.js
│ │ ├── BaseController.js
│ │ ├── Calendar.controller.js
│ │ ├── Main.controller.js
│ │ └── Other.controller.js
│ │ ├── css
│ │ └── style.css
│ │ ├── i18n
│ │ ├── i18n.properties
│ │ └── i18n_en.properties
│ │ ├── index.html
│ │ ├── localService
│ │ └── metadata.xml
│ │ ├── manifest.json
│ │ ├── model
│ │ ├── countries.json
│ │ └── models.js
│ │ ├── proxyrc.json
│ │ ├── test
│ │ ├── __screenshots__
│ │ │ └── .keepMe
│ │ └── e2e
│ │ │ ├── aggregation.test.js
│ │ │ ├── allControls.test.js
│ │ │ ├── allControlsForce.test.js
│ │ │ ├── basic.test.js
│ │ │ ├── bindingpath-matcher.test.js
│ │ │ ├── checkbox.test.js
│ │ │ ├── combobox.test.js
│ │ │ ├── error-logging.test.js
│ │ │ ├── exec.test.js
│ │ │ ├── fluent-async-api.test.js
│ │ │ ├── generated-methods.test.js
│ │ │ ├── hash-nav.test.js
│ │ │ ├── i18n-matcher.test.js
│ │ │ ├── interaction.test.js
│ │ │ ├── list-interaction.test.js
│ │ │ ├── locators-basic.test.js
│ │ │ ├── locators-regex.test.js
│ │ │ ├── matcher.test.js
│ │ │ ├── multiremote.test.js
│ │ │ ├── object.test.js
│ │ │ ├── pageObjects
│ │ │ ├── ComponentLocator.js
│ │ │ ├── Main.js
│ │ │ ├── Other.js
│ │ │ └── Page.js
│ │ │ ├── planningCalendar.test.js
│ │ │ ├── press.test.js
│ │ │ ├── prop-array.test.js
│ │ │ ├── properties-matcher.test.js
│ │ │ ├── screenshot.test.js
│ │ │ ├── ui5-late.test.js
│ │ │ └── wdioBridge.test.js
│ │ └── view
│ │ ├── App.view.xml
│ │ ├── Calendar.view.xml
│ │ ├── Dialog.fragment.xml
│ │ ├── Main.view.xml
│ │ └── Other.view.xml
└── ui5-ts-app
│ ├── .eslintrc.cjs
│ ├── .gitignore
│ ├── LICENSE
│ ├── approuter
│ ├── package.json
│ └── xs-app.json
│ ├── destinations.json
│ ├── mta.yaml
│ ├── package.json
│ ├── test
│ ├── e2e
│ │ ├── Authentication.test.ts
│ │ ├── AuthenticationCert.test.ts
│ │ ├── Basic.test.ts
│ │ ├── BasicMultiRemoteAuthentication.test.ts
│ │ ├── Custom.test.ts
│ │ ├── Input.test.ts
│ │ ├── MultiInput.test.ts
│ │ ├── Navigation.test.ts
│ │ ├── authentication
│ │ │ ├── wdio-base.conf.ts
│ │ │ ├── wdio-basic-auth-authentication.conf.ts
│ │ │ ├── wdio-btp-authentication-multiremote.conf.ts
│ │ │ ├── wdio-btp-authentication.conf.ts
│ │ │ ├── wdio-cert-authentication.conf.ts
│ │ │ └── wdio-custom-authentication.conf.ts
│ │ ├── cloud-services
│ │ │ ├── browserstack.conf.local.ts
│ │ │ ├── saucelabs.conf.local.ts
│ │ │ └── shared.conf.ts
│ │ ├── invalidate-cache.test.ts
│ │ ├── multiremote.test.ts
│ │ ├── pageObjects
│ │ │ ├── Other.ts
│ │ │ └── Page.ts
│ │ ├── protocol
│ │ │ ├── Protocol.test.ts
│ │ │ ├── stale-element.test.ts
│ │ │ ├── wdio-ui5-devtools.conf.ts
│ │ │ └── wdio-ui5-webdriver.conf.ts
│ │ ├── ui5-late.test.ts
│ │ └── workzone
│ │ │ ├── regular-journey.test.ts
│ │ │ ├── testlib-journey.test.ts
│ │ │ └── wdio-ui5-workzone.conf.ts
│ └── tsconfig.json
│ ├── tsconfig.json
│ ├── ui5.yaml
│ ├── wdio-ui5-late.conf.ts
│ ├── wdio-ui5.conf.ts
│ └── webapp
│ ├── Component.ts
│ ├── controller
│ ├── App.controller.ts
│ ├── BaseController.ts
│ ├── Main.controller.ts
│ └── Other.controller.ts
│ ├── i18n
│ ├── i18n.properties
│ ├── i18n_de.properties
│ └── i18n_en.properties
│ ├── index.html
│ ├── manifest.json
│ └── view
│ ├── App.view.xml
│ ├── Dialog.fragment.xml
│ ├── Main.view.xml
│ └── Other.view.xml
├── package-lock.json
├── package.json
├── src
├── .eslintrc.cjs
├── index.ts
├── launcher.ts
├── lib
│ ├── Logger.ts
│ ├── authentication
│ │ ├── Authenticator.ts
│ │ ├── BTPAuthenticator.ts
│ │ ├── BasicAuthenticator.ts
│ │ ├── CertAuthenticator.ts
│ │ ├── CustomAuthenticator.ts
│ │ └── Office365Authenticator.ts
│ ├── wdi5-bridge.ts
│ ├── wdi5-control.ts
│ ├── wdi5-fe.ts
│ ├── wdi5-object.ts
│ └── wdioApi.ts
├── service.ts
├── types
│ ├── browser-commands.ts
│ └── wdi5.types.ts
└── wdi5.ts
├── test
├── .eslintrc.cjs
├── AuthenticatorCustom.test.ts
├── Logger.test.ts
└── tsconfig.json
├── tsconfig-cjs.json
├── tsconfig.base.json
└── tsconfig.json
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: [js-soft]
4 | # patreon: # Replace with a single Patreon username
5 | # open_collective: # Replace with a single Open Collective username
6 | # ko_fi: # Replace with a single Ko-fi username
7 | # tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | # community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | # liberapay: # Replace with a single Liberapay username
10 | # issuehunt: # Replace with a single IssueHunt username
11 | # otechie: # Replace with a single Otechie username
12 | # lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
13 | # custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
14 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: "help us squat that \U0001FAB3!"
4 | title: ""
5 | labels: ""
6 | assignees: ""
7 | ---
8 |
9 | **Describe the bug**
10 | A clear and concise description of what the bug :bug: is.
11 |
12 | **To Reproduce**
13 | Steps to reproduce the behavior:
14 |
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Logs/Console Output**
24 | if applicable, please copypasta _code-fenced_ log output, e.g.
25 |
26 | ```shell
27 | $> ...
28 | ```
29 |
30 | **Screenshots**
31 | if applicable, add screenshots to help explain your problem.
32 |
33 | **Runtime Env (please complete the following information):**
34 |
35 | - `wdi5/wdio-ui5-service`-version: [e.g. 0.8.1]
36 | - `UI5` version: [e.g. 1.98.0]
37 | - `wdio`-version (output of `wdio --version`): [e.g. 7.16.12]
38 | - `node`-version (output of `node --version`): [e.g. v17.3.0]
39 | - OS: [e.g. Linux openSuSE]
40 | - Browser + Version [e.g. chrome 97.0.4692.71]
41 |
42 | **Additional context**
43 | Add any other context about the problem here,
44 | e.g. any options the target browser is started with like `--headless` or
45 | if the tests run in a CI environment
46 |
--------------------------------------------------------------------------------
/.github/workflows/close-inactive-issues.yml:
--------------------------------------------------------------------------------
1 | name: close inactive issues
2 | on:
3 | schedule:
4 | - cron: "30 1 * * *"
5 |
6 | jobs:
7 | close-issues:
8 | runs-on: ubuntu-latest
9 | permissions:
10 | issues: write
11 | pull-requests: write
12 | steps:
13 | - uses: actions/stale@v4
14 | with:
15 | days-before-issue-stale: 30
16 | days-before-issue-close: 14
17 | stale-issue-label: "stale"
18 | stale-issue-message: "hey 👋 - silence for 30 days 🤐 ... anybody? 😀"
19 | close-issue-message: "closed 📴 because silencio 🤫 since an additional 14 days after staleness 📠"
20 | exempt-issue-labels: "enhancement,feature request"
21 | days-before-pr-stale: -1
22 | days-before-pr-close: -1
23 | repo-token: ${{ secrets.GITHUB_TOKEN }}
24 |
--------------------------------------------------------------------------------
/.github/workflows/gh-release.yml:
--------------------------------------------------------------------------------
1 | # do a gh release after ci user has pushed the tag to main
2 |
3 | name: cut a gh release
4 |
5 | on:
6 | push:
7 | tags:
8 | - "v*.*.*"
9 | jobs:
10 | gh-release:
11 | # if: "contains(github.event.head_commit.author.name, 'wdi5 bot')"
12 | runs-on: ubuntu-latest
13 | permissions:
14 | contents: write
15 | steps:
16 | - uses: actions/checkout@v2
17 | - uses: actions/setup-node@v3
18 | with:
19 | node-version: 16
20 | cache: "npm"
21 | cache-dependency-path: "**/package-lock.json"
22 | - run: npm ci
23 | - run: npm run build
24 | - uses: ncipollo/release-action@v1
25 | with:
26 | artifacts: "cjs/**/*,esm/**/*,client-side-js/**/*"
27 | bodyFile: "CHANGELOG.md"
28 | token: ${{ secrets.GITHUB_TOKEN }}
29 |
--------------------------------------------------------------------------------
/.github/workflows/lint.yml:
--------------------------------------------------------------------------------
1 | # this will check
2 | # - src formatting in ./src ./client-side-js ./docs with prettier
3 | # - code rules in ./src (not yet ./client-side-js) with eslint
4 |
5 | name: lint w/ prettier and eslint
6 |
7 | on:
8 | push:
9 | branches:
10 | - main
11 | pull_request:
12 | branches:
13 | - main
14 |
15 | jobs:
16 | run-linters:
17 | name: Run linters
18 | runs-on: ubuntu-latest
19 |
20 | steps:
21 | - uses: actions/checkout@v4
22 | - uses: actions/setup-node@v4
23 | with:
24 | node-version: 22
25 | cache: "npm"
26 | cache-dependency-path: "**/package-lock.json"
27 | - name: tmp install prettier + eslint
28 | run: npm i prettier eslint@8
29 | - name: lint things
30 | run: |
31 | node_modules/.bin/prettier --debug-check client-side-js src docs
32 | node_modules/.bin/eslint --ext .ts src
33 | # TODO: linting rules for client-side-js
34 |
--------------------------------------------------------------------------------
/.github/workflows/npm-publish.yml:
--------------------------------------------------------------------------------
1 | name: build and publish wdi5 as wdio-ui5-service to npm
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | paths:
8 | # relevant
9 | - "client-side-js/**"
10 | - "src/**"
11 | - "docker/**"
12 | # irrevelant
13 | - "!.github/**"
14 | - "!.husky/**"
15 | - "!.vscode/**"
16 | - "!docs/**"
17 | - "!examples/**"
18 | - "!scripts/**"
19 | - "!.*"
20 | - "!*.md"
21 | - "!*.cjs"
22 | - "!*.json"
23 |
24 | jobs:
25 | build-and-publish:
26 | # only run on non-ci commits and
27 | # on filter docs-, deps-, deps-dev + ci-labeled commits
28 | ### if: "!contains(github.event.head_commit.author.name, 'wdi5 bot') && !startsWith(github.event.head_commit.message, 'chore(deps') && !startsWith(github.event.head_commit.message, 'docs') && !startsWith(github.event.head_commit.message, 'ci')"
29 | runs-on: ubuntu-latest
30 | steps:
31 | - uses: actions/checkout@v3
32 | with:
33 | token: ${{secrets.ADMIN_WDI5}}
34 | - uses: actions/setup-node@v3
35 | with:
36 | node-version: 16
37 | cache: "npm"
38 | cache-dependency-path: "**/package-lock.json"
39 | registry-url: https://registry.npmjs.org/
40 | - run: npm ci
41 | - run: npm run build
42 | - run: |
43 | git config --global user.name 'wdi5 bot'
44 | git config --global user.email 'wdi5-bot@users.noreply.github.com'
45 | # use standard-version for bumping versions in files,
46 | # and generating changelog
47 | # and tagging the release properly
48 | # and auto-incrementing the release number
49 | - run: npm run release
50 | # push to self aka main from within gh action with "release-state"
51 | # doesn't trigger this workflow again
52 | # b/c of missing personal access token here
53 | - run: git push --follow-tags origin main
54 | ## finally publish the npm package
55 | - run: npm publish
56 | env:
57 | NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
58 |
--------------------------------------------------------------------------------
/.github/workflows/publish-btp.yml:
--------------------------------------------------------------------------------
1 | name: build and publish wdi5 TS sample app to BTP
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | paths:
8 | - "examples/ui5-ts-app/**"
9 |
10 | jobs:
11 | build-sample-ts-app:
12 | runs-on: ubuntu-latest
13 | steps:
14 | - uses: actions/checkout@v4
15 | - uses: actions/setup-node@v4
16 | with:
17 | node-version: 20
18 | cache: "npm"
19 | cache-dependency-path: "**/package-lock.json"
20 | registry-url: https://registry.npmjs.org/
21 | - run: npm ci
22 | - run: |
23 | cd examples/ui5-ts-app
24 | npm run build:cf
25 | # let's check where we are
26 | - run: pwd
27 | - name: provide mtar
28 | uses: actions/upload-artifact@v4
29 | with:
30 | name: mtar
31 | path: examples/ui5-ts-app/mta_archives/ui5-approuter_1.0.0.mtar
32 |
33 | publish-sample-ts-app-to-btp:
34 | needs: build-sample-ts-app
35 | runs-on: ubuntu-latest
36 | steps:
37 | - name: get mtar
38 | uses: actions/download-artifact@v4
39 | with:
40 | name: mtar
41 |
42 | - name: check directory
43 | run: ls -la
44 |
45 | - name: deploy to BTP
46 | uses: elliottpope/cloudfoundry-cli-action@v6
47 | with:
48 | CF_API: https://api.cf.eu20.hana.ondemand.com
49 | USERNAME: ${{ secrets.BTP_LOGIN }}
50 | PASSWORD: ${{ secrets.BTP_PASSWORD }}
51 | ORG: ${{secrets.BTP_ORG}}
52 | SPACE: ${{secrets.BTP_SPACE}}
53 | COMMAND: deploy examples/ui5-ts-app/mta_archives/ui5-approuter_1.0.0.mtar -f
54 |
--------------------------------------------------------------------------------
/.github/workflows/wdi5-tests_core.yml:
--------------------------------------------------------------------------------
1 | name: core
2 | on:
3 | pull_request:
4 | branches:
5 | - main
6 | paths:
7 | # relevant
8 | - "client-side-js/**"
9 | - "examples/**"
10 | - "scripts/**"
11 | - "src/**"
12 | # don't run on changes to these
13 | - "!.husky/**"
14 | - "!.vscode/**"
15 | - "!docs/**"
16 | - "!.*"
17 | - "!*.md"
18 | - "!*.cjs"
19 |
20 | jobs:
21 | tests:
22 | runs-on: ubuntu-latest
23 |
24 | strategy:
25 | matrix:
26 | node-version: [18, 20]
27 |
28 | steps:
29 | - name: check out repo
30 | uses: actions/checkout@v3
31 |
32 | - name: use node ${{ matrix.node-version }}
33 | uses: actions/setup-node@v3
34 | with:
35 | node-version: ${{ matrix.node-version }}
36 | cache: "npm"
37 | cache-dependency-path: "**/package-lock.json"
38 |
39 | # install both module + sample app(s) deps
40 | - name: install packages (wdi5 + sample app(s))
41 | run: |
42 | npm pkg delete scripts.prepare
43 | npm ci
44 |
45 | # build things
46 | - name: build
47 | run: npm run build
48 |
49 | - name: test wdi5 core
50 | run: npm test
51 |
--------------------------------------------------------------------------------
/.github/workflows/wdi5-tests_fe-app.yml:
--------------------------------------------------------------------------------
1 | name: FE-app
2 | on:
3 | pull_request:
4 | branches:
5 | - main
6 | paths:
7 | # relevant
8 | - "client-side-js/**"
9 | - "examples/**"
10 | - "scripts/**"
11 | - "src/**"
12 | # don't run on changes to these
13 | - "!.husky/**"
14 | - "!.vscode/**"
15 | - "!docs/**"
16 | - "!.*"
17 | - "!*.md"
18 | - "!*.cjs"
19 |
20 | jobs:
21 | tests:
22 | runs-on: ubuntu-latest
23 |
24 | strategy:
25 | matrix:
26 | node-version: [18, 20]
27 |
28 | steps:
29 | ### no mas since wdio^8 allows for auto-download of matching chromedriver for the env
30 | ### -> we save CI time!
31 | ### yet leaving this as ref
32 | # - name: update chrome
33 | # run: |
34 | # wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add -
35 | # sudo sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list'
36 | # sudo apt-get update
37 | # sudo apt-get --only-upgrade install google-chrome-stable
38 | # # check chrome version
39 | # google-chrome --version
40 |
41 | - name: check out repo
42 | uses: actions/checkout@v3
43 |
44 | - name: use node ${{ matrix.node-version }}
45 | uses: actions/setup-node@v3
46 | with:
47 | node-version: ${{ matrix.node-version }}
48 | cache: "npm"
49 | cache-dependency-path: "**/package-lock.json"
50 |
51 | # install both module + sample app(s) deps
52 | - name: install packages (wdi5 + sample app(s))
53 | run: |
54 | npm pkg delete scripts.prepare
55 | npm ci
56 |
57 | # build things
58 | - name: build
59 | run: npm run build
60 |
61 | # run fiori elements-related wdi5 tests
62 | # explicit headless mode!
63 | - name: test fe-app
64 | run: npm run test:fe-app
65 |
--------------------------------------------------------------------------------
/.github/workflows/wdi5-tests_js-app.yml:
--------------------------------------------------------------------------------
1 | name: JS-app
2 |
3 | on:
4 | pull_request:
5 | branches:
6 | - main
7 |
8 | paths:
9 | # relevant
10 | - "client-side-js/**"
11 | - "examples/**"
12 | - "scripts/**"
13 | - "src/**"
14 | # don't run on changes to these
15 | - "!.husky/**"
16 | - "!.vscode/**"
17 | - "!docs/**"
18 | - "!.*"
19 | - "!*.md"
20 | - "!*.cjs"
21 |
22 | jobs:
23 | tests:
24 | runs-on: ubuntu-latest
25 |
26 | strategy:
27 | matrix:
28 | node-version: [18, 20]
29 |
30 | steps:
31 | ### no mas since wdio^8 allows for auto-download of matching chromedriver for the env
32 | ### -> we save CI time!
33 | ### yet leaving this as ref
34 | # - name: update chrome
35 | # run: |
36 | # wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add -
37 | # sudo sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list'
38 | # sudo apt-get update
39 | # sudo apt-get --only-upgrade install google-chrome-stable
40 | # # check chrome version
41 | # google-chrome --version
42 |
43 | - name: check out repo
44 | uses: actions/checkout@v3
45 |
46 | - name: use node ${{ matrix.node-version }}
47 | uses: actions/setup-node@v3
48 | with:
49 | node-version: ${{ matrix.node-version }}
50 | cache: "npm"
51 | cache-dependency-path: "**/package-lock.json"
52 |
53 | # install both module + sample app(s) deps
54 | - name: install packages (wdi5 + sample app(s))
55 | run: |
56 | npm pkg delete scripts.prepare
57 | npm ci
58 |
59 | # build things
60 | - name: build
61 | run: npm run build
62 |
63 | # run wdi5 tests within CJS app/env
64 | - name: test cjs js-app
65 | run: npm run test-h:js-app
66 |
67 | # run wdi5 tests within ESM app/env
68 | - name: test esm js-app
69 | run: npm run test-h:js-app:esm
70 |
--------------------------------------------------------------------------------
/.github/workflows/wdi5-tests_ts-app.yml:
--------------------------------------------------------------------------------
1 | name: TS-app
2 |
3 | on:
4 | pull_request:
5 | branches:
6 | - main
7 | paths:
8 | # relevant
9 | - "client-side-js/**"
10 | - "examples/**"
11 | - "scripts/**"
12 | - "src/**"
13 | # don't run on changes to these
14 | - "!.husky/**"
15 | - "!.vscode/**"
16 | - "!docs/**"
17 | - "!.*"
18 | - "!*.md"
19 | - "!*.cjs"
20 |
21 | jobs:
22 | tests:
23 | runs-on: ubuntu-latest
24 |
25 | strategy:
26 | matrix:
27 | node-version: [18, 20]
28 |
29 | steps:
30 | ### no mas since wdio^8 allows for auto-download of matching chromedriver for the env
31 | ### -> we save CI time!
32 | ### yet leaving this as ref
33 | # - name: update chrome
34 | # run: |
35 | # wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add -
36 | # sudo sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list'
37 | # sudo apt-get update
38 | # sudo apt-get --only-upgrade install google-chrome-stable
39 | # # check chrome version
40 | # google-chrome --version
41 |
42 | - name: check out repo
43 | uses: actions/checkout@v3
44 |
45 | - name: use node ${{ matrix.node-version }}
46 | uses: actions/setup-node@v3
47 | with:
48 | node-version: ${{ matrix.node-version }}
49 | cache: "npm"
50 | cache-dependency-path: "**/package-lock.json"
51 |
52 | # install both module + sample app(s) deps
53 | - name: install packages (wdi5 + sample app(s))
54 | run: |
55 | npm pkg delete scripts.prepare
56 | npm ci
57 |
58 | # build things
59 | - name: build
60 | run: npm run build
61 |
62 | # run wdi5 tests within app(s)
63 | # this includes test for late-injecting wdi5
64 | - name: test ts-app
65 | run: npm run test-h:ts-app
66 |
67 | # this runs against the deployed wdi5 TS sample app
68 | - name: test devtools support
69 | run: npm run test:protocol
70 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | docs/reveal.js
2 | yarn-error.log
3 | report/appium
4 |
5 | # Ignore Mac files
6 | .DS_Store
7 |
8 | # Logs
9 | logs
10 | *.log
11 | npm-debug.log*
12 | yarn-debug.log*
13 | yarn-error.log*
14 | lerna-debug.log*
15 |
16 | # Diagnostic reports (https://nodejs.org/api/report.html)
17 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
18 |
19 | # Runtime data
20 | pids
21 | *.pid
22 | *.seed
23 | *.pid.lock
24 |
25 | # Directory for instrumented libs generated by jscoverage/JSCover
26 | lib-cov
27 |
28 | # Coverage directory used by tools like istanbul
29 | coverage
30 | *.lcov
31 |
32 | # Compiled binary addons (https://nodejs.org/api/addons.html)
33 | build/Release
34 |
35 | # Dependency directories
36 | node_modules/
37 | jspm_packages/
38 |
39 | # TypeScript v1 declaration files
40 | typings/
41 |
42 | # TypeScript cache
43 | *.tsbuildinfo
44 |
45 | # Optional npm cache directory
46 | .npm
47 |
48 | # Optional eslint cache
49 | .eslintcache
50 |
51 | # Optional REPL history
52 | .node_repl_history
53 |
54 | # Output of 'npm pack'
55 | *.tgz
56 |
57 | # Yarn Integrity file
58 | .yarn-integrity
59 |
60 | # WebStorm/IntelliJ IDE
61 | .idea
62 |
63 | # don't include build artifacts
64 | cjs/**/*
65 | esm/**/*
66 | dist/**/*
67 |
68 | # Gitkeep generally
69 | !**/.gitkeep
70 |
71 | # Testing
72 |
73 | # multi-version test artifacts
74 | examples/ui5-js-app/wdio-wdi5*
75 | examples/ui5-js-app/e2e-test-config/wdio-wdi5*
76 | examples/ui5-js-app/webapp/index-*.html
77 | examples/ui5-js-app/dist/**/*
78 |
79 |
80 | # .env
81 | .env
82 | !.env.example
83 |
84 | # js sample app screenshots
85 | examples/ui5-js-app/webapp/test/__screenshots__/**/*.png
86 |
87 | # btp
88 | mta_archives
89 | *.mtar
90 | *.zip
91 | *.mta
92 |
93 | # cloud testing
94 | *.err
95 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "examples/cap-bookshop-wdi5"]
2 | path = examples/cap-bookshop-wdi5
3 | url = git@github.com:SAP-samples/cap-bookshop-wdi5.git
4 | branch = wdi5-tests
5 |
--------------------------------------------------------------------------------
/.husky/.gitignore:
--------------------------------------------------------------------------------
1 | _
2 |
--------------------------------------------------------------------------------
/.husky/commit-msg:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | . "$(dirname "$0")/_/husky.sh"
3 |
4 | npx --no -- commitlint --edit ""
5 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | . "$(dirname "$0")/_/husky.sh"
3 |
4 | npx lint-staged
5 |
--------------------------------------------------------------------------------
/.mocharc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extension": ["ts"],
3 | "spec": "test/**/*.test.ts",
4 | "require": "ts-node/register",
5 | "node-option": ["experimental-specifier-resolution=node", "loader=ts-node/esm"]
6 | }
7 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | 20
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | README.md
2 | # build artifacts
3 | dist
4 | CHANGELOG.md
5 | docs/resources.md
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "printWidth": 120,
3 | "trailingComma": "none",
4 | "tabWidth": 4,
5 | "useTabs": false,
6 | "semi": false,
7 | "singleQuote": false,
8 | "arrowParens": "always",
9 | "endOfLine": "lf",
10 | "overrides": [
11 | {
12 | "files": [
13 | "*.yaml",
14 | "*.yml",
15 | "*.json",
16 | "*.md"
17 | ],
18 | "options": {
19 | "tabWidth": 2
20 | }
21 | }
22 | ]
23 | }
--------------------------------------------------------------------------------
/.retrofit-pkg-json.js:
--------------------------------------------------------------------------------
1 | // transmogrifying wdi5's package.json to a cjs version
2 | import { promises as fs } from "fs"
3 | import pkgJson from "./package.json" assert { type: "json" }
4 | ;["type", "exports", "types", "files", "workspaces", "scripts", "lint-staged"].forEach((section) => {
5 | delete pkgJson[section]
6 | })
7 | ;(async () => {
8 | await fs.mkdir("./cjs", { recursive: true })
9 | await fs.writeFile("./cjs/package.json", JSON.stringify(pkgJson, null, 2))
10 | })()
11 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "type": "pwa-chrome",
9 | "request": "launch",
10 | "name": "Launch Chrome against localhost",
11 | "url": "http://localhost:8080",
12 | "webRoot": "${workspaceFolder}"
13 | }
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "editor.formatOnSave": true,
3 | "[typescript]": {
4 | "editor.defaultFormatter": "esbenp.prettier-vscode"
5 | },
6 | "javascript.preferences.importModuleSpecifierEnding": "js",
7 | "typescript.preferences.importModuleSpecifierEnding": "js",
8 | "search.useIgnoreFiles": true
9 | }
10 |
--------------------------------------------------------------------------------
/SUPPORT.md:
--------------------------------------------------------------------------------
1 | # Commercial Support
2 |
3 | The recommended way of obtaining commercial support for `wdi5` is via [GitHub Sponsors](https://docs.github.com/en/sponsors), providing process- and financial-flows without bureaucratic hassles.
4 | These are the companies and individuals offering commercial support for `wdi5`:
5 |
6 | - [j&s-soft](https://github.com/sponsors/js-soft)
7 |
8 | # Regular Support
9 |
10 | We're using [GitHub issues](https://github.com/ui5-community/wdi5/issues) as the default channel for all support requests.
11 | For questions, discussion, and announcement concerning, [GitHub discussions](https://github.com/ui5-community/wdi5/discussions) is the place to look.
12 |
--------------------------------------------------------------------------------
/client-side-js/_checkForUI5Ready.cjs:
--------------------------------------------------------------------------------
1 | async function clientSide__checkForUI5Ready(browserInstance) {
2 | return await browserInstance.executeAsync((done) => {
3 | window.bridge
4 | .waitForUI5(window.wdi5.waitForUI5Options)
5 | .then(() => {
6 | window.wdi5.Log.info("[browser wdi5] UI5 is ready")
7 | done(true)
8 | })
9 | .catch((error) => {
10 | console.error(error)
11 | })
12 | })
13 | }
14 |
15 | module.exports = {
16 | clientSide__checkForUI5Ready
17 | }
18 |
--------------------------------------------------------------------------------
/client-side-js/_getAggregation.cjs:
--------------------------------------------------------------------------------
1 | async function clientSide_getAggregation(webElement, aggregationName, browserInstance) {
2 | webElement = await Promise.resolve(webElement) // to plug into fluent async api
3 | browserInstance = await Promise.resolve(browserInstance)
4 | return await browserInstance.executeAsync(
5 | (webElement, aggregationName, done) => {
6 | window.bridge.waitForUI5(window.wdi5.waitForUI5Options).then(() => {
7 | // DOM to UI5
8 | try {
9 | let oControl = window.wdi5.getUI5CtlForWebObj(webElement)
10 | let cAggregation = oControl.getAggregation(aggregationName)
11 | // if getAggregation retrieves an element only it has to be transformed to an array
12 | if (cAggregation && !Array.isArray(cAggregation)) {
13 | cAggregation = [cAggregation]
14 | }
15 | // read classname eg. sap.m.ComboBox
16 | controlType = oControl.getMetadata()._sClassName
17 | let result = window.wdi5.createControlIdMap(cAggregation, controlType)
18 | done({ status: 0, result: result })
19 | } catch (e) {
20 | done({ status: 1, message: e.toString() })
21 | }
22 | })
23 | },
24 | webElement,
25 | aggregationName
26 | )
27 | }
28 |
29 | module.exports = {
30 | clientSide_getAggregation
31 | }
32 |
--------------------------------------------------------------------------------
/client-side-js/_navTo.cjs:
--------------------------------------------------------------------------------
1 | async function clientSide__navTo(sComponentId, sName, oParameters, oComponentTargetInfo, bReplace, browserInstance) {
2 | return await browserInstance.executeAsync(
3 | (sComponentId, sName, oParameters, oComponentTargetInfo, bReplace, done) => {
4 | window.bridge.waitForUI5(window.wdi5.waitForUI5Options).then(() => {
5 | window.wdi5.Log.info(`[browser wdi5] navigation to ${sName} triggered`)
6 |
7 | const router = sap.ui.getCore().getComponent(sComponentId).getRouter()
8 | const hashChanger = window.compareVersions.compare("1.75.0", sap.ui.version, ">")
9 | ? sap.ui.core.routing.HashChanger.getInstance()
10 | : router.getHashChanger()
11 |
12 | // on success result is the router
13 | // FIXME: should this be changed to attach once?
14 | hashChanger.attachEvent("hashChanged", (oEvent) => {
15 | done({
16 | status: 0,
17 | result: window.compareVersions.compare("1.75.0", sap.ui.version, ">")
18 | ? hashChanger.getHash()
19 | : hashChanger.hash
20 | })
21 | })
22 |
23 | // get component and trigger router
24 | // sName, oParameters?, oComponentTargetInfo?, bReplace?
25 | router.navTo(sName, oParameters, oComponentTargetInfo, bReplace)
26 | })
27 | },
28 | sComponentId,
29 | sName,
30 | oParameters,
31 | oComponentTargetInfo,
32 | bReplace
33 | )
34 | }
35 | module.exports = {
36 | clientSide__navTo
37 | }
38 |
--------------------------------------------------------------------------------
/client-side-js/allControls.cjs:
--------------------------------------------------------------------------------
1 | async function clientSide_allControls(controlSelector, browserInstance) {
2 | controlSelector = await Promise.resolve(controlSelector) // to plug into fluent async api
3 | return await browserInstance.executeAsync((controlSelector, done) => {
4 | const waitForUI5Options = Object.assign({}, window.wdi5.waitForUI5Options)
5 | if (controlSelector.timeout) {
6 | waitForUI5Options.timeout = controlSelector.timeout
7 | }
8 |
9 | window.wdi5.waitForUI5(
10 | waitForUI5Options,
11 | () => {
12 | window.wdi5.Log.info("[browser wdi5] locating " + JSON.stringify(controlSelector))
13 | controlSelector.selector = window.wdi5.createMatcher(controlSelector.selector)
14 | window.bridge
15 | .findAllDOMElementsByControlSelector(controlSelector)
16 | .then((domElements) => {
17 | // window.wdi5.Log.info('[browser wdi5] control located! - Message: ' + JSON.stringify(domElement));
18 | let returnElements = []
19 | domElements.forEach((domElement) => {
20 | const ui5Control = window.wdi5.getUI5CtlForWebObj(domElement)
21 | const id = ui5Control.getId()
22 | window.wdi5.Log.info(`[browser wdi5] control with id: ${id} located!`)
23 | const aProtoFunctions = window.wdi5.retrieveControlMethods(ui5Control)
24 | returnElements.push({ domElement, id, aProtoFunctions })
25 | })
26 |
27 | done({ status: 0, result: returnElements })
28 | })
29 | .catch(window.wdi5.errorHandling.bind(this, done))
30 | },
31 | window.wdi5.errorHandling.bind(this, done)
32 | )
33 | }, controlSelector)
34 | }
35 |
36 | module.exports = {
37 | clientSide_allControls
38 | }
39 |
--------------------------------------------------------------------------------
/client-side-js/fireEvent.cjs:
--------------------------------------------------------------------------------
1 | async function clientSide_fireEvent(webElement, eventName, oOptions, browserInstance) {
2 | return await browserInstance.executeAsync(
3 | (webElement, eventName, oOptions, done) => {
4 | window.wdi5.waitForUI5(
5 | window.wdi5.waitForUI5Options,
6 | () => {
7 | window.wdi5.Log.info("[browser wdi5] working " + eventName + " for " + webElement)
8 | // DOM to ui5
9 | let oControl = window.wdi5.getUI5CtlForWebObj(webElement)
10 | if (oControl && oControl.hasListeners(eventName)) {
11 | window.wdi5.Log.info("[browser wdi5] firing " + eventName + " on " + webElement)
12 | // element existent and has the target event
13 | try {
14 | // eval the options indicated by option of type string
15 | if (typeof oOptions === "string") {
16 | oOptions = eval(oOptions)()
17 | }
18 | oControl.fireEvent(eventName, oOptions)
19 | // convert to boolean
20 | done({ status: 0, result: true })
21 | } catch (e) {
22 | window.wdi5.errorHandling.bind(this, done)
23 | }
24 | } else {
25 | window.wdi5.errorHandling(this, done)
26 | }
27 | },
28 | window.wdi5.errorHandling.bind(this, done)
29 | )
30 | },
31 | webElement,
32 | eventName,
33 | oOptions
34 | )
35 | }
36 |
37 | module.exports = {
38 | clientSide_fireEvent
39 | }
40 |
--------------------------------------------------------------------------------
/client-side-js/getControl.cjs:
--------------------------------------------------------------------------------
1 | async function clientSide_getControl(controlSelector, browserInstance) {
2 | controlSelector = await Promise.resolve(controlSelector) // to plug into fluent async api
3 | return await browserInstance.executeAsync((controlSelector, done) => {
4 | const waitForUI5Options = Object.assign({}, window.wdi5.waitForUI5Options)
5 | if (controlSelector.timeout) {
6 | waitForUI5Options.timeout = controlSelector.timeout
7 | }
8 | window.wdi5.waitForUI5(
9 | waitForUI5Options,
10 | () => {
11 | window.wdi5.Log.info("[browser wdi5] locating " + JSON.stringify(controlSelector))
12 | controlSelector.selector = window.wdi5.createMatcher(controlSelector.selector)
13 | window.bridge
14 | .findDOMElementByControlSelector(controlSelector)
15 | .then((domElement) => {
16 | const ui5Control = window.wdi5.getUI5CtlForWebObj(domElement)
17 | const id = ui5Control.getId()
18 | const className = ui5Control.getMetadata()._sClassName
19 | window.wdi5.Log.info(`[browser wdi5] control with id: ${id} located!`)
20 | const aProtoFunctions = window.wdi5.retrieveControlMethods(ui5Control)
21 | done({
22 | status: 0,
23 | domElement: domElement,
24 | id: id,
25 | aProtoFunctions: aProtoFunctions,
26 | className: className
27 | })
28 | })
29 | .catch(window.wdi5.errorHandling.bind(this, done))
30 | },
31 | window.wdi5.errorHandling.bind(this, done)
32 | )
33 | }, controlSelector)
34 | }
35 |
36 | module.exports = {
37 | clientSide_getControl
38 | }
39 |
--------------------------------------------------------------------------------
/client-side-js/getObject.cjs:
--------------------------------------------------------------------------------
1 | async function clientSide_getObject(uuid) {
2 | return await browser.executeAsync((uuid, done) => {
3 | const waitForUI5Options = Object.assign({}, window.wdi5.waitForUI5Options)
4 |
5 | window.wdi5.waitForUI5(
6 | waitForUI5Options,
7 | () => {
8 | window.wdi5.Log.info("[browser wdi5] locating object " + uuid)
9 |
10 | let object = window.wdi5.objectMap[uuid]
11 | if (!object) {
12 | const errorMessage = `[browser wdi5] ERR: no object with uuid: ${uuid} found`
13 | window.wdi5.Log.error(errorMessage)
14 | done({ status: 1, message: errorMessage })
15 | }
16 |
17 | let className = ""
18 | if (object && object.getMetadata) {
19 | className = object.getMetadata()._sClassName
20 | }
21 | window.wdi5.Log.info(`[browser wdi5] object with uuid: ${uuid} located!`)
22 |
23 | // FIXME: extract, collapse and remove cylic in 1 step
24 |
25 | const aProtoFunctions = window.wdi5.retrieveControlMethods(object, true)
26 |
27 | object = window.wdi5.collapseObject(object)
28 |
29 | const collapsedAndNonCyclic = JSON.parse(JSON.stringify(object, window.wdi5.getCircularReplacer()))
30 |
31 | // remove all empty Array elements, inlcuding private keys (starting with "_")
32 | const semanticCleanedElements = window.wdi5.removeEmptyElements(collapsedAndNonCyclic)
33 |
34 | done({
35 | status: 0,
36 | uuid: uuid,
37 | aProtoFunctions: aProtoFunctions,
38 | className: className,
39 | object: semanticCleanedElements
40 | })
41 | },
42 | window.wdi5.errorHandling.bind(this, done)
43 | )
44 | }, uuid)
45 | }
46 |
47 | module.exports = {
48 | clientSide_getObject
49 | }
50 |
--------------------------------------------------------------------------------
/client-side-js/getSelectorForElement.cjs:
--------------------------------------------------------------------------------
1 | async function clientSide_getSelectorForElement(oOptions, browserInstance) {
2 | return await browserInstance.executeAsync((oOptions, done) => {
3 | window.wdi5.waitForUI5(
4 | window.wdi5.waitForUI5Options,
5 | () => {
6 | window.wdi5.Log.info("[browser wdi5] locating domElement")
7 | window.bridge
8 | .findControlSelectorByDOMElement(oOptions)
9 | .then((controlSelector) => {
10 | window.wdi5.Log.info("[browser wdi5] controlLocator created!")
11 | done({ status: 0, result: controlSelector })
12 | return controlSelector
13 | })
14 | .catch(window.wdi5.errorHandling.bind(this, done))
15 | },
16 | window.wdi5.errorHandling.bind(this, done)
17 | )
18 | }, oOptions)
19 | }
20 |
21 | module.exports = {
22 | clientSide_getSelectorForElement
23 | }
24 |
--------------------------------------------------------------------------------
/client-side-js/getUI5Version.cjs:
--------------------------------------------------------------------------------
1 | /**
2 | * @returns {string} UI5 version number in string form
3 | */
4 | async function clientSide_getUI5Version(browserInstance = browser) {
5 | return await browserInstance.executeAsync((done) => {
6 | done(sap.ui.version)
7 | })
8 | }
9 |
10 | module.exports = {
11 | clientSide_getUI5Version
12 | }
13 |
--------------------------------------------------------------------------------
/client-side-js/interactWithControl.cjs:
--------------------------------------------------------------------------------
1 | async function clientSide_interactWithControl(oOptions, browserInstance) {
2 | browserInstance = await Promise.resolve(browserInstance)
3 | return await browserInstance.executeAsync((oOptions, done) => {
4 | window.wdi5.waitForUI5(
5 | window.wdi5.waitForUI5Options,
6 | () => {
7 | window.wdi5.Log.info("[browser wdi5] locating controlSelector")
8 | oOptions.selector = window.wdi5.createMatcher(oOptions.selector)
9 | window.bridge
10 | .interactWithControl(oOptions)
11 | .then((result) => {
12 | window.wdi5.Log.info("[browser wdi5] interaction complete! - Message: " + result)
13 | done({ status: 0, result: result })
14 | })
15 | .catch(window.wdi5.errorHandling.bind(this, done))
16 | },
17 | window.wdi5.errorHandling.bind(this, done)
18 | )
19 | }, oOptions)
20 | }
21 |
22 | module.exports = {
23 | clientSide_interactWithControl
24 | }
25 |
--------------------------------------------------------------------------------
/commitlint.config.cjs:
--------------------------------------------------------------------------------
1 | module.exports = { extends: ["@commitlint/config-conventional"], ignores: [(message) => message.includes("wip")] }
2 |
--------------------------------------------------------------------------------
/docker/Dockerfile.standalone:
--------------------------------------------------------------------------------
1 | # base image
2 | ## pass this via docker build --build-arg node_version=17
3 | ARG node_version=16
4 | FROM node:${node_version}-bullseye-slim
5 |
6 | # basic tools
7 | RUN apt update
8 | RUN apt install -yq libgconf-2-4 gnupg2 wget ca-certificates lsb-release software-properties-common vim procps
9 | # install google chrome
10 | RUN sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list'
11 | RUN wget -O- https://dl.google.com/linux/linux_signing_key.pub |gpg --dearmor > /etc/apt/trusted.gpg.d/google.gpg
12 | RUN apt update
13 | RUN apt install -y google-chrome-stable --no-install-recommends
14 | # install git
15 | # see #599 https://github.com/ui5-community/wdi5/issues/599
16 | RUN apt install -y git
17 |
18 | WORKDIR /app
19 |
20 | # copy sample app to /app
21 | COPY ./examples/ui5-js-app/webapp webapp
22 | COPY ./examples/ui5-js-app/ui5.yaml ui5.yaml
23 | COPY ./docker/package-standalone.json package.json
24 | # this is the standard file and allows for a simple call like "wdio"
25 | COPY ./examples/ui5-js-app/e2e-test-config/wdio-docker-standalone.conf.js wdio.conf.js
26 | COPY ./examples/ui5-js-app/e2e-test-config/wdio.base.conf.js wdio.base.conf.js
27 | RUN mkdir -p report/screenshots
28 |
29 | # install basic setup:
30 | # - local minimal WebdriverIO environment
31 | # and setup, incl standard reporter + Chrome
32 | # - wdi5
33 | RUN npm install
34 |
35 | # run wdio + wdi5
36 | CMD npm test
37 |
--------------------------------------------------------------------------------
/docker/Dockerfile.wdi5:
--------------------------------------------------------------------------------
1 |
2 | # base image
3 | FROM node:14-alpine3.14
4 |
5 | RUN apk update \
6 | && apk upgrade
7 |
8 | # set working directory
9 | WORKDIR /app
10 |
11 | # COPY all to root
12 | COPY ../docker/package-wdi5.json package.json
13 | COPY ../examples/ui5-js-app/webapp/test test
14 | COPY ../examples/ui5-js-app/e2e-test-config/wdio-docker-selenium.conf.js wdio-docker-selenium.conf.js
15 | COPY ../examples/ui5-js-app/e2e-test-config/wdio.base.conf.js wdio.base.conf.js
16 |
17 | COPY ../ wdio-ui5-service
18 |
19 | RUN mkdir -p report/screenshots
20 |
21 | RUN npm install
22 |
23 | CMD node_modules/.bin/wait-on tcp:selenium-hub:4444 && npm run test:docker:selenium
24 |
--------------------------------------------------------------------------------
/docker/docker-compose-standalone.yaml:
--------------------------------------------------------------------------------
1 | # To execute this docker-compose yml file use `docker-compose -f docker-compose-v3.yml up`
2 | # Add the `-d` flag at the end for detached execution
3 | # To stop the execution, hit Ctrl+C, and then `docker-compose -f docker-compose-v3.yml down`
4 |
5 | # Mac M1 issues: https://github.com/SeleniumHQ/selenium/issues/9733
6 | version: "3"
7 |
8 | networks:
9 | wdi5:
10 | name: wdi5
11 | external: true
12 |
13 | services:
14 | selenium-hub:
15 | restart: always
16 | image: webdriverio/selenium-standalone
17 | container_name: selenium-standalone
18 | ports:
19 | - "4442:4442"
20 | - "4443:4443"
21 | - "4444:4444"
22 | networks:
23 | - wdi5
24 |
25 | wdi5:
26 | build:
27 | context: ../
28 | dockerfile: ./docker/Dockerfile.wdi5
29 | image: wdi5
30 | container_name: wdi5
31 | stdin_open: true # docker run -i
32 | tty: true # docker run -t
33 | # use for development
34 | # volumes:
35 | # - ./docker/package-wdi5-dev.json:/app/package.json
36 | environment:
37 | - BROWSERS=${BROWSERS}
38 | depends_on:
39 | - selenium-hub
40 | - test-app
41 | networks:
42 | - wdi5
43 |
44 | test-app:
45 | build:
46 | context: ../
47 | dockerfile: ./examples/ui5-js-app/Dockerfile
48 | image: test-app
49 | container_name: test-app
50 | ports:
51 | - "8888:8888"
52 | networks:
53 | - wdi5
54 |
--------------------------------------------------------------------------------
/docker/package-app.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "wdi5",
3 | "version": "0.0.1",
4 | "description": "test for wdi5 in docker",
5 | "keywords": ["wdio-service", "ui5", "openui5", "sapui5", "docker"],
6 | "scripts": {
7 | "test:docker:grid": "wdio e2e-test-config/wdio-docker.conf.js",
8 | "start": "soerver -d ./webapp -p 8888 -x ./webapp/proxyrc.json",
9 | "test:standalone": "wdio run e2e-test-config/wdio-docker-standalone.conf.js",
10 | "test:docker:standalone": "run-p start test:standalone"
11 | },
12 | "devDependencies": {
13 | "fs-extra": "^10.0.1",
14 | "npm-run-all": "^4.1.5",
15 | "soerver": "^0.0.3",
16 | "wait-on": "^6.0.1"
17 | },
18 | "engines": {
19 | "node": ">=14",
20 | "npm": ">=7"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/docker/package-standalone.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "wdi5",
3 | "version": "0.0.1",
4 | "scripts": {
5 | "start": "ui5 serve",
6 | "wdi5": "wdio run wdio.conf.js --spec basic.test",
7 | "test": "run-p -r start wdi5"
8 | },
9 | "devDependencies": {
10 | "@ui5/cli": "latest",
11 | "@wdio/cli": ">=8.14.0",
12 | "@wdio/mocha-framework": "^8",
13 | "@wdio/spec-reporter": "^8",
14 | "@wdio/local-runner": "^8",
15 | "fs-extra": "latest",
16 | "mocha": "latest",
17 | "npm-run-all": "latest",
18 | "ui5-middleware-simpleproxy": "latest",
19 | "wait-on": "latest",
20 | "wdio-ui5-service": "latest",
21 | "webdriverio": ">8.32.2"
22 | },
23 | "ui5": {
24 | "dependencies": ["ui5-middleware-simpleproxy"]
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/docker/package-wdi5-dev.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "wdi5",
3 | "version": "0.0.1",
4 | "description": "test for wdi5 in docker",
5 | "keywords": ["wdio-service", "ui5", "openui5", "sapui5", "docker"],
6 | "scripts": {
7 | "test:docker:grid": "wdio e2e-test-config/wdio-docker.conf.js",
8 | "start": "soerver -d ./webapp -p 8888 -x ./webapp/proxyrc.json",
9 | "test:standalone": "wdio run e2e-test-config/wdio-docker-standalone.conf.js",
10 | "test:docker:standalone": "run-p start test:standalone"
11 | },
12 | "devDependencies": {
13 | "@wdio/cli": "^8",
14 | "@wdio/mocha-framework": "^8",
15 | "@wdio/spec-reporter": "^8",
16 | "@wdio/local-runner": "^8",
17 | "fs-extra": "^10.0.1",
18 | "mocha": "^9.2.2",
19 | "npm-run-all": "^4.1.5",
20 | "soerver": "^0.0.3",
21 | "wait-on": "^6.0.1",
22 | "wdio-ui5-service": "file:wdio-ui5-service",
23 | "webdriverio": "^8"
24 | },
25 | "engines": {
26 | "node": ">=14",
27 | "npm": ">=7"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/docker/package-wdi5.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "wdi5",
3 | "version": "0.0.1",
4 | "description": "test for wdi5 in docker",
5 | "keywords": ["wdio-service", "ui5", "openui5", "sapui5", "docker"],
6 | "scripts": {
7 | "test:docker:grid": "wdio e2e-test-config/wdio-docker.conf.js",
8 | "test:docker:selenium": "wdio e2e-test-config/wdio-docker-selenium.conf.js",
9 | "start": "soerver -d ./webapp -p 8888 -x ./webapp/proxyrc.json",
10 | "test:standalone": "wdio run e2e-test-config/wdio-docker-standalone.conf.js",
11 | "test:docker:standalone": "run-p start test:standalone"
12 | },
13 | "devDependencies": {
14 | "@wdio/cli": "^8",
15 | "@wdio/mocha-framework": "^8",
16 | "@wdio/spec-reporter": "^8",
17 | "@wdio/local-runner": "^8",
18 | "fs-extra": "^10.0.1",
19 | "mocha": "^9.2.2",
20 | "npm-run-all": "^4.1.5",
21 | "soerver": "^0.0.3",
22 | "wait-on": "^6.0.1",
23 | "wdio-ui5-service": "*",
24 | "webdriverio": "^8"
25 | },
26 | "engines": {
27 | "node": ">=14",
28 | "npm": ">=7"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/docs/.gitignore:
--------------------------------------------------------------------------------
1 | reveal.js/**/*
2 | !reveal.js/wdi5*
3 | !reveal.js/wdi5-img/**/*
4 | .DS_Store
--------------------------------------------------------------------------------
/docs/.nojekyll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ui5-community/wdi5/93a69ff20d1563e34e99c9e371baa13cda0c8886/docs/.nojekyll
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | # Welcome 👋 to `wdi5`!
2 |
3 | `wdi5` (/vdif5/) is a [`Webdriver.IO`](https://webdriver.io) service (think: extension), utilizing [`UI5`’s test API](https://ui5.sap.com/#/api/sap.ui.test).
4 |
5 | It is designed to run cross-platform end-to-end tests on a [UI5](https://ui5.sap.com) application, with selectors compatible to [OPA5](https://ui5.sap.com/#/entity/sap.ui.test.Opa5).
6 |
7 | Because it adhere's to Webdriver.IO's standards, `wdi5`'s _technical_ name is `wdio-ui5-service`. It is also the [](https://www.npmjs.com/package/wdio-ui5-service) package name .
8 |
9 |  `wdi5` = UI5 Test API + Webdriver.IO
10 |
11 |
12 |
--------------------------------------------------------------------------------
/docs/_sidebar.md:
--------------------------------------------------------------------------------
1 | - [Home](/)
2 | - [Installation](installation.md)
3 | - [Migration](migration.md)
4 | - [Configuration](configuration.md)
5 | - [Running](running.md)
6 | - [Debugging](debugging.md)
7 | - [Usage](usage.md)
8 | - [Authentication](authentication.md)
9 | - [Locators](locators.md)
10 | - [Recipes](recipes.md)
11 | - [Test Library Integration](fe-testlib.md)
12 | - [Docker](docker.md)
13 | - [Contributing](contributing.md)
14 | - [Resources](resources.md)
15 |
--------------------------------------------------------------------------------
/docs/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ui5-community/wdi5/93a69ff20d1563e34e99c9e371baa13cda0c8886/docs/android-chrome-192x192.png
--------------------------------------------------------------------------------
/docs/android-chrome-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ui5-community/wdi5/93a69ff20d1563e34e99c9e371baa13cda0c8886/docs/android-chrome-512x512.png
--------------------------------------------------------------------------------
/docs/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ui5-community/wdi5/93a69ff20d1563e34e99c9e371baa13cda0c8886/docs/apple-touch-icon.png
--------------------------------------------------------------------------------
/docs/debugging.md:
--------------------------------------------------------------------------------
1 | # Debugging `wdi5` tests
2 |
3 | ## with VS Code
4 |
5 | Add breakpoints in the test source:
6 | 
7 |
8 | In the `Terminal` are of VS Code, start a "JavaScript Debug Terminal"...
9 | 
10 | ...and run `wdi5` from there...
11 |
12 | - via `npm run wdi5` (or whatever npm script name you've chosen)
13 | - directly via the `wdio` binary
14 | (in `/examples/ui5-js-app` of this repo)
15 | `$> ../../node_modules/.bin/wdio e2e-test-config/wdio-webserver.conf.js --spec /basic.test.js`
16 |
17 | Subsequently `wdi5` will halt execution at that breakpoint and allows inspecting things in the Debugger pane:
18 |
19 | 
20 |
21 | ?> for debugging tests, remember [to have a webserver running](running), serving the UI5 app under test
22 |
--------------------------------------------------------------------------------
/docs/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ui5-community/wdi5/93a69ff20d1563e34e99c9e371baa13cda0c8886/docs/favicon-16x16.png
--------------------------------------------------------------------------------
/docs/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ui5-community/wdi5/93a69ff20d1563e34e99c9e371baa13cda0c8886/docs/favicon-32x32.png
--------------------------------------------------------------------------------
/docs/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ui5-community/wdi5/93a69ff20d1563e34e99c9e371baa13cda0c8886/docs/favicon.ico
--------------------------------------------------------------------------------
/docs/img/01_installation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ui5-community/wdi5/93a69ff20d1563e34e99c9e371baa13cda0c8886/docs/img/01_installation.png
--------------------------------------------------------------------------------
/docs/img/05_installation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ui5-community/wdi5/93a69ff20d1563e34e99c9e371baa13cda0c8886/docs/img/05_installation.png
--------------------------------------------------------------------------------
/docs/img/07_installation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ui5-community/wdi5/93a69ff20d1563e34e99c9e371baa13cda0c8886/docs/img/07_installation.png
--------------------------------------------------------------------------------
/docs/img/09_installation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ui5-community/wdi5/93a69ff20d1563e34e99c9e371baa13cda0c8886/docs/img/09_installation.png
--------------------------------------------------------------------------------
/docs/img/20_running.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ui5-community/wdi5/93a69ff20d1563e34e99c9e371baa13cda0c8886/docs/img/20_running.png
--------------------------------------------------------------------------------
/docs/img/23_running.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ui5-community/wdi5/93a69ff20d1563e34e99c9e371baa13cda0c8886/docs/img/23_running.png
--------------------------------------------------------------------------------
/docs/img/auto-open-dev-tools.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ui5-community/wdi5/93a69ff20d1563e34e99c9e371baa13cda0c8886/docs/img/auto-open-dev-tools.png
--------------------------------------------------------------------------------
/docs/img/jsdoc-type-cast-codecompletion.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ui5-community/wdi5/93a69ff20d1563e34e99c9e371baa13cda0c8886/docs/img/jsdoc-type-cast-codecompletion.png
--------------------------------------------------------------------------------
/docs/img/social-media-wb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ui5-community/wdi5/93a69ff20d1563e34e99c9e371baa13cda0c8886/docs/img/social-media-wb.png
--------------------------------------------------------------------------------
/docs/img/social-media.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ui5-community/wdi5/93a69ff20d1563e34e99c9e371baa13cda0c8886/docs/img/social-media.png
--------------------------------------------------------------------------------
/docs/img/vscode-breakpoint.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ui5-community/wdi5/93a69ff20d1563e34e99c9e371baa13cda0c8886/docs/img/vscode-breakpoint.png
--------------------------------------------------------------------------------
/docs/img/vscode-debugger-halted.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ui5-community/wdi5/93a69ff20d1563e34e99c9e371baa13cda0c8886/docs/img/vscode-debugger-halted.png
--------------------------------------------------------------------------------
/docs/img/vscode-js-debug-terminal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ui5-community/wdi5/93a69ff20d1563e34e99c9e371baa13cda0c8886/docs/img/vscode-js-debug-terminal.png
--------------------------------------------------------------------------------
/docs/img/wdi5-logo-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ui5-community/wdi5/93a69ff20d1563e34e99c9e371baa13cda0c8886/docs/img/wdi5-logo-small.png
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | wdi5 documentation
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/docs/pageObjects.md:
--------------------------------------------------------------------------------
1 | - "recommended" wdio vs "gewohnt" UIVeri5 -> beispiele
2 |
--------------------------------------------------------------------------------
/docs/presentation/wdi5-img/idea01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ui5-community/wdi5/93a69ff20d1563e34e99c9e371baa13cda0c8886/docs/presentation/wdi5-img/idea01.png
--------------------------------------------------------------------------------
/docs/presentation/wdi5-img/idea02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ui5-community/wdi5/93a69ff20d1563e34e99c9e371baa13cda0c8886/docs/presentation/wdi5-img/idea02.png
--------------------------------------------------------------------------------
/docs/presentation/wdi5-img/idea03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ui5-community/wdi5/93a69ff20d1563e34e99c9e371baa13cda0c8886/docs/presentation/wdi5-img/idea03.png
--------------------------------------------------------------------------------
/docs/presentation/wdi5-img/principle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ui5-community/wdi5/93a69ff20d1563e34e99c9e371baa13cda0c8886/docs/presentation/wdi5-img/principle.png
--------------------------------------------------------------------------------
/docs/presentation/wdi5-img/scope.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ui5-community/wdi5/93a69ff20d1563e34e99c9e371baa13cda0c8886/docs/presentation/wdi5-img/scope.png
--------------------------------------------------------------------------------
/docs/resources.md:
--------------------------------------------------------------------------------
1 | # Resources
2 |
3 | In addition to this documentation, there are also external resources regarding `wdi5` available:
4 |
5 | - :pencil: [SAP Community Blog Posts](https://blogs.sap.com/tag/wdi5/)
6 | - :construction_worker: [wdi5 workshop at UI5con 2022](https://github.com/ui5-community/wdi5-workshop)
7 | - :construction_worker: [devtoberfest 2022 session](https://www.youtube.com/watch?v=f-0ztSnb2-c)
8 | - :video_camera: [YouTube Videos](https://www.youtube.com/results?search_query=wdi5&sp=EgIIBQ%253D%253D)
9 | - :question: [SAP Community Questions](https://answers.sap.com/topics/wdi5.html)
10 | - :ear: Podcasts
11 | - [SAP Podcast "The Open Source Way"](https://podcast.opensap.info/open-source-way/2022/07/27/wdi5-ui5s-open-source-end-to-end-testing-framework/): `wdi5` - UI5’s Open-Source End-to-End Testing Framework
12 | - [UI5 NewsCast #28](https://podcast.opensap.info/ui5-newscast/2022/07/04/ui5-newscast-028-wdi5-ui5s-open-source-end-to-end-testing-framework/)
13 |
14 | To drop in for a curiosity-driven chat :speech_balloon:, [hop over to #wdi5 on Slack](https://ui5-slack-invite.cfapps.eu10.hana.ondemand.com/).
15 |
16 | The best place to follow announcements :mega: regarding `wdi5` are [the GitHub discussions](https://github.com/ui5-community/wdi5/discussions), the Twitter account [`@_wdi5_`](https://twitter.com/_wdi5_) and the [`@_wdi5_` FOSStodon account](https://fosstodon.org/@_wdi5_).
17 |
18 | ## Tools
19 |
20 | - [UI5 Journey Recorder](https://github.com/ui5-community/ui5-journey-recorder)
21 | a chrome extension for recording the user path through a UI5 app and exporting it to `wdi5`
22 | - [UI5 Test Recorder](https://ui5.sap.com/#/topic/2535ef9272064cb6bd6b44e5402d531d) for finetuning control selectors and test assertions (`expect`s)
23 | - [wdio/wdi5 VS Code extension](https://github.com/marcellourbani/vscode-wdio) - test cockpit integration in VS Code
24 |
25 | ## Commercial Support
26 |
27 | The recommended way of obtaining commercial support for `wdi5` is via [GitHub Sponsors](https://docs.github.com/en/sponsors), providing process- and financial-flows with little bureaucratic hassles.
28 | These are the companies and individuals offering commercial support for `wdi5`:
29 |
30 | - [j&s-soft](https://github.com/sponsors/js-soft)
31 |
--------------------------------------------------------------------------------
/docs/running.md:
--------------------------------------------------------------------------------
1 | # Running `wdi5`
2 |
3 | Two components are involved for executing `wdi5`-tests:
4 |
5 | 1. a webserver delivering your UI5 application
6 | 2. the WebdriverIO cli to run the `wdi5`-tests
7 | 3. [test files](/usage#test-suites) (default: `$ui5-app/webapp/test/*.test.(j|t)s`)
8 |
9 | First, start the UI5 app, e.g. via [the ui5-tooling](https://sap.github.io/ui5-tooling/) (`ui5 serve`).
10 |
11 | 
12 |
13 | Then, kick off the test(s) via `wdio` (that, for best practice, should be wrapped in an npm script. Here: `npm run test:ui5tooling`).
14 |
15 | 
16 |
17 | To start the test without creating an npm script:
18 |
19 | ```shell
20 | $> node_modules/.bin/wdio run wdio-ui5tooling.conf.js "--spec" "basic"
21 | ```
22 |
23 | Voilà!
24 |
--------------------------------------------------------------------------------
/docs/site.webmanifest:
--------------------------------------------------------------------------------
1 | {"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
--------------------------------------------------------------------------------
/docs/test-pyramid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ui5-community/wdi5/93a69ff20d1563e34e99c9e371baa13cda0c8886/docs/test-pyramid.png
--------------------------------------------------------------------------------
/docs/wdi5-control-instantiation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ui5-community/wdi5/93a69ff20d1563e34e99c9e371baa13cda0c8886/docs/wdi5-control-instantiation.png
--------------------------------------------------------------------------------
/docs/wdi5-testing-framework.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ui5-community/wdi5/93a69ff20d1563e34e99c9e371baa13cda0c8886/docs/wdi5-testing-framework.png
--------------------------------------------------------------------------------
/docs/wdio.md:
--------------------------------------------------------------------------------
1 | # Element
2 |
3 | ## Function List
4 |
5 | ```Json
6 | "$",
7 | "$$",
8 | "addValue",
9 | "clearValue",
10 | "click",
11 | "custom$$",
12 | "custom$",
13 | "doubleClick",
14 | "dragAndDrop",
15 | "getAttribute",
16 | "getCSSProperty",
17 | "getComputedLabel",
18 | "getComputedRole",
19 | "getHTML",
20 | "getLocation",
21 | "getProperty",
22 | "getSize",
23 | "getTagName",
24 | "getText",
25 | "getValue",
26 | "isClickable",
27 | "isDisplayed",
28 | "isDisplayedInViewport",
29 | "isEnabled",
30 | "isEqual",
31 | "isExisting",
32 | "isFocused",
33 | "isSelected",
34 | "moveTo",
35 | "nextElement",
36 | "parentElement",
37 | "previousElement",
38 | "react$$",
39 | "react$",
40 | "saveScreenshot",
41 | "scrollIntoView",
42 | "selectByAttribute",
43 | "selectByIndex",
44 | "selectByVisibleText",
45 | "setValue",
46 | "shadow$$",
47 | "shadow$",
48 | "touchAction",
49 | "waitForClickable",
50 | "waitForDisplayed",
51 | "waitForEnabled",
52 | "waitForExist",
53 | "waitUntil"
54 | ```
55 |
--------------------------------------------------------------------------------
/examples/cucumber/features/login.feature:
--------------------------------------------------------------------------------
1 | Feature: The Internet Guinea Pig Website
2 |
3 | Scenario Outline: As a user, I can log into the secure area
4 |
5 | Given I am on the login page
6 | When I login with and
7 | Then I should see a flash message saying
8 |
9 | Examples:
10 | | username | password | message |
11 | | tomsmith | SuperSecretPassword! | You logged into a secure area! |
12 | | foobar | barfoo | Your username is invalid! |
13 |
--------------------------------------------------------------------------------
/examples/cucumber/features/pageobjects/login.page.js:
--------------------------------------------------------------------------------
1 | const Page = require("./page")
2 |
3 | /**
4 | * sub page containing specific selectors and methods for a specific page
5 | */
6 | class LoginPage extends Page {
7 | /**
8 | * define selectors using getter methods
9 | */
10 | get inputUsername() {
11 | return $("#username")
12 | }
13 |
14 | get inputPassword() {
15 | return $("#password")
16 | }
17 |
18 | get btnSubmit() {
19 | return $('button[type="submit"]')
20 | }
21 |
22 | /**
23 | * a method to encapsule automation code to interact with the page
24 | * e.g. to login using username and password
25 | */
26 | async login(username, password) {
27 | await this.inputUsername.setValue(username)
28 | await this.inputPassword.setValue(password)
29 | await this.btnSubmit.click()
30 | }
31 |
32 | /**
33 | * overwrite specific options to adapt it to page object
34 | */
35 | open() {
36 | return super.open("login")
37 | }
38 | }
39 |
40 | module.exports = new LoginPage()
41 |
--------------------------------------------------------------------------------
/examples/cucumber/features/pageobjects/page.js:
--------------------------------------------------------------------------------
1 | /**
2 | * main page object containing all methods, selectors and functionality
3 | * that is shared across all page objects
4 | */
5 | module.exports = class Page {
6 | /**
7 | * Opens a sub page of the page
8 | * @param path path of the sub page (e.g. /path/to/page.html)
9 | */
10 | open(path) {
11 | return browser.url(`https://the-internet.herokuapp.com/${path}`)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/examples/cucumber/features/pageobjects/secure.page.js:
--------------------------------------------------------------------------------
1 | const Page = require("./page")
2 |
3 | /**
4 | * sub page containing specific selectors and methods for a specific page
5 | */
6 | class SecurePage extends Page {
7 | /**
8 | * define selectors using getter methods
9 | */
10 | get flashAlert() {
11 | return $("#flash")
12 | }
13 | }
14 |
15 | module.exports = new SecurePage()
16 |
--------------------------------------------------------------------------------
/examples/cucumber/features/step-definitions/steps.js:
--------------------------------------------------------------------------------
1 | const { Given, When, Then } = require("@wdio/cucumber-framework")
2 |
3 | const LoginPage = require("../pageobjects/login.page")
4 | const SecurePage = require("../pageobjects/secure.page")
5 |
6 | const pages = {
7 | login: LoginPage
8 | }
9 |
10 | Given(/^I am on the (\w+) page$/, async (page) => {
11 | await pages[page].open()
12 | })
13 |
14 | When(/^I login with (\w+) and (.+)$/, async (username, password) => {
15 | await LoginPage.login(username, password)
16 | })
17 |
18 | Then(/^I should see a flash message saying (.*)$/, async (message) => {
19 | await expect(SecurePage.flashAlert).toBeExisting()
20 | await expect(SecurePage.flashAlert).toHaveTextContaining(message)
21 | })
22 |
--------------------------------------------------------------------------------
/examples/cucumber/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "devDependencies": {
3 | "@wdio/cli": "^8",
4 | "@wdio/cucumber-framework": "^8",
5 | "@wdio/local-runner": "^8",
6 | "@wdio/mocha-framework": "^8",
7 | "@wdio/spec-reporter": "^8",
8 | "wdio-ui5-service": "*"
9 | },
10 | "scripts": {
11 | "test": "wdio run ./wdio.conf.js"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/examples/fe-app/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: "eslint:recommended",
3 | env: {
4 | node: true,
5 | es6: true,
6 | jest: true
7 | },
8 | parserOptions: {
9 | ecmaVersion: 2017
10 | },
11 | globals: {
12 | browser: true,
13 | before: true,
14 | SELECT: true,
15 | INSERT: true,
16 | UPDATE: true,
17 | DELETE: true,
18 | CREATE: true,
19 | DROP: true,
20 | cds: true
21 | },
22 | rules: {
23 | "no-console": "off",
24 | "require-atomic-updates": "off"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/examples/fe-app/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | target
--------------------------------------------------------------------------------
/examples/fe-app/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "types": ["node", "@wdio/globals/types", "@wdio/mocha-framework", "wdio-ui5-service/cjs", "expect-webdriverio"]
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/examples/fe-app/karma.conf.js:
--------------------------------------------------------------------------------
1 | module.exports = function (config) {
2 | config.set({
3 | frameworks: ["ui5"],
4 | ui5: {},
5 |
6 | browsers: ["ChromeCustom"],
7 | customLaunchers: {
8 | ChromeCustom: {
9 | base: "Chrome",
10 | flags: ["--start-maximized"]
11 | },
12 | ChromeCustomHeadless: {
13 | base: "ChromeHeadless",
14 | flags: ["--window-size=1600,900"]
15 | }
16 | },
17 |
18 | browserConsoleLogOptions: {
19 | level: "warn"
20 | },
21 |
22 | preprocessors: {
23 | "**/webapp/!(test|localService)/**/*.js": ["coverage"]
24 | },
25 | coverageReporter: {
26 | includeAllSources: true,
27 | reporters: [
28 | {
29 | type: "html",
30 | dir: "./target/coverage"
31 | },
32 | {
33 | type: "text"
34 | }
35 | ]
36 | },
37 |
38 | junitReporter: {
39 | outputDir: "./target/junit",
40 | outputFile: "TEST-qunit.xml",
41 | suite: "",
42 | useBrowserName: true
43 | },
44 | htmlReporter: {
45 | outputFile: "./target/html/QUnit.html",
46 |
47 | // Optional
48 | pageTitle: "Test Results",
49 | subPageTitle: "Detailed test results",
50 | groupSuites: true,
51 | useCompactStyle: true,
52 | useLegacyStyle: true,
53 | showOnlyFailed: false
54 | },
55 | reporters: ["progress", "coverage", "junit", "html"]
56 | })
57 | }
58 |
--------------------------------------------------------------------------------
/examples/fe-app/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "incidents",
3 | "version": "0.0.1",
4 | "description": "SAP Fiori elements",
5 | "keywords": [
6 | "ui5",
7 | "openui5",
8 | "sapui5"
9 | ],
10 | "main": "webapp/index.html",
11 | "scripts": {
12 | "deploy": "npx -p @sap/ux-ui5-tooling fiori add deploy-config cf",
13 | "test": "karma start --singleRun",
14 | "test:wdi5": "wdio run wdio.conf.js --headless",
15 | "serve": "ui5 serve --port 8088"
16 | },
17 | "devDependencies": {
18 | "@ui5/cli": "^3",
19 | "@wdio/cli": "^8",
20 | "detect-libc": "^2.0.3",
21 | "@wdio/local-runner": "^8",
22 | "@wdio/mocha-framework": "^8",
23 | "@wdio/spec-reporter": "^8",
24 | "wdio-ui5-service": "*",
25 | "@sap-ux/ui5-middleware-fe-mockserver": "latest"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/examples/fe-app/ui5.yaml:
--------------------------------------------------------------------------------
1 | specVersion: "2.6"
2 | metadata:
3 | name: incidents
4 | # allowSapInternal: true # //> only for sap.fe.templates below
5 | type: application
6 | ### we bootstrap from cdn again to get prebuilt libs (preloads)
7 | # framework:
8 | # name: "SAPUI5"
9 | # version: "1.97.2"
10 | # libraries:
11 | # - name: sap.ui.core
12 | # - name: sap.m
13 | # - name: sap.ushell
14 | # - name: sap.fe.templates
15 | # - name: sap.ui.dt
16 | # - name: sap.ui.rta
17 | # - name: sap.fe.test
18 | # # - name: sap.suite.ui.generic.template
19 | # - name: sap.gantt
20 | # - name: themelib_sap_fiori_3
21 | server:
22 | customMiddleware:
23 | - name: sap-fe-mockserver
24 | beforeMiddleware: csp
25 | configuration:
26 | service:
27 | urlBasePath: "/incident"
28 | name: ""
29 | metadataXmlPath: "./webapp/localService/metadata.xml"
30 | mockdataRootPath: "./webapp/localService/mockdata"
31 | generateMockData: true
32 |
--------------------------------------------------------------------------------
/examples/fe-app/wdio.conf.js:
--------------------------------------------------------------------------------
1 | const { join } = require("path")
2 |
3 | exports.config = {
4 | wdi5: {
5 | screenshotPath: join("app", "incidents", "webapp", "wdi5-test", "__screenshots__"),
6 | logLevel: "verbose", // error | verbose | silent
7 | waitForUI5Timeout: 30000
8 | },
9 | //// wdio runner config
10 | specs: [join("webapp", "wdi5-test", "**/*.test.js")],
11 | // Patterns to exclude.
12 | exclude: [],
13 | //// capabilities ("browser") config
14 | maxInstances: 10,
15 | capabilities: [
16 | {
17 | // overwrite global "maxInstances"
18 | maxInstances: 5,
19 | browserName: "chrome",
20 | acceptInsecureCerts: true,
21 | "goog:chromeOptions": {
22 | args:
23 | process.argv.indexOf("--headless") > -1
24 | ? ["window-size=1440,800", "--headless"]
25 | : process.argv.indexOf("--debug") > -1
26 | ? ["window-size=1920,1280", "--auto-open-devtools-for-tabs"]
27 | : ["window-size=1440,800"]
28 | }
29 | }
30 | ],
31 | //// test config
32 | // Level of logging verbosity: trace | debug | info | warn | error | silent
33 | logLevel: "error",
34 | bail: 0,
35 | baseUrl: "http://localhost:8088/index.html#fe-lrop-v4",
36 |
37 | waitforTimeout: 20000,
38 | connectionRetryTimeout: 120000,
39 | connectionRetryCount: 3,
40 |
41 | services: ["ui5"],
42 |
43 | framework: "mocha",
44 | mochaOpts: {
45 | ui: "bdd",
46 | timeout: 120000
47 | },
48 | reporters: ["spec"]
49 | }
50 |
--------------------------------------------------------------------------------
/examples/fe-app/webapp/Component.js:
--------------------------------------------------------------------------------
1 | sap.ui.define(["sap/fe/core/AppComponent"], function (AppComponent) {
2 | "use strict"
3 |
4 | return AppComponent.extend("sap.fe.demo.incidents.Component", {
5 | metadata: {
6 | manifest: "json"
7 | }
8 | })
9 | })
10 |
--------------------------------------------------------------------------------
/examples/fe-app/webapp/ext/controller/ProcessFlow.controller.js:
--------------------------------------------------------------------------------
1 | //the UI5_184 view controller pair has to be used for UI5 version >= 1.84
2 | sap.ui.define(
3 | [
4 | "sap/ui/core/mvc/Controller",
5 | "sap/fe/core/controllerextensions/RoutingListener",
6 | "sap/ui/model/json/JSONModel",
7 | "sap/m/MessageToast"
8 | ],
9 | function (Controller, RoutingListener, JSONModel, MessageToast) {
10 | "use strict"
11 |
12 | return Controller.extend("myController", {
13 | routingListener: RoutingListener,
14 |
15 | constructor: function () {
16 | Controller.apply(this, arguments)
17 | // No need to instantiate the extension, it's done automatically
18 | },
19 |
20 | onInit: function () {
21 | var oView = this.getView()
22 | this.oProcessFlow1 = oView.byId("processflow1")
23 |
24 | var sDataPath = jQuery.sap.getModulePath(
25 | "sap.fe.demo.incidents.ext.controller",
26 | "/ProcessFlowLanesAndNodes.json"
27 | )
28 | var oModelPf1 = new JSONModel(sDataPath)
29 | oView.setModel(oModelPf1, "ProcessFlowModel")
30 | oModelPf1.attachRequestCompleted(this.oProcessFlow1.updateModel.bind(this.oProcessFlow1))
31 | },
32 |
33 | onNodePress: function (event) {
34 | MessageToast.show("Node " + event.getParameters().getNodeId() + " has been clicked.")
35 | }
36 | })
37 | }
38 | )
39 |
--------------------------------------------------------------------------------
/examples/fe-app/webapp/ext/fragment/CustomSectionGantt.fragment.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/examples/fe-app/webapp/ext/fragment/CustomSectionGantt.js:
--------------------------------------------------------------------------------
1 | sap.ui.define(
2 | ["sap/ui/core/mvc/Controller", "sap/ui/model/json/JSONModel", "sap/gantt/misc/Format"],
3 | function (Controller, JSONModel, Format) {
4 | "use strict"
5 |
6 | return {
7 | fnTimeConverter: function (sTimestamp) {
8 | return Format.abapTimestampToDate(sTimestamp)
9 | }
10 | }
11 | }
12 | )
13 |
--------------------------------------------------------------------------------
/examples/fe-app/webapp/i18n/i18n.properties:
--------------------------------------------------------------------------------
1 | # This is the resource bundle for incidents
2 |
3 | #Texts for manifest.json
4 |
5 | #XTIT: Application name
6 | appTitle=Incidents Management
7 |
8 | #YDES: Application description
9 | appDescription=SAP Fiori elements
10 |
--------------------------------------------------------------------------------
/examples/fe-app/webapp/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | {{appTitle}}
8 |
9 |
24 |
25 |
26 |
35 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/examples/fe-app/webapp/localService/mockdata/Category.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "code": "security",
4 | "name": "Security",
5 | "descr": "Reporting of security or login issues"
6 | },
7 | {
8 | "code": "software",
9 | "name": "Software",
10 | "descr": "Operating system and Standard Software issues"
11 | },
12 | {
13 | "code": "hardware",
14 | "name": "Hardware",
15 | "descr": "Hardware issues related to Desktop, Notebook, Beamer, Monitor, etc."
16 | },
17 | {
18 | "code": "telephony",
19 | "name": "Telephony",
20 | "descr": "Mobile Phone, Softphone, Deskphone issues"
21 | },
22 | {
23 | "code": "database",
24 | "name": "Database",
25 | "descr": "In-Memory Database, RDBMS, OODBMS issues"
26 | },
27 | {
28 | "code": "inquiry",
29 | "name": "Inquiry",
30 | "descr": "Inquiries related to Facility Management, Hard-/Software Ordering, Travel Booking"
31 | }
32 | ]
33 |
--------------------------------------------------------------------------------
/examples/fe-app/webapp/localService/mockdata/IncidentStatus.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "code": "new",
4 | "name": "New",
5 | "descr": "An incident that has been logged but not yet worked on."
6 | },
7 | {
8 | "code": "assigned",
9 | "name": "Assigned",
10 | "descr": "Incident has been asssigned to a technician"
11 | },
12 | {
13 | "code": "in process",
14 | "name": "In Process",
15 | "descr": "Case is being actively worked on"
16 | },
17 | {
18 | "code": "on hold",
19 | "name": "On Hold",
20 | "descr": "Incident has been put on hold"
21 | },
22 | {
23 | "code": "resolved",
24 | "name": "Resolved",
25 | "descr": "Resolution has been found"
26 | },
27 | {
28 | "code": "closed",
29 | "name": "Closed",
30 | "descr": "Incident was acknowleged closed by end user"
31 | }
32 | ]
33 |
--------------------------------------------------------------------------------
/examples/fe-app/webapp/localService/mockdata/Individual.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "ID": "067460c5-196c-4783-9563-ede797399da8",
4 | "fullName": "Susanne Muster",
5 | "emailAddress": "susanne.muster@sap.com"
6 | },
7 | {
8 | "ID": "efec3e9f-ceea-4d17-80a7-50073f71c322",
9 | "fullName": "Manfred Muster",
10 | "emailAddress": "manfred.muster@sap.com"
11 | },
12 | {
13 | "ID": "efec3e9f-ceea-4d17-80a7-50073f71c323",
14 | "fullName": "Max Muster",
15 | "emailAddress": "Max.Muster@sap.com"
16 | },
17 | {
18 | "ID": "efec3e9f-ceea-4d17-80a7-50073f71c324",
19 | "fullName": "Maxime Muster",
20 | "emailAddress": "Maxime.Muster@sap.com"
21 | },
22 | {
23 | "ID": "efec3e9f-ceea-4d17-80a7-50073f71c325",
24 | "fullName": "Erika Muster",
25 | "emailAddress": "Erika.Muster@sap.com"
26 | }
27 | ]
28 |
--------------------------------------------------------------------------------
/examples/fe-app/webapp/localService/mockdata/Priority.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "code": "3_low",
4 | "name": "Low",
5 | "descr": ""
6 | },
7 | {
8 | "code": "2_medium",
9 | "name": "Medium",
10 | "descr": ""
11 | },
12 | {
13 | "code": "1_high",
14 | "name": "High",
15 | "descr": ""
16 | }
17 | ]
18 |
--------------------------------------------------------------------------------
/examples/fe-app/webapp/test/integration/Opa.qunit.js:
--------------------------------------------------------------------------------
1 | sap.ui.require(
2 | [
3 | "sap/fe/test/JourneyRunner",
4 | "sap/fe/demo/incidents/test/integration/pages/MainListReport",
5 | "sap/fe/demo/incidents/test/integration/pages/MainObjectPage",
6 | "sap/fe/demo/incidents/test/integration/OpaJourney"
7 | ],
8 | function (JourneyRunner, MainListReport, MainObjectPage, Journey) {
9 | "use strict"
10 | var JourneyRunner = new JourneyRunner({
11 | // start index.html in web folder
12 | launchUrl: sap.ui.require.toUrl("sap/fe/demo/incidents") + "/index.html#fe-lrop-v4"
13 | })
14 |
15 | JourneyRunner.run(
16 | {
17 | pages: { onTheMainPage: MainListReport, onTheDetailPage: MainObjectPage }
18 | },
19 | Journey.run
20 | )
21 | }
22 | )
23 |
--------------------------------------------------------------------------------
/examples/fe-app/webapp/test/integration/OpaJourney.js:
--------------------------------------------------------------------------------
1 | sap.ui.define(["sap/ui/test/opaQunit"], function (opaTest) {
2 | "use strict"
3 |
4 | var Journey = {
5 | run: function () {
6 | QUnit.module("Sample journey")
7 |
8 | opaTest("#000: Start", function (Given, When, Then) {
9 | Given.iResetTestData().and.iStartMyApp()
10 | Then.onTheMainPage.iSeeThisPage()
11 | })
12 |
13 | opaTest("#1: ListReport: Check List Report Page loads", function (Given, When, Then) {
14 | When.onTheMainPage.onFilterBar().iExecuteSearch()
15 | Then.onTheMainPage.onTable().iCheckRows()
16 | })
17 |
18 | opaTest("#2: Object Page: Check Object Page loads", function (Given, When, Then) {
19 | Then.onTheMainPage.iSeeThisPage()
20 | })
21 |
22 | opaTest("#999: Tear down", function (Given, When, Then) {
23 | Given.iTearDownMyApp()
24 | })
25 | }
26 | }
27 |
28 | return Journey
29 | })
30 |
--------------------------------------------------------------------------------
/examples/fe-app/webapp/test/integration/opaTests.qunit.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{appTitle}}
5 |
6 |
7 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/examples/fe-app/webapp/test/integration/pages/MainListReport.js:
--------------------------------------------------------------------------------
1 | sap.ui.define(["sap/fe/test/ListReport"], function (ListReport) {
2 | "use strict"
3 |
4 | var AdditionalCustomListReportDefinition = {
5 | actions: {},
6 | assertions: {}
7 | }
8 |
9 | return new ListReport(
10 | {
11 | appId: "sap.fe.demo.incidents",
12 | componentId: "IncidentsList",
13 | entitySet: "Incidents"
14 | },
15 | AdditionalCustomListReportDefinition
16 | )
17 | })
18 |
--------------------------------------------------------------------------------
/examples/fe-app/webapp/test/integration/pages/MainObjectPage.js:
--------------------------------------------------------------------------------
1 | sap.ui.define(["sap/fe/test/ObjectPage"], function (ObjectPage) {
2 | "use strict"
3 |
4 | // OPTIONAL
5 | var AdditionalCustomObjectPageDefinition = {
6 | actions: {},
7 | assertions: {}
8 | }
9 |
10 | return new ObjectPage(
11 | {
12 | appId: "sap.fe.demo.incidents", // MANDATORY: Compare sap.app.id in manifest.json
13 | componentId: "IncidentsObjectPage", // MANDATORY: Compare sap.ui5.routing.targets.id in manifest.json
14 | entitySet: "Incidents" // MANDATORY: Compare entityset in manifest.json
15 | },
16 | AdditionalCustomObjectPageDefinition
17 | )
18 | })
19 |
--------------------------------------------------------------------------------
/examples/fe-app/webapp/test/testsuite.qunit.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | QUnit test suite for Worklist
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/examples/fe-app/webapp/test/testsuite.qunit.js:
--------------------------------------------------------------------------------
1 | window.suite = function () {
2 | "use strict"
3 |
4 | // eslint-disable-next-line
5 | var oSuite = new parent.jsUnitTestSuite(),
6 | sContextPath = location.pathname.substring(0, location.pathname.lastIndexOf("/") + 1)
7 | oSuite.addTestPage(sContextPath + "integration/opaTests.qunit.html")
8 |
9 | return oSuite
10 | }
11 |
--------------------------------------------------------------------------------
/examples/ui5-js-app-esm/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "types": [
4 | "node",
5 | "@openui5/types",
6 | "@wdio/globals/types",
7 | "@wdio/mocha-framework",
8 | "wdio-ui5-service/esm",
9 | "expect-webdriverio"
10 | ]
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/examples/ui5-js-app-esm/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ui5-app-esm",
3 | "version": "0.8.15-notimportant",
4 | "private": true,
5 | "description": "sample ESM ui5 app for testing wdi5",
6 | "license": "UNLICENSED",
7 | "author": "j&s-soft GmbH",
8 | "main": "webapp/index.html",
9 | "type": "module",
10 | "scripts": {
11 | "start": "ui5 serve -p 8082",
12 | "wdi5": "wdio ./webapp/test/e2e/wdio.conf.js"
13 | },
14 | "devDependencies": {
15 | "@ui5/cli": "^3",
16 | "@types/sinon": "^17.0.3",
17 | "sinon": "^17.0.1",
18 | "@wdio/cli": "^8",
19 | "@wdio/local-runner": "^8",
20 | "@wdio/mocha-framework": "^8",
21 | "@wdio/spec-reporter": "^8",
22 | "ui5-middleware-simpleproxy": "latest",
23 | "wdio-ui5-service": "*"
24 | },
25 | "dependencies": {
26 | "@wdio/sauce-service": "^8"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/examples/ui5-js-app-esm/ui5.yaml:
--------------------------------------------------------------------------------
1 | specVersion: "2.0"
2 | metadata:
3 | name: ui5-app
4 | type: application
5 | server:
6 | customMiddleware:
7 | - name: ui5-middleware-simpleproxy
8 | afterMiddleware: compression
9 | mountPath: /V2
10 | configuration:
11 | baseUri: "https://services.odata.org/V2"
12 | strictSSL: false
13 |
--------------------------------------------------------------------------------
/examples/ui5-js-app-esm/webapp/Component.js:
--------------------------------------------------------------------------------
1 | sap.ui.define(
2 | [
3 | "sap/ui/core/UIComponent",
4 | "sap/ui/Device",
5 | "test/Sample/model/models",
6 | "sap/ui/core/ComponentSupport" // make sure to include the ComponentSupport in the bundle
7 | ],
8 | function (UIComponent, Device, models) {
9 | "use strict"
10 |
11 | return UIComponent.extend("test.Sample.Component", {
12 | metadata: {
13 | manifest: "json"
14 | },
15 |
16 | /**
17 | * The component is initialized by UI5 automatically during the startup of the app and calls the init method once.
18 | * @public
19 | * @override
20 | */
21 | init: function () {
22 | // call the base component's init function
23 | UIComponent.prototype.init.apply(this, arguments)
24 |
25 | // enable routing
26 | this.getRouter().initialize()
27 |
28 | // set the device model
29 | this.setModel(models.createDeviceModel(), "device")
30 | }
31 | })
32 | }
33 | )
34 |
--------------------------------------------------------------------------------
/examples/ui5-js-app-esm/webapp/controller/App.controller.js:
--------------------------------------------------------------------------------
1 | sap.ui.define(["test/Sample/controller/BaseController"], function (Controller) {
2 | return Controller.extend("test.Sample.controller.App", {
3 | onInit: function () {}
4 | })
5 | })
6 |
--------------------------------------------------------------------------------
/examples/ui5-js-app-esm/webapp/controller/Other.controller.js:
--------------------------------------------------------------------------------
1 | sap.ui.define(
2 | ["test/Sample/controller/BaseController", "sap/m/MessageToast", "sap/m/StandardListItem"],
3 | function (Controller, MessageToast, StandardListItem) {
4 | "use strict"
5 |
6 | return Controller.extend("test.Sample.controller.Other", {
7 | onInit: function () {},
8 |
9 | onItemPress(oEvent) {
10 | this.getView().byId("idTextFieldClickResult").setText(oEvent.getParameter("listItem").data("key"))
11 |
12 | MessageToast.show(oEvent.getParameter("listItem").data("key"))
13 | },
14 |
15 | onAddLineItem(oEvent) {
16 | this.getView()
17 | .byId("PeopleList")
18 | .addItem(
19 | new StandardListItem({
20 | title: "FirstName LastName",
21 | type: "Navigation"
22 | })
23 | )
24 | }
25 | })
26 | }
27 | )
28 |
--------------------------------------------------------------------------------
/examples/ui5-js-app-esm/webapp/css/style.css:
--------------------------------------------------------------------------------
1 | /* Enter your custom styles here */
2 |
--------------------------------------------------------------------------------
/examples/ui5-js-app-esm/webapp/i18n/i18n.properties:
--------------------------------------------------------------------------------
1 | appTitle=Sample
2 | appDescription=App Description
3 |
4 | ###
5 |
6 | startPage.userButton.text=User Test Text
7 | startPage.navButton.text=to Other view
8 | startPage.currentUI5.text=most current UI5 version available
9 | startPage.title.text=UI5 demo
10 | startPage.text.username=Username
11 |
12 | ###
13 |
14 | otherPage.title=Another View...
15 | otherPage.listHeader=...bites the dust!
16 |
17 | ###
18 |
19 | dialog.title=Here we are!
20 | dialog.text=born to be kings
21 | dialog.close=cut
22 |
23 |
--------------------------------------------------------------------------------
/examples/ui5-js-app-esm/webapp/i18n/i18n_en.properties:
--------------------------------------------------------------------------------
1 | appTitle=Sample
2 | appDescription=App Description
3 |
4 | ###
5 |
6 | startPage.navButton.text=to Other view
7 | startPage.currentUI5.text=most current UI5 version available
8 | startPage.title.text=UI5 demo
9 | startPage.text.username=Username
10 |
11 | ###
12 |
13 | otherPage.title=Another View...
14 | otherPage.listHeader=...bites the dust!
15 |
16 | ###
17 |
18 | dialog.title=Here we are!
19 | dialog.text=born to be kings
20 | dialog.close=cut
--------------------------------------------------------------------------------
/examples/ui5-js-app-esm/webapp/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Sample UI5 Application
7 |
8 |
12 |
13 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/examples/ui5-js-app-esm/webapp/model/countries.json:
--------------------------------------------------------------------------------
1 | {
2 | "CountriesCollection": [
3 | {
4 | "key": "DZ",
5 | "text": "Algeria"
6 | },
7 | {
8 | "key": "AR",
9 | "text": "Argentina"
10 | },
11 | {
12 | "key": "AU",
13 | "text": "Australia"
14 | },
15 | {
16 | "key": "AT",
17 | "text": "Austria"
18 | },
19 | {
20 | "key": "BH",
21 | "text": "Bahrain"
22 | },
23 | {
24 | "key": "BE",
25 | "text": "Belgium"
26 | },
27 | {
28 | "key": "BA",
29 | "text": "Bosnia and Herzegovina"
30 | },
31 | {
32 | "key": "BR",
33 | "text": "Brazil"
34 | }
35 | ]
36 | }
37 |
--------------------------------------------------------------------------------
/examples/ui5-js-app-esm/webapp/model/models.js:
--------------------------------------------------------------------------------
1 | sap.ui.define(["sap/ui/model/json/JSONModel", "sap/ui/Device"], function (JSONModel, Device) {
2 | "use strict"
3 |
4 | return {
5 | createDeviceModel: function () {
6 | var oModel = new JSONModel(Device)
7 | oModel.setDefaultBindingMode("OneWay")
8 | return oModel
9 | }
10 | }
11 | })
12 |
--------------------------------------------------------------------------------
/examples/ui5-js-app-esm/webapp/proxyrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "/V2/": {
3 | "target": "https://services.odata.org/V2/",
4 | "secure": false,
5 | "changeOrigin": true
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/examples/ui5-js-app-esm/webapp/test/e2e/esm.test.js:
--------------------------------------------------------------------------------
1 | import { wdi5 } from "wdio-ui5-service"
2 | import * as sinon from "sinon"
3 |
4 | describe("ui5 basic", () => {
5 | it("should use the ESM style logger", () => {
6 | const logSpy = sinon.spy(console, "log")
7 | const Logger = wdi5.getLogger("esm!")
8 | Logger.log("Hello ESM World!")
9 | expect(logSpy.getCall(0).args[1]).toContain("esm!")
10 | logSpy.restore()
11 | })
12 |
13 | it("window should have the right title", async () => {
14 | const title = await browser.getTitle()
15 | expect(title).toEqual("Sample UI5 Application")
16 | })
17 |
18 | it("wdi5 should use a control selector with dots and colons", async () => {
19 | const selector = {
20 | selector: {
21 | id: "Title::NoAction.h1",
22 | viewName: "test.Sample.view.Main"
23 | }
24 | }
25 | const text = await /** @type {import("wdio-ui5-service/esm/lib/wdi5-control.js").WDI5Control} */ (
26 | await browser.asControl(selector)
27 | ).getText()
28 | expect(text).toEqual("UI5 demo")
29 | })
30 | })
31 |
--------------------------------------------------------------------------------
/examples/ui5-js-app-esm/webapp/test/e2e/wdio.conf.js:
--------------------------------------------------------------------------------
1 | export const config = {
2 | wdi5: {
3 | logLevel: "verbose"
4 | },
5 | baseUrl: "http://localhost:8082/index.html",
6 |
7 | services: ["ui5"],
8 | specs: ["./**/*.test.js"],
9 | maxInstances: 1,
10 | capabilities: [
11 | {
12 | maxInstances: 1,
13 | browserName: "chrome",
14 | "goog:chromeOptions": {
15 | args:
16 | process.argv.indexOf("--headless") > -1
17 | ? ["window-size=1440,800", "--headless"]
18 | : ["window-size=1440,800"]
19 | }
20 | }
21 | ],
22 | logLevel: "error",
23 |
24 | reporters: ["spec"],
25 |
26 | framework: "mocha"
27 | }
28 |
--------------------------------------------------------------------------------
/examples/ui5-js-app-esm/webapp/view/App.view.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/examples/ui5-js-app-esm/webapp/view/Dialog.fragment.xml:
--------------------------------------------------------------------------------
1 |
3 |
15 |
--------------------------------------------------------------------------------
/examples/ui5-js-app-esm/webapp/view/Other.view.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
11 |
12 |
16 |
19 |
20 |
21 |
23 |
24 |
26 |
29 |
30 |
31 |
34 |
35 |
36 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/examples/ui5-js-app/e2e-test-config/wdi5-multiversion.js:
--------------------------------------------------------------------------------
1 | const fsExtra = require("fs-extra")
2 | const path = require("path")
3 | const replace = require("replace-in-file")
4 | const { Launcher } = require("@wdio/cli")
5 |
6 | // lts versions (> 1.60)
7 | // empty string will get the newest Version which can be a "SNAPSHOT" version
8 | const versions = ["", "1.71", "1.84", "1.96"]
9 |
10 | ;(async () => {
11 | for (const version of versions) {
12 | // create an index.html for bootstrapping per version
13 | const targetIndex = path.resolve(__dirname, `../webapp/index-${version}.html`)
14 | const bootstrapSrc = `https://ui5.sap.com/${version}/resources/sap-ui-core.js`
15 | fsExtra.copySync(path.resolve(__dirname, `../webapp/index.html`), targetIndex)
16 | const optionsIndex = {
17 | files: targetIndex,
18 | from: [/src=\".*\"/, /"sap_horizon"/],
19 | to: [`src="${bootstrapSrc}"`, "sap_belize"]
20 | }
21 | await replace(optionsIndex)
22 | console.log(`created index-${version}!`)
23 |
24 | // create a wdio/wdi5 config per version
25 | const targetWdioConf = path.resolve(__dirname, `wdio-wdi5-ui5-${version}.conf.js`)
26 | fsExtra.copySync(path.resolve(__dirname, "wdio-webserver.conf.js"), targetWdioConf)
27 | const optionsWdioConf = {
28 | files: targetWdioConf,
29 | from: [/8888"/, /specs: \[.*\]/],
30 | to: [
31 | `8888/index-${version}"`, // this is only b/c of the "soerver" webserver in use...
32 | `specs: ["${path.resolve(__dirname, "../webapp/test/e2e/properties-matcher.test.js")}"]`
33 | ]
34 | }
35 | await replace(optionsWdioConf)
36 | console.log(`created wdio-wdi5-ui5-${version}.conf.js`)
37 |
38 | // run it
39 | const wdio = new Launcher(targetWdioConf)
40 | //> REVISIT: this is only necessary to wait for the async constructor to resolve
41 | //> seehttps://github.com/webdriverio/webdriverio/pull/10607
42 | await new Promise((resolve) => setTimeout(resolve, 1000))
43 | await wdio.run().then((code) => {
44 | if (code === 1) {
45 | process.exit(1)
46 | }
47 | })
48 | }
49 | })()
50 |
--------------------------------------------------------------------------------
/examples/ui5-js-app/e2e-test-config/wdi5-urlDeprecation.conf.js:
--------------------------------------------------------------------------------
1 | const { join } = require("path")
2 | const { baseConfig } = require("./wdio.base.conf")
3 | const merge = require("deepmerge")
4 |
5 | const _config = {
6 | // check that the url property still works even though it is deprecated
7 | wdi5: {
8 | url: "#"
9 | },
10 | specs: [join("..", "webapp", "test", "e2e", "**/hash-nav.test.js")],
11 | exclude: [
12 | join("..", "webapp", "test", "e2e", "ui5-late.test.js"),
13 | join("webapp", "test", "e2e", "multiremote.test.js")
14 | ],
15 | baseUrl: "http://localhost:8888"
16 | }
17 |
18 | exports.config = merge(baseConfig, _config)
19 |
--------------------------------------------------------------------------------
/examples/ui5-js-app/e2e-test-config/wdio-docker-selenium.conf.js:
--------------------------------------------------------------------------------
1 | const { getBrowsers } = require("./scripts/getBrowsers")
2 |
3 | // TODO: use wdio.base.conf.js
4 | const chrome = {
5 | maxInstances: 1,
6 | browserName: "chrome",
7 | acceptInsecureCerts: true,
8 | "goog:chromeOptions": {
9 | w3c: false,
10 | args: ["--headless", "--no-sandbox"]
11 | }
12 | }
13 |
14 | const firefox = {
15 | maxInstances: 1,
16 | browserName: "firefox",
17 | "moz:firefoxOptions": {
18 | // flag to activate Firefox headless mode (see https://github.com/mozilla/geckodriver/blob/master/README.md#firefox-capabilities for more details about moz:firefoxOptions)
19 | args: ["-headless"]
20 | }
21 | }
22 |
23 | const _config = {
24 | specs: ["../test/e2e/basic.test.js"],
25 | hostname: "selenium-standalone", // tests running inside the container should connect to the same network
26 | port: 4444,
27 | runner: "local",
28 | path: "/wd/hub",
29 | maxInstances: 1,
30 | capabilities: [],
31 | wdi5: {
32 | screenshotPath: "report/screenshots",
33 | logLevel: "verbose" // error | verbose | silent
34 | },
35 | services: ["ui5"],
36 | logLevel: "info",
37 | logLevels: {
38 | webdriver: "info"
39 | },
40 | baseUrl: "http://test-app:8888",
41 | bail: 0,
42 | waitforTimeout: 10000,
43 | connectionRetryTimeout: 60000,
44 | connectionRetryCount: 3,
45 | framework: "mocha",
46 | reporters: ["spec"],
47 | mochaOpts: {
48 | ui: "bdd",
49 | timeout: 60000
50 | }
51 | }
52 |
53 | const browsers = getBrowsers()
54 |
55 | if (browsers) {
56 | if (browsers.includes("chrome")) {
57 | _config.capabilities.push(chrome)
58 | }
59 | if (browsers.includes("firefox")) {
60 | _config.capabilities.push(firefox)
61 | }
62 | } else {
63 | // nothing defined -> start all
64 | _config.capabilities.push(chrome)
65 |
66 | _config.capabilities.push(firefox)
67 | }
68 |
69 | exports.config = _config
70 |
--------------------------------------------------------------------------------
/examples/ui5-js-app/e2e-test-config/wdio-docker-standalone.conf.js:
--------------------------------------------------------------------------------
1 | const { baseConfig } = require("./wdio.base.conf")
2 | const { join } = require("path")
3 | const merge = require("deepmerge")
4 |
5 | // avoid multiple chrome sessions
6 | delete baseConfig.capabilities
7 |
8 | const _config = {
9 | wdi5: {
10 | screenshotPath: join("report", "screenshots")
11 | },
12 | baseUrl: "http://localhost:8080/index.html",
13 | maxInstances: 1,
14 | capabilities: [
15 | {
16 | maxInstances: 1,
17 | browserName: "chrome",
18 | "goog:chromeOptions": {
19 | args: [
20 | process.env.HEADFUL === undefined ? "--headless" : "--dummy",
21 | "--no-sandbox",
22 | "--disable-gpu",
23 | "--disable-dev-shm-usage",
24 | "--window-size=1920,1080"
25 | ]
26 | }
27 | }
28 | ],
29 | specs: ["../webapp/test/e2e/*.test.js"]
30 | }
31 |
32 | exports.config = merge(baseConfig, _config)
33 |
--------------------------------------------------------------------------------
/examples/ui5-js-app/e2e-test-config/wdio-multiremote.conf.js:
--------------------------------------------------------------------------------
1 | const { baseConfig } = require("./wdio.base.conf")
2 | const { join } = require("path")
3 | const merge = require("deepmerge")
4 |
5 | // avoid multiple chrome sessions
6 | delete baseConfig.capabilities
7 |
8 | const _config = {
9 | wdi5: {
10 | screenshotPath: join("report", "screenshots")
11 | },
12 | maxInstances: 1,
13 | capabilities: {
14 | one: {
15 | capabilities: {
16 | browserName: "chrome",
17 | acceptInsecureCerts: true,
18 | "goog:chromeOptions": {
19 | args:
20 | process.argv.indexOf("--headless") > -1
21 | ? ["window-size=1440,800", "--headless"]
22 | : process.argv.indexOf("--debug") > -1
23 | ? ["window-size=1920,1280", "--auto-open-devtools-for-tabs"]
24 | : ["window-size=1440,800"]
25 | }
26 | }
27 | },
28 | two: {
29 | capabilities: {
30 | browserName: "chrome",
31 | acceptInsecureCerts: true,
32 | "goog:chromeOptions": {
33 | args:
34 | process.argv.indexOf("--headless") > -1
35 | ? ["window-size=1440,800", "--headless"]
36 | : process.argv.indexOf("--debug") > -1
37 | ? ["window-size=1920,1280", "--auto-open-devtools-for-tabs"]
38 | : ["window-size=1440,800"]
39 | }
40 | }
41 | }
42 | },
43 | specs: ["../webapp/test/e2e/multiremote.test.js"]
44 | }
45 |
46 | exports.config = merge(baseConfig, _config)
47 |
--------------------------------------------------------------------------------
/examples/ui5-js-app/e2e-test-config/wdio-selenium-service.conf.js:
--------------------------------------------------------------------------------
1 | const { join } = require("path")
2 | const { baseConfig } = require("./wdio.base.conf")
3 | const merge = require("deepmerge")
4 |
5 | const _config = {
6 | specs: [join("..", "webapp", "test", "e2e", "**/*.test.js")],
7 | exclude: [join("..", "webapp", "test", "e2e", "ui5-late.test.js")],
8 | logLevel: "error",
9 | bail: 0,
10 | baseUrl: "http://localhost:8888",
11 | services: [["selenium-standalone", { drivers: { chrome: true, chromiumedge: "latest" } }]]
12 | }
13 |
14 | exports.config = merge(baseConfig, _config)
15 |
--------------------------------------------------------------------------------
/examples/ui5-js-app/e2e-test-config/wdio-ui5-late.conf.js:
--------------------------------------------------------------------------------
1 | const { join } = require("path")
2 | const { baseConfig } = require("./wdio.base.conf")
3 | const merge = require("deepmerge")
4 |
5 | const _config = {
6 | wdi5: {
7 | skipInjectUI5OnStart: true,
8 | waitForUI5Timeout: 654321
9 | },
10 | specs: [join("..", "webapp", "test", "e2e", "ui5-late.test.js")],
11 | baseUrl: "https://github.com/ui5-community/wdi5/"
12 | }
13 |
14 | exports.config = merge(baseConfig, _config)
15 |
--------------------------------------------------------------------------------
/examples/ui5-js-app/e2e-test-config/wdio-ui5tooling.conf.js:
--------------------------------------------------------------------------------
1 | const { join } = require("path")
2 | const { baseConfig } = require("./wdio.base.conf")
3 | const merge = require("deepmerge")
4 |
5 | const _config = {
6 | specs: [join("..", "webapp", "test", "e2e", "basic.test.js"), join("webapp", "test", "e2e", "hash-nav.test.js")],
7 | baseUrl: "http://localhost:8080/index.html?isui5toolingTest=true",
8 | wdi5: {
9 | ignoreAutoWaitUrls: [".*/Categories.*"]
10 | }
11 | }
12 |
13 | exports.config = merge(baseConfig, _config)
14 |
--------------------------------------------------------------------------------
/examples/ui5-js-app/e2e-test-config/wdio-webserver.conf.js:
--------------------------------------------------------------------------------
1 | const { join } = require("path")
2 | const { baseConfig } = require("./wdio.base.conf")
3 | const merge = require("deepmerge")
4 |
5 | const _config = {
6 | specs: [join("..", "webapp", "test", "e2e", "**/*.test.js")],
7 | exclude: [
8 | join("..", "webapp", "test", "e2e", "ui5-late.test.js"),
9 | join("..", "webapp", "test", "e2e", "multiremote.test.js")
10 | ],
11 | baseUrl: "http://localhost:8888"
12 | }
13 |
14 | exports.config = merge(baseConfig, _config)
15 |
--------------------------------------------------------------------------------
/examples/ui5-js-app/e2e-test-config/wdio.base.conf.js:
--------------------------------------------------------------------------------
1 | const { join } = require("path")
2 |
3 | exports.baseConfig = {
4 | wdi5: {
5 | screenshotPath: join("webapp", "test", "__screenshots__"),
6 | logLevel: "error",
7 | waitForUI5Timeout: 29000
8 | },
9 | maxInstances: 10,
10 | capabilities: [
11 | {
12 | maxInstances: 4,
13 | browserName: "chrome",
14 | acceptInsecureCerts: true,
15 | "goog:chromeOptions": {
16 | args:
17 | process.argv.indexOf("--headless") > -1
18 | ? ["window-size=1920,1280", "--headless"]
19 | : process.argv.indexOf("--debug") > -1
20 | ? ["window-size=1920,1280", "--auto-open-devtools-for-tabs"]
21 | : ["window-size=1920,1280"]
22 | }
23 | }
24 | ],
25 | logLevel: "error",
26 | bail: 0,
27 | baseUrl: "http://localhost:8888",
28 |
29 | waitforTimeout: 20000,
30 | connectionRetryTimeout: process.argv.indexOf("--debug") > -1 ? 1200000 : 120000,
31 | connectionRetryCount: 3,
32 |
33 | services: ["ui5"],
34 |
35 | framework: "mocha",
36 | mochaOpts: {
37 | ui: "bdd",
38 | timeout: process.argv.indexOf("--debug") > -1 ? 600000 : 90000
39 | },
40 | reporters: ["spec"]
41 | }
42 |
--------------------------------------------------------------------------------
/examples/ui5-js-app/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "types": [
4 | "node",
5 | "@openui5/types",
6 | "@wdio/globals/types",
7 | "@wdio/mocha-framework",
8 | "wdio-ui5-service/cjs",
9 | "expect-webdriverio"
10 | ]
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/examples/ui5-js-app/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ui5-app",
3 | "version": "0.8.15-notimportant",
4 | "private": true,
5 | "description": "sample ui5 app for testing wdi5",
6 | "license": "UNLICENSED",
7 | "author": "j&s-soft GmbH",
8 | "main": "webapp/index.html",
9 | "scripts": {
10 | "serve": "ui5 serve",
11 | "test": "run-s test:*",
12 | "test-h": "run-s \"test:* -- --headless\"",
13 | "start": "soerver -d ./webapp -p 8888 -x ./webapp/proxyrc.json",
14 | "test:lateInject": "wdio run e2e-test-config/wdio-ui5-late.conf.js",
15 | "test:ui5tooling": "wdio run e2e-test-config/wdio-ui5tooling.conf.js",
16 | "test:webserver": "wdio run e2e-test-config/wdio-webserver.conf.js",
17 | "test:multiremote": "wdio run e2e-test-config/wdio-multiremote.conf.js",
18 | "//>REVISIT:wdio-bug:test:multiversion": "node e2e-test-config/wdi5-multiversion.js",
19 | "test:urlDeprecation": "wdio run e2e-test-config/wdi5-urlDeprecation.conf.js",
20 | "test-selenium": "wdio run e2e-test-config/wdio-selenium-service.conf.js"
21 | },
22 | "devDependencies": {
23 | "@ui5/cli": "^3",
24 | "@wdio/cli": "^8",
25 | "@wdio/local-runner": "^8",
26 | "@wdio/mocha-framework": "^8",
27 | "@wdio/spec-reporter": "^8",
28 | "ui5-middleware-simpleproxy": "latest",
29 | "wdio-ui5-service": "*",
30 | "@sap-ux/ui5-middleware-fe-mockserver": "latest"
31 | },
32 | "dependencies": {
33 | "@wdio/sauce-service": "^8"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/examples/ui5-js-app/scripts/delayedMockServer.js:
--------------------------------------------------------------------------------
1 | const femiddleware = require("@sap-ux/ui5-middleware-fe-mockserver")
2 |
3 | module.exports = async function middleware(middlewareConfig) {
4 | const feMiddleware = await femiddleware(middlewareConfig)
5 | return async (req, res, next) => {
6 | if (req.originalUrl.startsWith("/V2") && req.originalUrl.includes("/Categories")) {
7 | await new Promise((resolve) => setTimeout(resolve, 1000))
8 | feMiddleware(req, res, next)
9 | return
10 | }
11 | next()
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/examples/ui5-js-app/scripts/getBrowsers.js:
--------------------------------------------------------------------------------
1 | /**
2 | * analyse browser arguments
3 | */
4 | exports.getBrowsers = function () {
5 | const availableBrowsers = ["chrome", "safari", "edge", "firefox"]
6 | const argv = process.argv
7 | let browsers = []
8 | const _browsers = argv.indexOf("--browsers")
9 |
10 | if (_browsers > -1) {
11 | // contain browsers
12 | // slice all params before
13 | const myArgs = process.argv.slice(_browsers + 1)
14 |
15 | browsers = myArgs.filter(function (element) {
16 | return availableBrowsers.includes(element)
17 | })
18 |
19 | console.log(`BROWSERS: ${browsers}`)
20 | return browsers
21 | }
22 | // browsers not set return all
23 | console.log(`BROWSERS: ${availableBrowsers}`)
24 | return availableBrowsers
25 | }
26 |
--------------------------------------------------------------------------------
/examples/ui5-js-app/ui5.yaml:
--------------------------------------------------------------------------------
1 | specVersion: "2.0"
2 | metadata:
3 | name: ui5-app
4 | type: application
5 | server:
6 | customMiddleware:
7 | - name: ui5-middleware-simpleproxy
8 | afterMiddleware: compression
9 | mountPath: /V2
10 | configuration:
11 | baseUri: "https://services.odata.org/V2"
12 | strictSSL: false
13 | - name: delayed-mockserver
14 | beforeMiddleware: ui5-middleware-simpleproxy
15 | configuration:
16 | service:
17 | urlBasePath: "/V2/Northwind/Northwind.svc"
18 | name: ""
19 | metadataXmlPath: "./webapp/localService/metadata.xml"
20 | generateMockData: true
21 | ---
22 | specVersion: "1.0"
23 | metadata:
24 | name: delayed-mockserver
25 | kind: extension
26 | type: server-middleware
27 | middleware:
28 | path: scripts/delayedMockServer.js
29 |
--------------------------------------------------------------------------------
/examples/ui5-js-app/webapp/Component.js:
--------------------------------------------------------------------------------
1 | sap.ui.define(
2 | [
3 | "sap/ui/core/UIComponent",
4 | "sap/ui/Device",
5 | "test/Sample/model/models",
6 | "sap/ui/core/ComponentSupport" // make sure to include the ComponentSupport in the bundle
7 | ],
8 | function (UIComponent, Device, models) {
9 | "use strict"
10 |
11 | return UIComponent.extend("test.Sample.Component", {
12 | metadata: {
13 | manifest: "json"
14 | },
15 |
16 | /**
17 | * The component is initialized by UI5 automatically during the startup of the app and calls the init method once.
18 | * @public
19 | * @override
20 | */
21 | init: function () {
22 | // call the base component's init function
23 | UIComponent.prototype.init.apply(this, arguments)
24 |
25 | // enable routing
26 | this.getRouter().initialize()
27 |
28 | // set the device model
29 | this.setModel(models.createDeviceModel(), "device")
30 |
31 | const url = new URL(location.href)
32 | if (url.searchParams.get("isui5toolingTest")?.toLocaleLowerCase() === "true") {
33 | const startXHR = () => {
34 | this.getModel().read("/Categories", {
35 | success: startXHR
36 | })
37 | }
38 | startXHR()
39 | const startFetch = () => {
40 | fetch("/V2/Northwind/Northwind.svc/Categories").then(startFetch)
41 | }
42 | startFetch()
43 | }
44 | }
45 | })
46 | }
47 | )
48 |
--------------------------------------------------------------------------------
/examples/ui5-js-app/webapp/controller/App.controller.js:
--------------------------------------------------------------------------------
1 | "use strict"
2 |
3 | sap.ui.define(["test/Sample/controller/BaseController"], function (Controller) {
4 | "use strict"
5 |
6 | return Controller.extend("test.Sample.controller.App", {
7 | onInit: function () {}
8 | })
9 | })
10 |
--------------------------------------------------------------------------------
/examples/ui5-js-app/webapp/controller/Other.controller.js:
--------------------------------------------------------------------------------
1 | sap.ui.define(
2 | ['test/Sample/controller/BaseController', 'sap/m/MessageToast', 'sap/m/StandardListItem'],
3 | function (Controller, MessageToast, StandardListItem) {
4 | 'use strict';
5 |
6 | return Controller.extend('test.Sample.controller.Other', {
7 | onInit: function () {},
8 |
9 | onItemPress(oEvent) {
10 | this.getView().byId('idTextFieldClickResult').setText(oEvent.getParameter('listItem').data('key'));
11 |
12 | MessageToast.show(oEvent.getParameter('listItem').data('key'));
13 | },
14 |
15 | onAddLineItem(oEvent) {
16 | this.getView()
17 | .byId('PeopleList')
18 | .addItem(
19 | new StandardListItem({
20 | title: 'FirstName LastName',
21 | type: 'Navigation'
22 | })
23 | );
24 | }
25 | });
26 | }
27 | );
28 |
--------------------------------------------------------------------------------
/examples/ui5-js-app/webapp/css/style.css:
--------------------------------------------------------------------------------
1 | /* Enter your custom styles here */
--------------------------------------------------------------------------------
/examples/ui5-js-app/webapp/i18n/i18n.properties:
--------------------------------------------------------------------------------
1 | appTitle=Sample
2 | appDescription=App Description
3 |
4 | ###
5 |
6 | startPage.userButton.text=User Test Text
7 | startPage.navButton.text=to Other view
8 | startPage.currentUI5.text=most current UI5 version available
9 | startPage.title.text=UI5 demo
10 | startPage.text.username=Benutzername
11 |
12 | ###
13 |
14 | otherPage.title=Another View...
15 | otherPage.listHeader=...bites the dust!
16 |
17 | ###
18 |
19 | dialog.title=Here we are!
20 | dialog.text=born to be kings
21 | dialog.close=cut
22 |
23 |
--------------------------------------------------------------------------------
/examples/ui5-js-app/webapp/i18n/i18n_en.properties:
--------------------------------------------------------------------------------
1 | appTitle=Sample
2 | appDescription=App Description
3 |
4 | ###
5 |
6 | startPage.navButton.text=to Other view
7 | startPage.currentUI5.text=most current UI5 version available
8 | startPage.title.text=UI5 demo
9 | startPage.text.username=Username
10 |
11 | ###
12 |
13 | otherPage.title=Another View...
14 | otherPage.listHeader=...bites the dust!
15 |
16 | ###
17 |
18 | dialog.title=Here we are!
19 | dialog.text=born to be kings
20 | dialog.close=cut
--------------------------------------------------------------------------------
/examples/ui5-js-app/webapp/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Sample UI5 Application
8 |
9 |
11 |
12 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/examples/ui5-js-app/webapp/model/countries.json:
--------------------------------------------------------------------------------
1 | {
2 | "CountriesCollection": [
3 | {
4 | "key": "DZ",
5 | "text": "Algeria"
6 | },
7 | {
8 | "key": "AR",
9 | "text": "Argentina"
10 | },
11 | {
12 | "key": "AU",
13 | "text": "Australia"
14 | },
15 | {
16 | "key": "AT",
17 | "text": "Austria"
18 | },
19 | {
20 | "key": "BH",
21 | "text": "Bahrain"
22 | },
23 | {
24 | "key": "BE",
25 | "text": "Belgium"
26 | },
27 | {
28 | "key": "BA",
29 | "text": "Bosnia and Herzegovina"
30 | },
31 | {
32 | "key": "BR",
33 | "text": "Brazil"
34 | }
35 | ]
36 | }
37 |
--------------------------------------------------------------------------------
/examples/ui5-js-app/webapp/model/models.js:
--------------------------------------------------------------------------------
1 | sap.ui.define(["sap/ui/model/json/JSONModel", "sap/ui/Device"], function (JSONModel, Device) {
2 | "use strict"
3 |
4 | return {
5 | createDeviceModel: function () {
6 | var oModel = new JSONModel(Device)
7 | oModel.setDefaultBindingMode("OneWay")
8 | return oModel
9 | }
10 | }
11 | })
12 |
--------------------------------------------------------------------------------
/examples/ui5-js-app/webapp/proxyrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "/V2/": {
3 | "target": "https://services.odata.org/V2/",
4 | "secure": false,
5 | "changeOrigin": true
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/examples/ui5-js-app/webapp/test/__screenshots__/.keepMe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ui5-community/wdi5/93a69ff20d1563e34e99c9e371baa13cda0c8886/examples/ui5-js-app/webapp/test/__screenshots__/.keepMe
--------------------------------------------------------------------------------
/examples/ui5-js-app/webapp/test/e2e/allControls.test.js:
--------------------------------------------------------------------------------
1 | const Main = require("./pageObjects/Main")
2 |
3 | const selector = {
4 | wdio_ui5_key: "allButtons",
5 | selector: {
6 | controlType: "sap.m.Button",
7 | viewName: "test.Sample.view.Main"
8 | }
9 | }
10 |
11 | describe("ui5 basic, get all buttons", () => {
12 | before(async () => {
13 | await Main.open()
14 | })
15 |
16 | it("check number of buttons", async () => {
17 | const buttons = await browser.allControls(selector)
18 | // 8 buttons in view and the panel expand button => 8
19 | expect(buttons.length).toEqual(9)
20 | })
21 |
22 | it("no force select", async () => {
23 | const buttons = await browser.allControls(selector)
24 | expect(await buttons[0].getText()).toEqual("to Other view")
25 | })
26 |
27 | it("with force select", async () => {
28 | const selectorWForce = selector
29 | selectorWForce.forceSelect = true
30 |
31 | const buttons = await browser.allControls(selectorWForce)
32 | expect(await buttons[0].getText()).toEqual("to Other view")
33 | })
34 |
35 | it("reuse the cached wdi5 controls", async () => {
36 | const buttons = await browser.allControls(selector)
37 | expect(await buttons[0].getText()).toEqual("to Other view")
38 | })
39 |
40 | it("test webelement", async () => {
41 | const buttons = await browser.allControls(selector)
42 | const webButton = await buttons[0].getWebElement()
43 | expect(webButton).toBeTruthy()
44 | })
45 | })
46 |
--------------------------------------------------------------------------------
/examples/ui5-js-app/webapp/test/e2e/allControlsForce.test.js:
--------------------------------------------------------------------------------
1 | const Other = require("./pageObjects/Other")
2 | const { wdi5 } = require("wdio-ui5-service")
3 |
4 | const oCheckboxSelector = {
5 | forceSelect: true,
6 | selector: {
7 | controlType: "sap.m.CheckBox",
8 | viewName: "test.Sample.view.Other"
9 | }
10 | }
11 |
12 | describe("ui5 basic, get all buttons", () => {
13 | before(async () => {
14 | await Other.open()
15 | })
16 |
17 | it("test with multiple checkboxes", async () => {
18 | const aCheckbox = await browser.allControls(oCheckboxSelector)
19 |
20 | expect(aCheckbox.length).toEqual(9)
21 |
22 | for await (let oCheckbox of aCheckbox) {
23 | const selected = await oCheckbox.getSelected()
24 | wdi5.getLogger().log(`oCheckbox: ${oCheckbox.getControlInfo().id} isSelected: ${selected} `)
25 | if (!selected) {
26 | await oCheckbox.press()
27 | }
28 | }
29 |
30 | for await (let oCheckbox of aCheckbox) {
31 | expect(await oCheckbox.getSelected()).toBeTruthy()
32 | }
33 | })
34 | })
35 |
--------------------------------------------------------------------------------
/examples/ui5-js-app/webapp/test/e2e/bindingpath-matcher.test.js:
--------------------------------------------------------------------------------
1 | describe("binding path matcher/locator", () => {
2 | it("bindingPath: contextPath")
3 | it("bindingPath: propertyPath")
4 | it("bindingPath: context- and propertyPath")
5 | it("bindingPath: regular expressions in declarative syntax")
6 | })
7 |
--------------------------------------------------------------------------------
/examples/ui5-js-app/webapp/test/e2e/checkbox.test.js:
--------------------------------------------------------------------------------
1 | const { wdi5 } = require("wdio-ui5-service")
2 | const Main = require("./pageObjects/Main")
3 |
4 | describe("ui5 checkbox test", () => {
5 | before(async () => {
6 | await Main.open()
7 | })
8 |
9 | it("should check the checkbox", async () => {
10 | const cb = await Main.getCheckbox()
11 | await cb.setSelected(true)
12 | expect(await cb.getProperty("selected")).toBeTruthy
13 | })
14 | it("should uncheck the checkbox", async () => {
15 | const cb = await Main.getCheckbox()
16 | await cb.setSelected(false)
17 | expect(await cb.getProperty("selected")).toBeFalsy()
18 | })
19 |
20 | it("should uncheck the checkbox via wdio-native .click()", async () => {
21 | const ui5checkBox2 = await Main.getCheckbox()
22 |
23 | // wdio-alternative to ui5 native api
24 | // retrieve the wdio element from the ui5 control via .getWebElement()
25 | const $wdioCheckBox2 = await ui5checkBox2.getWebElement()
26 | $wdioCheckBox2.click()
27 | wdi5.getLogger().log(
28 | "ui5checkBox2.getProperty('selected') returned: " + (await ui5checkBox2.getProperty("selected"))
29 | )
30 |
31 | expect(await ui5checkBox2.getProperty("selected")).toBeTruthy()
32 | })
33 | })
34 |
--------------------------------------------------------------------------------
/examples/ui5-js-app/webapp/test/e2e/combobox.test.js:
--------------------------------------------------------------------------------
1 | const Main = require("./pageObjects/Main")
2 |
3 | const oComboboxSelector = {
4 | forceSelect: true,
5 | selector: {
6 | interaction: "root",
7 | id: "combobox",
8 | viewName: "test.Sample.view.Main"
9 | }
10 | }
11 |
12 | // #121
13 | describe("ui5 sap.m.Combobox", () => {
14 | before(async () => {
15 | await Main.open()
16 | })
17 |
18 | it("get combobox items aggregation as WebdriverIO representations", async () => {
19 | const combobox = await browser.asControl(oComboboxSelector)
20 |
21 | // no need to open the combobox
22 | const items = await combobox.getItems(true)
23 | expect(items.length).toEqual(8)
24 | })
25 |
26 | it("get combobox single item aggregation as ui5 items", async () => {
27 | const combobox = await browser.asControl(oComboboxSelector)
28 | // another issue with the combobox. If the Box was not opend prevoiusly the items are not rendered -> unretrieveable with ui5
29 | await combobox.open()
30 |
31 | const items = await combobox.getItems(4)
32 | expect(await items.getTitle()).toEqual("Bahrain")
33 | })
34 |
35 | it("get combobox items aggregation as ui5 items", async () => {
36 | const combobox = await browser.asControl(oComboboxSelector)
37 | // another issue with the combobox. If the Box was not opend prevoiusly the items are not rendered -> unretrieveable with ui5
38 | await combobox.open()
39 |
40 | const items = await combobox.getItems()
41 | expect(await items[4].getTitle()).toEqual("Bahrain")
42 | })
43 | })
44 |
--------------------------------------------------------------------------------
/examples/ui5-js-app/webapp/test/e2e/fluent-async-api.test.js:
--------------------------------------------------------------------------------
1 | const { wdi5 } = require("wdio-ui5-service")
2 | const Other = require("./pageObjects/Other")
3 |
4 | const listSelector = {
5 | // forceSelect: true,
6 | selector: {
7 | id: "PeopleList",
8 | viewName: "test.Sample.view.Other"
9 | }
10 | }
11 |
12 | const tests = [{ api: "asControl" }, { api: "_asControl" }]
13 |
14 | describe("async api", () => {
15 | before(async () => {
16 | await Other.open()
17 | })
18 |
19 | for (const test of tests) {
20 | it(`api: browser.${test.api} - getItems(x) and getTitle() in sequence`, async () => {
21 | const list = await browser[test.api](listSelector)
22 | wdi5.getLogger().info("//> ********************")
23 | wdi5.getLogger().info("//> done with sap.m.List")
24 | const listItem = await list.getItems(1) // ui5 api
25 | wdi5.getLogger().info("//> ********************")
26 | wdi5.getLogger().info("//> done with List Item")
27 | const title = await listItem.getTitle() // ui5 api
28 | wdi5.getLogger().info("//> ********************")
29 | wdi5.getLogger().info("//> done with sap.m.Title")
30 | expect(title).toBe("Andrew Fuller")
31 | })
32 | }
33 |
34 | it("chain getItems(x) and getTitle()", async () => {
35 | const title = await browser.asControl(listSelector).getItems(1).getTitle()
36 | expect(title).toBe("Andrew Fuller")
37 | })
38 |
39 | it("chain events, setter and getter", async () => {
40 | const selector = {
41 | selector: {
42 | id: "idAddLineItemButton",
43 | viewName: "test.Sample.view.Other"
44 | }
45 | }
46 | const oldText = await browser.asControl(selector).press().getText()
47 | expect(oldText).toBe("add Line Item")
48 | const _newText = "changed!"
49 | const newText = await browser.asControl(selector).press().setText(_newText).getText()
50 | expect(newText).toBe(_newText)
51 | })
52 | })
53 |
--------------------------------------------------------------------------------
/examples/ui5-js-app/webapp/test/e2e/i18n-matcher.test.js:
--------------------------------------------------------------------------------
1 | const Main = require("./pageObjects/Main")
2 | const { wdi5 } = require("wdio-ui5-service")
3 |
4 | describe("i18NText matcher", () => {
5 | before(async () => {
6 | await Main.open()
7 | })
8 |
9 | it("w/ propertyName + key", async () => {
10 | const i18nSelector = {
11 | selector: {
12 | i18NText: {
13 | propertyName: "text",
14 | key: "startPage.navButton.text"
15 | },
16 | controlType: "sap.m.Button",
17 | viewName: "test.Sample.view.Main"
18 | }
19 | }
20 |
21 | const button = await browser.asControl(i18nSelector)
22 | const buttonText = await button.getText()
23 | expect(buttonText).toEqual("to Other view")
24 | })
25 |
26 | it("check i18nText matcher user button", async () => {
27 | const i18nSelector = {
28 | selector: {
29 | i18NText: {
30 | propertyName: "text",
31 | key: "startPage.userButton.text"
32 | },
33 | controlType: "sap.m.Button",
34 | viewName: "test.Sample.view.Main"
35 | }
36 | }
37 |
38 | const button = await browser.asControl(i18nSelector)
39 | const buttonText = await button.getText()
40 | expect(buttonText).toEqual("User Test Text")
41 | })
42 |
43 | it("use 18n with parameters other than propertyName and key")
44 | })
45 |
--------------------------------------------------------------------------------
/examples/ui5-js-app/webapp/test/e2e/list-interaction.test.js:
--------------------------------------------------------------------------------
1 | const { wdi5 } = require("wdio-ui5-service")
2 | const Other = require("./pageObjects/Other")
3 |
4 | describe("list interaction", () => {
5 | const buttonSelector = {
6 | selector: {
7 | id: "NavFwdButton",
8 | viewName: "test.Sample.view.Main"
9 | }
10 | }
11 |
12 | const listSelector = {
13 | selector: {
14 | id: "PeopleList",
15 | viewName: "test.Sample.view.Other"
16 | }
17 | }
18 |
19 | before(async () => {
20 | await Other.open()
21 | })
22 |
23 | beforeEach(async () => {
24 | await browser.screenshot("list-interaction-before")
25 | })
26 |
27 | afterEach(async () => {
28 | await browser.screenshot("list-interaction-after")
29 | })
30 |
31 | it("should have the correct list header", async () => {
32 | const list = await browser.asControl(listSelector)
33 | expect(await list.getProperty("headerText")).toEqual("...bites the dust!")
34 | })
35 |
36 | it("press a list item to show its' data:key property", async () => {
37 | // fire click event on a list item
38 | // it will set the list items' data:key value to the text field
39 | // the event handler function checks the data:key property of the list item -> manually add this property to the event.
40 | const list = await browser.asControl(listSelector)
41 | await list.fireEvent("itemPress", {
42 | eval: () => {
43 | return {
44 | listItem: {
45 | data: () => {
46 | return "Mock Name"
47 | }
48 | }
49 | }
50 | }
51 | })
52 |
53 | const textFieldSelector = {
54 | selector: {
55 | id: "idTextFieldClickResult",
56 | viewName: "test.Sample.view.Other"
57 | }
58 | }
59 |
60 | const mockedListItemPress = await browser.asControl(textFieldSelector)
61 |
62 | expect(await mockedListItemPress.getText()).toEqual("Mock Name")
63 | })
64 | })
65 |
--------------------------------------------------------------------------------
/examples/ui5-js-app/webapp/test/e2e/multiremote.test.js:
--------------------------------------------------------------------------------
1 | const Main = require("./pageObjects/Main")
2 | describe("Multi Remote", () => {
3 | before(async () => {
4 | await Main.open()
5 | })
6 |
7 | it("allows handling ui5 controls in different browsers", async () => {
8 | const button = await browser.two.asControl({
9 | selector: {
10 | id: "openDialogButton",
11 | viewName: "test.Sample.view.Main"
12 | }
13 | })
14 | const metadata = button.getControlInfo()
15 |
16 | expect(metadata.id).toEqual("container-Sample---Main--openDialogButton")
17 | expect(metadata.className).toEqual("sap.m.Button")
18 | expect(metadata.key).toEqual("openDialogButtontestSample.view.Main")
19 |
20 | await button.press()
21 |
22 | const dialogSelector = {
23 | selector: {
24 | id: "Dialog-title",
25 | searchOpenDialogs: true
26 | }
27 | }
28 |
29 | const text = await browser.two.asControl(dialogSelector).getText()
30 | expect(text).toEqual("Here we are!")
31 | expect(await browser.one.asControl(dialogSelector).getInitStatus()).toBeFalsy()
32 | })
33 | it("should return an array of results of both browsers if called directly by browser", async () => {
34 | const button = await browser.asControl({
35 | selector: {
36 | id: "openDialogButton",
37 | viewName: "test.Sample.view.Main"
38 | }
39 | })
40 | const buttonOne = button[0]
41 | const buttonTwo = button[1]
42 | expect(buttonOne._browserInstance.sessionId).not.toEqual(buttonTwo._browserInstance.sessionId)
43 |
44 | expect(await buttonOne.getText()).toEqual("open Dialog")
45 | expect(await buttonTwo.getText()).toEqual("open Dialog")
46 | })
47 | })
48 |
--------------------------------------------------------------------------------
/examples/ui5-js-app/webapp/test/e2e/pageObjects/ComponentLocator.js:
--------------------------------------------------------------------------------
1 | const { wdi5 } = require("wdio-ui5-service")
2 | const Page = require("./Page")
3 |
4 | // const planningCalendarRow = {
5 | // selector: {
6 | // controlType: "sap.m.internal.PlanningCalendarRowListItem",
7 | // id: /__row0-__xmlview0--schedulePlanningCalendar-0-CLI$/
8 | // }
9 | // }
10 |
11 | class ComponentLocator extends Page {
12 | async open(path) {
13 | wdi5.goTo(path)
14 | }
15 |
16 | async open() {
17 | await super.open(`#/Calendar`)
18 | }
19 |
20 | async getShowButton() {
21 | const showSummary = {
22 | selector: {
23 | controlType: "sap.m.Button",
24 | id: /TodayBtn$/
25 | }
26 | }
27 |
28 | return await browser.asControl(showSummary)
29 | }
30 |
31 | async getCloseSummaryButton() {
32 | const closeSummary = {
33 | selector: {
34 | controlType: "sap.m.Button",
35 | id: /closeSummaryButton$/
36 | }
37 | }
38 |
39 | return await browser.asControl(closeSummary)
40 | }
41 |
42 | async getPlanningCalendar() {
43 | const planningCalendar = {
44 | selector: {
45 | controlType: "sap.m.PlanningCalendar",
46 | id: /PC1/
47 | }
48 | }
49 |
50 | return await browser.asControl(planningCalendar)
51 | }
52 | }
53 |
54 | module.exports = new ComponentLocator()
55 |
--------------------------------------------------------------------------------
/examples/ui5-js-app/webapp/test/e2e/pageObjects/Main.js:
--------------------------------------------------------------------------------
1 | const Page = require("./Page")
2 |
3 | class Main extends Page {
4 | async open() {
5 | await super.open(`#/Main`)
6 | }
7 |
8 | _viewName = "test.Sample.view.Main"
9 |
10 | async getCheckbox() {
11 | const cbSelector1 = {
12 | wdio_ui5_key: "cbSelector1",
13 | selector: {
14 | id: "idCheckbox",
15 | viewName: this._viewName,
16 | controlType: "sap.m.CheckBox"
17 | }
18 | }
19 |
20 | return await browser.asControl(cbSelector1)
21 | }
22 | }
23 |
24 | module.exports = new Main()
25 |
--------------------------------------------------------------------------------
/examples/ui5-js-app/webapp/test/e2e/pageObjects/Other.js:
--------------------------------------------------------------------------------
1 | const Page = require("./Page")
2 |
3 | class Other extends Page {
4 | allNames = [
5 | "Nancy Davolio",
6 | "Andrew Fuller",
7 | "Janet Leverling",
8 | "Margaret Peacock",
9 | "Steven Buchanan",
10 | "Michael Suyama",
11 | "Robert King",
12 | "Laura Callahan",
13 | "Anne Dodsworth"
14 | ]
15 | _viewName = "test.Sample.view.Other"
16 |
17 | async open() {
18 | await super.open(`#/Other`)
19 | }
20 |
21 | async getList(force = false) {
22 | const listSelector = {
23 | wdio_ui5_key: "PeopleList",
24 | selector: {
25 | id: "PeopleList",
26 | viewName: this._viewName
27 | },
28 | forceSelect: force //> this will populate down to $ui5Control.getAggregation and $ui5Control.getProperty as well
29 | }
30 |
31 | return await browser.asControl(listSelector)
32 | }
33 |
34 | async getListItems(force = false) {
35 | const list = await this.getList(force)
36 | return await list.getAggregation("items")
37 | }
38 |
39 | async getAddLineItemButtom() {
40 | return await browser.asControl({
41 | selector: {
42 | id: "idAddLineItemButton",
43 | viewName: this._viewName
44 | }
45 | })
46 | }
47 | }
48 |
49 | module.exports = new Other()
50 |
--------------------------------------------------------------------------------
/examples/ui5-js-app/webapp/test/e2e/pageObjects/Page.js:
--------------------------------------------------------------------------------
1 | const { wdi5 } = require("wdio-ui5-service")
2 | module.exports = class Page {
3 | async open(path) {
4 | wdi5.goTo(path)
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/examples/ui5-js-app/webapp/test/e2e/prop-array.test.js:
--------------------------------------------------------------------------------
1 | const Main = require("./pageObjects/Main")
2 |
3 | const multiComboBoxSelector = {
4 | forceSelect: true,
5 | selector: {
6 | interaction: "root",
7 | id: "multiComboBox",
8 | viewName: "test.Sample.view.Main"
9 | }
10 | }
11 |
12 | describe("ui5 property array test", () => {
13 | before(async () => {
14 | await Main.open()
15 | })
16 |
17 | it("should get empty array", async () => {
18 | const oMultiComboBox = await browser.asControl(multiComboBoxSelector)
19 | const aSelectedKeys = await oMultiComboBox.getSelectedKeys()
20 | expect(aSelectedKeys.length).toEqual(0)
21 | })
22 |
23 | it("select two countries from list", async () => {
24 | const oMultiComboBox = await browser.asControl(multiComboBoxSelector)
25 | await oMultiComboBox.setSelectedKeys(["IN", "BR"])
26 | const aSelectedKeys = await oMultiComboBox.getSelectedKeys()
27 | expect(aSelectedKeys.length).toEqual(2)
28 | })
29 |
30 | it("get text from items list", async () => {
31 | const oMultiComboBox = await browser.asControl(multiComboBoxSelector)
32 | await oMultiComboBox.open()
33 | const items = await oMultiComboBox.getItems()
34 | const firstItemText = await items[2].getTitle()
35 | expect(firstItemText).toEqual("Australia")
36 | })
37 | })
38 |
--------------------------------------------------------------------------------
/examples/ui5-js-app/webapp/test/e2e/screenshot.test.js:
--------------------------------------------------------------------------------
1 | const fs = require("fs/promises")
2 | const path = require("path")
3 | const Main = require("./pageObjects/Main")
4 |
5 | describe("screenshots (async tests)", () => {
6 | before(async () => {
7 | await Main.open()
8 | })
9 | it("should validate screenshots capability", async () => {
10 | await browser.screenshot("ui5-page")
11 |
12 | // seed to wait some time until the screenshot is actually saved to the file system before reading it again
13 | setTimeout(async () => {
14 | const screenShotPath = path.join("examples", "ui5-js-app", "webapp", "test", "__screenshots__")
15 | const screenshots = await fs.readdir(screenShotPath)
16 | const ours = screenshots.find((shot) => shot.match(/.*ui5-page.*/))
17 | expect(ours).toMatch(/.*ui5-page.*/)
18 | }, 500)
19 | })
20 |
21 | it("should validate screenshots capability with unnamed screenshot", async () => {
22 | await browser.screenshot()
23 |
24 | // seed to wait some time until the screenshot is actually saved to the file system before reading it again
25 | setTimeout(async () => {
26 | const screenShotPath = path.join("examples", "ui5-js-app", "webapp", "test", "__screenshots__")
27 | const screenshots = fs.readdirSync(screenShotPath)
28 | const ours = screenshots.find((shot) => shot.match(/.*-screenshot.png/))
29 | expect(ours).toMatch(/.*-screenshot.png/)
30 | }, 500)
31 | })
32 | })
33 |
--------------------------------------------------------------------------------
/examples/ui5-js-app/webapp/test/e2e/ui5-late.test.js:
--------------------------------------------------------------------------------
1 | // only requiring the service for late inject/init
2 | const { default: _ui5Service } = require("wdio-ui5-service")
3 | const ui5Service = new _ui5Service()
4 |
5 | describe("ui5 basic", () => {
6 | it('should show a non UI5 page, then advance to a UI5 page and late init "wdi5"', async () => {
7 | // native wdio functionality - navigates to the wdi5 github page
8 | await browser.$("#user-content-wdi5-").waitForExist()
9 | // open local app
10 | await browser.url("http://localhost:8888")
11 | // do the late injection
12 | await ui5Service.injectUI5()
13 | })
14 |
15 | it("should verify the caching of the wdi5 config", async () => {
16 | // open local app
17 | await browser.url("http://localhost:8888")
18 | // do the late injection
19 | await ui5Service.injectUI5()
20 | // check if config have been cached
21 | expect(__wdi5Config.wdi5.waitForUI5Timeout).toBe(654321)
22 | })
23 |
24 | // after late injection, use wdi5 as usual
25 | it("should get a button text via model binding", async () => {
26 | const buttonText = await browser
27 | .asControl({
28 | selector: {
29 | bindingPath: {
30 | modelName: "testModel",
31 | propertyPath: "/buttonText"
32 | },
33 | viewName: "test.Sample.view.Main",
34 | controlType: "sap.m.Button"
35 | }
36 | })
37 | .getText()
38 |
39 | expect(buttonText).toContain("press me")
40 | })
41 | })
42 |
--------------------------------------------------------------------------------
/examples/ui5-js-app/webapp/view/App.view.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/examples/ui5-js-app/webapp/view/Dialog.fragment.xml:
--------------------------------------------------------------------------------
1 |
3 |
15 |
--------------------------------------------------------------------------------
/examples/ui5-js-app/webapp/view/Other.view.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
11 |
12 |
16 |
19 |
20 |
21 |
23 |
24 |
26 |
29 |
30 |
31 |
34 |
35 |
36 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/examples/ui5-ts-app/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | parser: "@typescript-eslint/parser", // Specifies the ESLint parser
3 | parserOptions: {
4 | ecmaVersion: "latest",
5 | sourceType: "module" // imports
6 | },
7 | extends: [
8 | "plugin:@typescript-eslint/recommended", // Uses the recommended rules from the @typescript-eslint/eslint-plugin
9 | "prettier", // Uses eslint-config-prettier to disable ESLint rules from @typescript-eslint/eslint-plugin that would conflict with prettier
10 | "plugin:prettier/recommended" // Enables eslint-plugin-prettier and eslint-config-prettier. This will display prettier errors as ESLint errors. Make sure this is always the last configuration in the extends array.
11 | ],
12 | rules: {
13 | // Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs
14 | // e.g. "@typescript-eslint/explicit-function-return-type": "off",
15 | "@typescript-eslint/no-explicit-any": "off",
16 | "@typescript-eslint/explicit-module-boundary-types": "off",
17 | "@typescript-eslint/ban-ts-comment": "warn"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/examples/ui5-ts-app/.gitignore:
--------------------------------------------------------------------------------
1 | # build results
2 | dist
3 |
4 | # Logs
5 | logs
6 | *.log
7 | npm-debug.log*
8 | yarn-debug.log*
9 | yarn-error.log*
10 |
11 | # Dependency directories
12 | node_modules/
13 |
14 | .DS_Store
15 |
16 | # do not add dependency lock files to this repo because it should remain independent from specific dependency managers
17 | package-lock.json
18 | yarn.lock
19 |
20 | # PFX file
21 | sap.pfx
22 |
--------------------------------------------------------------------------------
/examples/ui5-ts-app/approuter/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "approuter",
3 | "dependencies": {
4 | "@sap/approuter": "latest"
5 | },
6 | "scripts": {
7 | "start": "node node_modules/@sap/approuter/approuter.js"
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/examples/ui5-ts-app/approuter/xs-app.json:
--------------------------------------------------------------------------------
1 | {
2 | "welcomeFile": "index.html",
3 | "authenticationMethod": "route",
4 | "routes": [
5 | {
6 | "source": ".*/V2(.*)",
7 | "target": "$1",
8 | "destination": "V2",
9 | "authenticationType": "none",
10 | "csrfProtection": false
11 | },
12 | {
13 | "source": "^.*/resources/(.*)$",
14 | "target": "/resources/$1",
15 | "authenticationType": "none",
16 | "destination": "ui5"
17 | },
18 | {
19 | "source": "^.*/test-resources/(.*)$",
20 | "target": "/test-resources/$1",
21 | "authenticationType": "none",
22 | "destination": "ui5"
23 | },
24 | {
25 | "source": "^/no-auth/(.*)$",
26 | "target": "$1",
27 | "authenticationType": "none",
28 | "localDir": "webapp/"
29 | },
30 | {
31 | "source": "^/xsuaa/(.*)$",
32 | "target": "$1",
33 | "authenticationType": "xsuaa",
34 | "localDir": "webapp/"
35 | },
36 | {
37 | "source": "^/basic-auth/(.*)$",
38 | "target": "$1",
39 | "authenticationType": "basic",
40 | "localDir": "webapp/"
41 | }
42 | ]
43 | }
44 |
--------------------------------------------------------------------------------
/examples/ui5-ts-app/destinations.json:
--------------------------------------------------------------------------------
1 | {
2 | "init_data": {
3 | "subaccount": {
4 | "existing_destinations_policy": "update",
5 | "destinations": [
6 | {
7 | "Name": "V2",
8 | "Authentication": "NoAuthentication",
9 | "ProxyType": "Internet",
10 | "Type": "HTTP",
11 | "URL": "https://services.odata.org/V2"
12 | },
13 | {
14 | "Name": "ui5",
15 | "Authentication": "NoAuthentication",
16 | "ProxyType": "Internet",
17 | "Type": "HTTP",
18 | "URL": "https://ui5.sap.com"
19 | }
20 | ]
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/examples/ui5-ts-app/mta.yaml:
--------------------------------------------------------------------------------
1 | ID: ui5-approuter
2 | _schema-version: 3.2.0
3 | version: 1.0.0
4 | parameters:
5 | enable-parallel-deployments: true
6 |
7 | modules:
8 | - name: approuter
9 | type: approuter.nodejs
10 | path: ./approuter
11 | parameters:
12 | disk-quota: 256M
13 | memory: 256M
14 | routes:
15 | - route: wdi5-sample-app.cfapps.eu20.hana.ondemand.com
16 | requires:
17 | - name: html5-destination
18 | - name: html5-uaa
19 | resources:
20 | - name: html5-destination
21 | type: org.cloudfoundry.managed-service
22 | parameters:
23 | service-plan: lite
24 | service: destination
25 | path: ./destinations.json
26 | - name: html5-uaa
27 | type: org.cloudfoundry.managed-service
28 | parameters:
29 | service: xsuaa
30 | service-plan: application
31 | config:
32 | xsappname: "ui5-approuter-uaa"
33 | tenant-mode: dedicated
34 |
--------------------------------------------------------------------------------
/examples/ui5-ts-app/test/e2e/Authentication.test.ts:
--------------------------------------------------------------------------------
1 | import { wdi5 } from "wdio-ui5-service"
2 |
3 | describe("wdi5 authentication", async () => {
4 | it("wdi5.isLoggedIn", async () => {
5 | expect(await wdi5.isLoggedIn()).toBeTruthy()
6 | })
7 | })
8 |
--------------------------------------------------------------------------------
/examples/ui5-ts-app/test/e2e/AuthenticationCert.test.ts:
--------------------------------------------------------------------------------
1 | import { wdi5 } from "wdio-ui5-service"
2 |
3 | describe("wdi5 authentication", async () => {
4 | it("wdi5.isLoggedIn", async () => {
5 | expect(await wdi5.isLoggedIn()).toBeTruthy()
6 | })
7 |
8 | it("should contain 'Zeis' in header title", async () => {
9 | await browser.waitUntil(
10 | async () => {
11 | const state = await browser.execute(() => {
12 | return (
13 | window.document.readyState === "complete" &&
14 | (!(window as any)._pendingFetchRequests || (window as any)._pendingFetchRequests === 0)
15 | )
16 | })
17 | return state
18 | },
19 | {
20 | timeout: 10000,
21 | timeoutMsg: "Network did not reach idle state after 10s"
22 | }
23 | )
24 |
25 | const logger = wdi5.getLogger()
26 | logger.log("Starting test to check header title")
27 |
28 | const headerTitle = await $("span[id='cockpitObjectPageHeader-innerTitle']")
29 | logger.log("Found header title element")
30 |
31 | const titleText = await headerTitle.getText()
32 | logger.log(`Retrieved title text: "${titleText}"`)
33 |
34 | expect(titleText).toContain("Zeis")
35 | logger.log("Successfully verified title contains 'Zeis'")
36 | })
37 | })
38 |
--------------------------------------------------------------------------------
/examples/ui5-ts-app/test/e2e/BasicMultiRemoteAuthentication.test.ts:
--------------------------------------------------------------------------------
1 | import { wdi5 } from "wdio-ui5-service"
2 |
3 | describe("MultiRemote Authentication", async () => {
4 | it("isLoggedIn status", async () => {
5 | expect(await wdi5.isLoggedIn("one")).toBeTruthy()
6 | expect(await wdi5.isLoggedIn("two")).toBeTruthy()
7 | })
8 | })
9 |
--------------------------------------------------------------------------------
/examples/ui5-ts-app/test/e2e/Custom.test.ts:
--------------------------------------------------------------------------------
1 | describe("custom login", async () => {
2 | // we silently acknowledge that this is not a UI5 app
3 | // being tested, so browser-scoped errors are ok
4 | it("w/ a username + password form", async () => {
5 | const expectedSuccessMessage = "You logged into a secure area!"
6 | const actualSuccessMessage = await $("#flash").getText()
7 | expect(actualSuccessMessage).toContain(expectedSuccessMessage)
8 | })
9 | })
10 |
--------------------------------------------------------------------------------
/examples/ui5-ts-app/test/e2e/Input.test.ts:
--------------------------------------------------------------------------------
1 | import Input from "sap/m/Input"
2 | import { wdi5Selector } from "wdio-ui5-service"
3 |
4 | const inputSelector: wdi5Selector = {
5 | selector: {
6 | id: "mainUserInput",
7 | viewName: "test.Sample.tsapp.view.Main"
8 | }
9 | }
10 |
11 | describe("Input", async () => {
12 | it("should read name from username field", async () => {
13 | const input = await browser.asControl(inputSelector)
14 | const value = await input.getValue()
15 | expect(value).toEqual("Helvetius Nagy")
16 | })
17 |
18 | it("should check if the field is writeable", async () => {
19 | const newValue = "Smith Smithersson"
20 | await browser.asControl(inputSelector).setValue(newValue)
21 | const input = await browser.asControl(inputSelector).getValue()
22 | expect(input).toEqual(newValue)
23 | })
24 |
25 | it("should retrieve the webcomponent's bound path via a managed object", async () => {
26 | const control = await browser.asControl(inputSelector)
27 | const bindingInfo = await control.getBindingInfo("value")
28 | // @ts-expect-error "parts" is not part of the type definition
29 | const parts = await bindingInfo.parts
30 | expect(parts[0].path).toEqual("/Customers('TRAIH')/ContactName")
31 | })
32 | })
33 |
--------------------------------------------------------------------------------
/examples/ui5-ts-app/test/e2e/MultiInput.test.ts:
--------------------------------------------------------------------------------
1 | import MultiInput from "sap/m/MultiInput"
2 | import { wdi5Selector } from "wdio-ui5-service"
3 |
4 | describe("MultiInput", async () => {
5 | it("should put text into the multi input control", async () => {
6 | const multiInputSelector: wdi5Selector = {
7 | forceSelect: true, // we need this as the control receives an input that changes the dom structure
8 | selector: {
9 | id: "MultiInput",
10 | viewName: "test.Sample.tsapp.view.Main",
11 | interaction: "root"
12 | }
13 | }
14 | // @ts-expect-error we'd need to properly type the multi input control
15 | await (browser.asControl(multiInputSelector) as unknown as MultiInput).enterText("123")
16 |
17 | const multiInput = await browser.asControl(multiInputSelector)
18 | const text = await multiInput.getValue()
19 | expect(text).toEqual("123")
20 | })
21 | })
22 |
--------------------------------------------------------------------------------
/examples/ui5-ts-app/test/e2e/Navigation.test.ts:
--------------------------------------------------------------------------------
1 | import Button from "sap/ui/webc/main/Button"
2 | import List from "sap/ui/webc/main/List"
3 | import { wdi5Selector } from "wdio-ui5-service"
4 |
5 | describe("Navigation", async () => {
6 | it("nav to other view and check if successful", async () => {
7 | // click webcomponent button to trigger navigation
8 | const navButton = await browser.asControl