├── .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 [![npm](https://img.shields.io/npm/v/wdio-ui5-service)](https://www.npmjs.com/package/wdio-ui5-service) package name . 8 | 9 | ![wdi5 Logo](./img/wdi5-logo-small.png) `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 | ![screenshot of a source code snippet with a breakpoint set in the far left area of the line](img/vscode-breakpoint.png) 7 | 8 | In the `Terminal` are of VS Code, start a "JavaScript Debug Terminal"... 9 | ![screenshot of clicking on the plus sign in the terminal are to see the menu for "open JavaScript debug terminal"](img/vscode-js-debug-terminal.png) 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 | ![screenshot of VS Code halted at breakpoint](img/vscode-debugger-halted.png) 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 | ![ui5 serve for starting a UI5 appThen](./img/20_running.png) 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 | ![wdio for executing wdi5 tests](./img/23_running.png) 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 | 6 | 7 | 8 | 9 | 10 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /examples/ui5-js-app-esm/webapp/view/Other.view.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 11 | 12 | 16 | 19 | 20 | 21 | 23 |