diff --git a/.github/assets/enable-workflows.png b/.github/assets/enable-workflows.png new file mode 100644 index 000000000..e9e3876fc Binary files /dev/null and b/.github/assets/enable-workflows.png differ diff --git a/.github/assets/protected-token-creation.gif b/.github/assets/protected-token-creation.gif new file mode 100644 index 000000000..398064ee4 Binary files /dev/null and b/.github/assets/protected-token-creation.gif differ diff --git a/.github/assets/test-results-report.gif b/.github/assets/test-results-report.gif new file mode 100644 index 000000000..656561438 Binary files /dev/null and b/.github/assets/test-results-report.gif differ diff --git a/.github/workflows/add-screenshots.yml b/.github/workflows/add-screenshots.yml new file mode 100644 index 000000000..05f674510 --- /dev/null +++ b/.github/workflows/add-screenshots.yml @@ -0,0 +1,253 @@ +name: Generate Playwright Screenshots + +env: + PW_COMPONENT_FILTER: +on: + push: + branches-ignore: [ main,develop,alpha ] + paths: + - '**/*.spec.ts' + - '**/*.spec.js' + - '**/*Playwright.ts' + + # Allows you to run this workflow manually from the Actions tab. + workflow_dispatch: + +jobs: + screenshot-prepare: + if: github.event_name != 'workflow_dispatch' || (github.ref != 'refs/heads/main' && github.ref != 'refs/heads/develop' && github.ref != 'refs/heads/alpha') + name: "Detect required tests" + runs-on: macos-latest + outputs: + matrix: ${{ steps.get-component-filter.outputs.result }} + steps: + - name: Configure OS + run: | + echo "Setting CGFontDisableAntialiasing" + defaults write CoreGraphics CGFontDisableAntialiasing YES + echo "Disabling AppleFontSmoothing" + defaults write -g AppleFontSmoothing -int 0 + echo "Completed OS configure" + + - name: Dump env 💩 + run: env | sort + + - name: Dump GitHub context 💩 + env: + GITHUB_CONTEXT: ${{ toJson(github) }} + run: echo "$GITHUB_CONTEXT" + + + - name: Checkout 🛎️ + uses: actions/checkout@v3 + with: + persist-credentials: true + fetch-depth: 0 + + - name: Ensure branch up to date 🌿 + # Only on branches + if: ${{ startsWith(github.ref, 'refs/heads/') }} + run: | + git pull + + - name: Use Node.js 16.x ✔ + uses: actions/setup-node@v3 + with: + node-version: 16.x + registry-url: 'https://registry.npmjs.org' + always-auth: true + + - name: Install Package Dependencies + run: | + npm ci --force + + - name: Detect necessary components to test + uses: actions/github-script@v6 + id: get-component-filter + with: + script: | + console.log('Checking required components'); + + const path = require('path'); + const { execSync } = require('child_process'); + + const { globby } = await import('${{ github.workspace }}/node_modules/globby/index.js'); + + const ghCtx = ${{ toJson(github) }}; + let files = []; + let excludeFiles = []; + + async function getAllComponents() { + + const entryPoints = (await globby('${{ github.workspace }}/src/**/!(*.(style|test|stories|spec)).(ts|js)')) + .filter(value => + !value.startsWith('./src/utils') && + !value.includes('OmniInputPlaywright') && + !value.includes('OmniInputStories')); + return entryPoints.map(e => e.replace('./', '').replace('${{ github.workspace }}/', '')).join('\n'); + } + + let list = ''; + if (ghCtx.event_name === 'workflow_dispatch') { + console.log('Manual Dispatch'); + try { + const response = await github.request(`GET /repos/${ghCtx.repository}/actions/runs?branch=${ghCtx.ref_name}&event=workflow_dispatch&per_page=100`); + const before = response.data.workflow_runs.find(wr => wr.name === ghCtx.workflow && wr.id?.toString() !== ghCtx.run_id?.toString() && wr.head_sha && wr.head_sha !== ghCtx.sha && wr.head_branch === ghCtx.ref_name && wr.status === 'completed' && wr.conclusion !== 'failure')?.head_sha; + if (before) { + list = execSync(`git diff-tree --no-commit-id --name-only -r ${before} ${ghCtx.sha}`).toString(); + } else { + list = await getAllComponents(); + } + } catch (error) { + console.warn(error); + list = await getAllComponents(); + } + } else if (ghCtx.event_name === 'pull_request') { + console.log('Pull Request Automated Workflow'); + try { + const response = await github.request(`GET /repos/${ghCtx.repository}/actions/runs?branch=${ghCtx.head_ref}&event=pull_request&per_page=100`); + const beforeWorkflows = response.data.workflow_runs.filter(wr => wr.name === ghCtx.workflow && wr.id?.toString() !== ghCtx.run_id?.toString() && wr.head_sha && wr.head_sha !== ghCtx.sha && wr.head_branch === ghCtx.head_ref && wr.status === 'completed' && wr.pull_requests && wr.pull_requests.length > 0 && wr.pull_requests.find(p => p.id === ghCtx.event.pull_request?.id)); + let currentSha = ghCtx.event.pull_request?.head?.sha ?? ghCtx.sha; + if (beforeWorkflows && beforeWorkflows.length > 0) { + for (let index = 0; index < beforeWorkflows.length; index++) { + const beforeWorkflow = beforeWorkflows[index]; + + if (beforeWorkflow?.head_sha) { + const jobsResponse = await github.request(`GET /repos/${ghCtx.repository}/actions/runs/${beforeWorkflow.id}/attempts/${beforeWorkflow.run_attempt}/jobs`); + if (jobsResponse?.data) { + const passedBefore = jobsResponse.data.jobs.filter(j => j.name.startsWith('Test (') && j.status === 'completed' && j.conclusion === 'success').map(j => j.name.match(/\(([^)]+)\)/)[1]); + if (passedBefore && passedBefore.length > 0) { + const diffList = execSync(`git diff-tree --no-commit-id --name-only -r ${beforeWorkflow?.head_sha} ${currentSha}`).toString(); + const changedFiles = []; + diffList.split(/(\r\n|\n|\r)/gm).forEach(f => { + if (f.startsWith('src') && !f.startsWith('src/utils') && !f.startsWith('src/core') && !f.startsWith('src/icons') && !f.endsWith('index.ts') && f.endsWith('.ts')) { + const filter = path.basename(f).replace('.stories', '').replace('.spec', '').replace('.ts', '.spec.ts'); + if (!changedFiles.includes(filter)) { + changedFiles.push(filter); + } + } + }); + excludeFiles = [...excludeFiles, ...passedBefore.filter(p => !changedFiles.includes(p) && !excludeFiles.includes(p))]; + } + } + } + } + } + list = execSync(`git diff-tree --no-commit-id --name-only -r ${ghCtx.event.pull_request?.base?.sha ?? ghCtx.event.before} ${ghCtx.event.pull_request?.head?.sha ?? ghCtx.sha}`).toString(); + } catch (error) { + list = execSync(`git diff-tree --no-commit-id --name-only -r ${ghCtx.event.pull_request?.base?.sha ?? ghCtx.event.before} ${ghCtx.event.pull_request?.head?.sha ?? ghCtx.sha}`).toString(); + } + + } else { + console.log('Automated Workflow'); + list = execSync(`git diff-tree --no-commit-id --name-only -r ${ghCtx.event.pull_request?.base?.sha ?? ghCtx.event.before} ${ghCtx.event.pull_request?.head?.sha ?? ghCtx.sha}`).toString(); + } + if (list.includes('src/core')) { + console.log('Core was changed. Testing all components'); + list = await getAllComponents(); + } + + if (!list) { + console.log('No specific component modified. Testing all components'); + list = await getAllComponents(); + } + + console.log(list); + list.split(/(\r\n|\n|\r)/gm).forEach(f => { + if (f.startsWith('src') && !f.startsWith('src/utils') && !f.startsWith('src/core') && !f.startsWith('src/icons') && !f.endsWith('index.ts') && f.endsWith('.ts')) { + const filter = path.basename(f).replace('.stories', '').replace('.spec', '').replace('.ts', '.spec.ts'); + if (!files.includes(filter)) { + files.push(filter); + } + } + }); + if (excludeFiles.length > 0) { + console.log('Excluding files: ', JSON.stringify(excludeFiles)); + } + files = files.filter(p => !excludeFiles.includes(p)); + + console.log(JSON.stringify(files)); + return files; + result-encoding: json + + screenshot-add: + if: ${{ needs.screenshot-prepare.outputs.matrix != '[]' && needs.screenshot-prepare.outputs.matrix != '' && needs.screenshot-prepare.outputs.matrix && (github.event_name != 'workflow_dispatch' || (github.ref != 'refs/heads/main' && github.ref != 'refs/heads/develop' && github.ref != 'refs/heads/alpha')) }} + name: "Add Missing Screenshots" + timeout-minutes: 240 + permissions: write-all + runs-on: macos-latest + needs: [ screenshot-prepare ] + strategy: + fail-fast: false + matrix: + value: ${{fromJson(needs.screenshot-prepare.outputs.matrix)}} + steps: + - name: Configure OS + run: | + echo "Setting CGFontDisableAntialiasing" + defaults write CoreGraphics CGFontDisableAntialiasing YES + echo "Disabling AppleFontSmoothing" + defaults write -g AppleFontSmoothing -int 0 + echo "Completed OS configure" + + - name: Dump env 💩 + run: env | sort + + - name: Dump GitHub context 💩 + env: + GITHUB_CONTEXT: ${{ toJson(github) }} + run: echo "$GITHUB_CONTEXT" + + + - name: Checkout 🛎️ + uses: actions/checkout@v3 + with: + persist-credentials: true + fetch-depth: 0 + token: ${{ secrets.PROTECTED_TOKEN || secrets.GITHUB_TOKEN }} + + - name: Ensure branch up to date 🌿 + # Only on branches + if: ${{ startsWith(github.ref, 'refs/heads/') }} + run: | + git pull + + - name: Use Node.js 16.x ✔ + uses: actions/setup-node@v3 + with: + node-version: 16.x + registry-url: 'https://registry.npmjs.org' + always-auth: true + + - name: Install Package Dependencies + run: | + npm ci --force + + - name: Update PW_COMPONENT_FILTER + run: | + PW_COMPONENT_FILTER="${{matrix.value}}" + echo PW_COMPONENT_FILTER=${PW_COMPONENT_FILTER} >> $GITHUB_ENV + + - name: Install Playwright Chrome Dependencies + run: npx playwright install --with-deps + + - name: Run Tests + run: npm run test:add-screenshots + env: + CI: true + PW_NO_RETRIES: true + + - name: Ensure branch up to date (again) 🌿 + # Only on branches even when failed + if: ${{ always() && startsWith(github.ref, 'refs/heads/') }} + run: | + git pull || true + + - name: Auto Commit Changes 👩💻 + uses: stefanzweifel/git-auto-commit-action@v4 + # Only on branches even when failed + if: ${{ always() && startsWith(github.ref, 'refs/heads/') }} + with: + commit_message: Added Missing Screenshots + branch: ${{ env.GITHUB_REF_NAME }} + file_pattern: '*.png' \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 07bf3cd2e..92262f905 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -20,11 +20,12 @@ env: LOCAL_VERSION: jobs: build-and-release: + if: ${{ github.repository_owner == 'capitec' && github.repository_owner_id == '109590421' }} concurrency: group: ${{ github.ref }} cancel-in-progress: true - runs-on: ubuntu-latest + runs-on: macos-latest strategy: matrix: diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 44517d517..8c5e14872 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -1,16 +1,20 @@ name: Pull Request + +env: + PW_COMPONENT_FILTER: on: pull_request: - branches: [ main, develop] + branches: [ main, develop ] # Allows you to run this workflow manually from the Actions tab workflow_dispatch: +permissions: write-all jobs: pr-lint: name: Lint timeout-minutes: 15 - runs-on: ubuntu-latest + runs-on: macos-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 @@ -30,7 +34,7 @@ jobs: pr-format: name: Format timeout-minutes: 15 - runs-on: ubuntu-latest + runs-on: macos-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 @@ -47,14 +51,202 @@ jobs: run: | npm run format:check + pr-prepare: + name: "Detect required tests" + runs-on: macos-latest + outputs: + matrix: ${{ steps.get-component-filter.outputs.result }} + steps: + - name: Configure OS + run: | + echo "Setting CGFontDisableAntialiasing" + defaults write CoreGraphics CGFontDisableAntialiasing YES + echo "Disabling AppleFontSmoothing" + defaults write -g AppleFontSmoothing -int 0 + echo "Completed OS configure" + + - name: Dump env 💩 + run: env | sort + + - name: Dump GitHub context 💩 + env: + GITHUB_CONTEXT: ${{ toJson(github) }} + run: echo "$GITHUB_CONTEXT" + + - name: Checkout 🛎️ + uses: actions/checkout@v3 + with: + persist-credentials: true + fetch-depth: 0 + + - name: Ensure branch up to date 🌿 + # Only on branches + if: ${{ startsWith(github.ref, 'refs/heads/') }} + run: | + git pull + + - name: Use Node.js 16.x ✔ + uses: actions/setup-node@v3 + with: + node-version: 16.x + registry-url: 'https://registry.npmjs.org' + always-auth: true + + - name: Install Package Dependencies + run: | + npm ci --force + + - name: Detect necessary components to test + uses: actions/github-script@v6 + id: get-component-filter + with: + script: | + console.log('Checking required components'); + + const path = require('path'); + const { execSync } = require('child_process'); + + const { globby } = await import('${{ github.workspace }}/node_modules/globby/index.js'); + + const ghCtx = ${{ toJson(github) }}; + let files = []; + let excludeFiles = []; + + async function getAllComponents() { + + const entryPoints = (await globby('${{ github.workspace }}/src/**/!(*.(style|test|stories|spec)).(ts|js)')) + .filter(value => + !value.startsWith('./src/utils') && + !value.includes('OmniInputPlaywright') && + !value.includes('OmniInputStories')); + return entryPoints.map(e => e.replace('./', '').replace('${{ github.workspace }}/', '')).join('\n'); + } + + let list = ''; + if (ghCtx.event_name === 'workflow_dispatch') { + console.log('Manual Dispatch'); + try { + const response = await github.request(`GET /repos/${ghCtx.repository}/actions/runs?branch=${ghCtx.ref_name}&event=workflow_dispatch&per_page=100`); + const before = response.data.workflow_runs.find(wr => wr.name === ghCtx.workflow && wr.id?.toString() !== ghCtx.run_id?.toString() && wr.head_sha && wr.head_sha !== ghCtx.sha && wr.head_branch === ghCtx.ref_name && wr.status === 'completed' && wr.conclusion !== 'failure')?.head_sha; + if (before) { + list = execSync(`git diff-tree --no-commit-id --name-only -r ${before} ${ghCtx.sha}`).toString(); + } else { + list = await getAllComponents(); + } + } catch (error) { + console.warn(error); + list = await getAllComponents(); + } + } else if (ghCtx.event_name === 'pull_request') { + console.log('Pull Request Automated Workflow'); + try { + const response = await github.request(`GET /repos/${ghCtx.repository}/actions/runs?branch=${ghCtx.head_ref}&event=pull_request&per_page=100`); + const beforeWorkflows = response.data.workflow_runs.filter(wr => wr.name === ghCtx.workflow && wr.id?.toString() !== ghCtx.run_id?.toString() && wr.head_sha && wr.head_sha !== ghCtx.sha && wr.head_branch === ghCtx.head_ref && wr.status === 'completed' && wr.pull_requests && wr.pull_requests.length > 0 && wr.pull_requests.find(p => p.id === ghCtx.event.pull_request?.id)); + let currentSha = ghCtx.event.pull_request?.head?.sha ?? ghCtx.sha; + if (beforeWorkflows && beforeWorkflows.length > 0) { + for (let index = 0; index < beforeWorkflows.length; index++) { + const beforeWorkflow = beforeWorkflows[index]; + + if (beforeWorkflow?.head_sha) { + const jobsResponse = await github.request(`GET /repos/${ghCtx.repository}/actions/runs/${beforeWorkflow.id}/attempts/${beforeWorkflow.run_attempt}/jobs`); + if (jobsResponse?.data) { + const passedBefore = jobsResponse.data.jobs.filter(j => j.name.startsWith('Test (') && j.status === 'completed' && j.conclusion === 'success').map(j => j.name.match(/\(([^)]+)\)/)[1]); + if (passedBefore && passedBefore.length > 0) { + const diffList = execSync(`git diff-tree --no-commit-id --name-only -r ${beforeWorkflow?.head_sha} ${currentSha}`).toString(); + const changedFiles = []; + diffList.split(/(\r\n|\n|\r)/gm).forEach(f => { + if (f.startsWith('src') && !f.startsWith('src/utils') && !f.startsWith('src/core') && !f.startsWith('src/icons') && !f.endsWith('index.ts') && f.endsWith('.ts')) { + const filter = path.basename(f).replace('.stories', '').replace('.spec', '').replace('.ts', '.spec.ts'); + if (!changedFiles.includes(filter)) { + changedFiles.push(filter); + } + } + }); + excludeFiles = [...excludeFiles, ...passedBefore.filter(p => !changedFiles.includes(p) && !excludeFiles.includes(p))]; + } + } + } + } + } + list = execSync(`git diff-tree --no-commit-id --name-only -r ${ghCtx.event.pull_request?.base?.sha ?? ghCtx.event.before} ${ghCtx.event.pull_request?.head?.sha ?? ghCtx.sha}`).toString(); + } catch (error) { + list = execSync(`git diff-tree --no-commit-id --name-only -r ${ghCtx.event.pull_request?.base?.sha ?? ghCtx.event.before} ${ghCtx.event.pull_request?.head?.sha ?? ghCtx.sha}`).toString(); + } + + } else { + console.log('Automated Workflow'); + list = execSync(`git diff-tree --no-commit-id --name-only -r ${ghCtx.event.pull_request?.base?.sha ?? ghCtx.event.before} ${ghCtx.event.pull_request?.head?.sha ?? ghCtx.sha}`).toString(); + } + if (list.includes('src/core')) { + console.log('Core was changed. Testing all components'); + list = await getAllComponents(); + } + + if (!list) { + console.log('No specific component modified. Testing all components'); + list = await getAllComponents(); + } + + console.log(list); + list.split(/(\r\n|\n|\r)/gm).forEach(f => { + if (f.startsWith('src') && !f.startsWith('src/utils') && !f.startsWith('src/core') && !f.startsWith('src/icons') && !f.endsWith('index.ts') && f.endsWith('.ts')) { + const filter = path.basename(f).replace('.stories', '').replace('.spec', '').replace('.ts', '.spec.ts'); + if (!files.includes(filter)) { + files.push(filter); + } + } + }); + if (excludeFiles.length > 0) { + console.log('Excluding files: ', JSON.stringify(excludeFiles)); + } + files = files.filter(p => !excludeFiles.includes(p)); + + console.log(JSON.stringify(files)); + return files; + result-encoding: json + pr-test: - needs: ["pr-lint", "pr-format"] + if: ${{ needs.pr-prepare.outputs.matrix != '[]' && needs.pr-prepare.outputs.matrix != '' && needs.pr-prepare.outputs.matrix }} + needs: [ pr-lint, pr-format, pr-prepare ] + permissions: write-all name: "Test" - timeout-minutes: 15 - runs-on: ubuntu-latest + timeout-minutes: 300 + runs-on: macos-latest + strategy: + fail-fast: false + matrix: + value: ${{fromJson(needs.pr-prepare.outputs.matrix)}} steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - name: Configure OS + run: | + echo "Setting CGFontDisableAntialiasing" + defaults write CoreGraphics CGFontDisableAntialiasing YES + echo "Disabling AppleFontSmoothing" + defaults write -g AppleFontSmoothing -int 0 + echo "Completed OS configure" + + - name: Dump env 💩 + run: env | sort + + - name: Dump GitHub context 💩 + env: + GITHUB_CONTEXT: ${{ toJson(github) }} + run: echo "$GITHUB_CONTEXT" + + - name: Checkout 🛎️ + uses: actions/checkout@v3 + with: + persist-credentials: true + fetch-depth: 0 + + - name: Ensure branch up to date 🌿 + # Only on branches + if: ${{ startsWith(github.ref, 'refs/heads/') }} + run: | + git pull + + - name: Use Node.js 16.x ✔ + uses: actions/setup-node@v3 with: node-version: 16.x registry-url: 'https://registry.npmjs.org' @@ -63,6 +255,11 @@ jobs: - name: Install Package Dependencies run: | npm ci --force + + - name: Update PW_COMPONENT_FILTER + run: | + PW_COMPONENT_FILTER="${{matrix.value}}" + echo PW_COMPONENT_FILTER=${PW_COMPONENT_FILTER} >> $GITHUB_ENV - name: Install Playwright Chrome Dependencies run: npx playwright install --with-deps @@ -75,14 +272,26 @@ jobs: uses: actions/upload-artifact@v3 if: always() with: - name: playwright-report + name: "playwright-report-${{matrix.value}}" path: playwright-report/ retention-days: 30 - name: Upload Test Coverage uses: actions/upload-artifact@v3 if: always() with: - name: coverage + name: "coverage-report-${{matrix.value}}" path: coverage/ retention-days: 30 + pr-pass: + needs: [ pr-prepare, pr-test ] + if: ${{ always() }} + name: "Ensure All Tests Pass" + runs-on: macos-latest + steps: + - name: Fail on Error or Cancelation + run: | + echo "${{ toJson(needs) }}" + exit 1 + # see https://github.com/orgs/community/discussions/26822#discussioncomment-5122101 + if: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }} diff --git a/.tooling/eleventy/assets/css/style.css b/.tooling/eleventy/assets/css/style.css index 22cce4ca2..2c193df9e 100644 --- a/.tooling/eleventy/assets/css/style.css +++ b/.tooling/eleventy/assets/css/style.css @@ -27,7 +27,7 @@ a:visited { } a, -a>* { +a > * { cursor: pointer; } @@ -76,7 +76,7 @@ footer .logo { footer .links { margin-top: 24px; display: flex; - flex-direction: row; + flex-direction: row; justify-content: center; } @@ -187,7 +187,7 @@ header .header-version-select { /* background-color: turquoise; */ } -.header-actions>.action { +.header-actions > .action { padding: 7px; cursor: pointer; border-radius: 6px; @@ -195,15 +195,15 @@ header .header-version-select { display: flex; } -.header-actions>.action, -.header-actions>.header-action-section { +.header-actions > .action, +.header-actions > .header-action-section { --header-action-background-color: var(--omni-theme-background-color); background-color: var(--omni-theme-background-color); color: var(--omni-theme-font-color); } -.header-actions>.action, -.header-actions>.header-action-section>* { +.header-actions > .action, +.header-actions > .header-action-section > * { color: var(--omni-theme-font-color); background-color: var(--header-action-background-color); } @@ -212,19 +212,19 @@ header .header-version-select { --omni-select-items-max-height: 300px; } -.header-actions>.action:hover, -.header-actions>.header-action-section:hover>.action { +.header-actions > .action:hover, +.header-actions > .header-action-section:hover > .action { border-color: var(--omni-form-hover-color, var(--omni-primary-hover-color)); } /* border: 1px solid var(--docs-border-hover-color); */ -.header-actions>.header-action-section { +.header-actions > .header-action-section { position: relative; margin-right: 12px; /* margin-left: 12px; */ } -.header-actions>.header-action-section>.theme-select { +.header-actions > .header-action-section > .theme-select { width: 132px; position: absolute; right: 0; @@ -232,7 +232,7 @@ header .header-version-select { height: 37px; } -.header-actions>.header-action-section>.action { +.header-actions > .header-action-section > .action { padding: 5px; cursor: pointer; border-radius: 6px; @@ -271,7 +271,7 @@ header .header-version-select { } /* .code-modal > div > div > [data-identifier="custom-theme-source-code"] { */ -[data-identifier="custom-theme-source-code"] { +[data-identifier='custom-theme-source-code'] { --code-editor-max-height: 500px; --code-editor-min-height: 400px; } @@ -298,7 +298,6 @@ header .header-version-select { align-items: stretch; padding: 0px; background-color: var(--omni-theme-background-color); - ; } @media screen and (min-width: 767px) { @@ -328,9 +327,9 @@ header .header-version-select { .home-header { display: flex; - flex-direction: column; - align-items: center; - background-color: var(--docs-home-header-background-color); + flex-direction: column; + align-items: center; + background-color: var(--docs-home-header-background-color); padding: 24px 48px; } @@ -358,8 +357,8 @@ header .header-version-select { .home-content .wrapper { flex: 1; - display: flex; - flex-direction: row; + display: flex; + flex-direction: row; } .home-content .wrapper .block { @@ -395,7 +394,6 @@ header .header-version-select { /* CONTROLS */ - .flex-row { display: inline-flex; flex-direction: row; @@ -405,7 +403,7 @@ header .header-version-select { margin: 6px; } -.flex-row>* { +.flex-row > * { margin: 6px; } @@ -494,14 +492,14 @@ header .header-version-select { .menu-item.separator .line { height: 1px; - border: none; - color: var(--docs-border-color); - background-color: var(--docs-border-color); + border: none; + color: var(--docs-border-color); + background-color: var(--docs-border-color); margin-left: 24px; } .menu-item.selected { - border-left: 2px solid #3AB6FF; + border-left: 2px solid #3ab6ff; } .menu-link { @@ -606,7 +604,7 @@ h2 { font-family: ui-monospace, monospace; font-size: 14px; } - + .static-article code:not([class]), .story-description code:not([class]), .component-tab#docs code:not([class]), @@ -674,7 +672,7 @@ h2 { cursor: pointer; } -.link-item>* { +.link-item > * { cursor: pointer; } @@ -742,7 +740,7 @@ h2 { .component-name { display: inline-flex; - align-items: center; + align-items: center; font-size: 32px; line-height: 1.2; font-weight: 600; @@ -773,7 +771,7 @@ h2 { overflow: hidden; } -.component-framework-toggle>* { +.component-framework-toggle > * { padding-left: 5px; padding-right: 5px; cursor: pointer; @@ -781,12 +779,12 @@ h2 { padding: 4px; } -.component-framework-toggle>.selected-framework { +.component-framework-toggle > .selected-framework { background-color: var(--omni-theme-primary-color); color: var(--omni-theme-background-color); } -.component-framework-toggle>*:not(:last-child) { +.component-framework-toggle > *:not(:last-child) { border-right-width: 1px; border-right-style: solid; border-right-color: var(--omni-theme-primary-color); @@ -804,7 +802,7 @@ h2 { margin: 6px 0; } -.component-info-item>div:first-child { +.component-info-item > div:first-child { width: 100px; } @@ -832,7 +830,7 @@ h2 { transform: translate(0, 0) scale(0.9); } -.component-info-item-code .no-code-scroll>span, +.component-info-item-code .no-code-scroll > span, .component-info-item-code .no-code-scroll { white-space: pre; } @@ -978,7 +976,7 @@ h2 { align-content: stretch; } -.component-stories .story .preview>div { +.component-stories .story .preview > div { flex: 1; } @@ -1053,43 +1051,9 @@ h2 { align-items: center; } -.component-stories .story .failure { - display: none; - flex-direction: column; - padding: 24px; - border-top: 1px solid var(--docs-border-color); -} - -.component-stories .story .two-part .play-tests { - display: flex; - flex-direction: column; -} - -.component-stories .story .two-part .play-tests .success { - flex-direction: row; - margin: 6px 6px 6px 10px; - border-radius: 100%; - padding: 2px; - color: #155724; - background-color: #d4edda; - border-color: #c3e6cb; - display: none; - align-items: center; -} - -.component-stories .story .failure .play-tests-out { - flex-direction: row; - border-radius: 4px; - padding: 12px; - color: #721c24; - background-color: #f8d7da; - border-color: #f5c6cb; - display: flex; -} - .component-toc-wrapper { flex: 0 0; - /* 260px; */ + /* 260px; */ margin-top: 24px; min-width: 200px; } @@ -1164,7 +1128,7 @@ table tr:nth-child(even) { background-color: var(--docs-secondary-background-color); } -.component-props-table>td { +.component-props-table > td { border: 1px solid #000000; } @@ -1196,7 +1160,7 @@ table tr:nth-child(even) { code[class*='language-'], pre[class*='language-'], -code[class*='language-']>span { +code[class*='language-'] > span { color: var(--omni-theme-font-color); background: none; text-shadow: var(--docs-prism-span-text-shadow); @@ -1219,10 +1183,7 @@ code[class*='language-']>span { hyphens: none; } - - @media print { - code[class*='language-'], pre[class*='language-'] { text-shadow: none; @@ -1382,9 +1343,7 @@ pre[class*='language-'] { /* FORM FACTORS */ - @media screen and (max-width: 1080px) { - .home-content { padding: 24px; } @@ -1411,7 +1370,6 @@ pre[class*='language-'] { } @media screen and (max-width: 915px) { - /* CONTROLS */ .css-prop { @@ -1420,7 +1378,6 @@ pre[class*='language-'] { } @media screen and (max-width: 760px) { - footer { flex-direction: column; justify-content: center; @@ -1476,7 +1433,7 @@ pre[class*='language-'] { padding-right: 0; } - .header-actions>.header-action-section>.theme-select { + .header-actions > .header-action-section > .theme-select { width: 37px; height: 37px; position: absolute; @@ -1545,7 +1502,7 @@ pre[class*='language-'] { padding: 0 24px; } - .component-info-item>div:first-child { + .component-info-item > div:first-child { display: none; } @@ -1598,12 +1555,6 @@ pre[class*='language-'] { border-bottom: 1px solid var(--docs-border-color); } - .component-stories .story .two-part .play-tests .success { - margin: 6px 6px 6px 10px; - border-radius: 100%; - padding: 2px; - } - table { border: 0; } @@ -1680,7 +1631,6 @@ pre[class*='language-'] { } @media screen and (max-width: 376px) { - .header-logo { flex: 1; } @@ -1688,7 +1638,6 @@ pre[class*='language-'] { .component-info-item { font-size: 0.9em; } - } /* LOADING INDICATOR: https://loading.io/css/ */ @@ -1744,4 +1693,4 @@ pre[class*='language-'] { height: 72px; opacity: 0; } -} \ No newline at end of file +} diff --git a/.tooling/playwright/globalSetup.js b/.tooling/playwright/globalSetup.js new file mode 100644 index 000000000..924f0e350 --- /dev/null +++ b/.tooling/playwright/globalSetup.js @@ -0,0 +1,24 @@ +import chalk from 'chalk'; +import fsp from 'fs/promises'; +import fs from 'fs'; + +async function globalSetup(config) { + if (!process.env.CI && !process.env.PW_SCREENSHOT_TESTING) { + console.error(chalk.yellow('No "CI" or "PW_SCREENSHOT_TESTING" environment variables set. Skipping screenshot assertion!')); + } + + try { + await fsp.rm('coverage', { force: true, recursive: true }); + } catch (error) { + //Ignore + } + + // Create coverage output directory if necessary + if (!fs.existsSync(`./coverage`)) { + await fsp.mkdir(`./coverage`, { + recursive: true + }); + } +} + +export default globalSetup; \ No newline at end of file diff --git a/.tooling/playwright/globalTeardown.js b/.tooling/playwright/globalTeardown.js new file mode 100644 index 000000000..4ecb57ac4 --- /dev/null +++ b/.tooling/playwright/globalTeardown.js @@ -0,0 +1,102 @@ + +/* eslint-disable no-useless-catch */ +import fsp from 'fs/promises'; +import fs from 'fs'; +import chalk from 'chalk'; +import libCoverage from 'istanbul-lib-coverage'; +import libReport from 'istanbul-lib-report'; +import reports from 'istanbul-reports'; +import v8toIstanbul from 'v8-to-istanbul'; + + +/** + * Test whether file is available and not locked + * @param {*} filePath + * @returns + */ +const isAvailable = (filePath) => { + let fileAccess = false + try { + fs.closeSync(fs.openSync(filePath, 'r+')) + fileAccess = true + } catch (err) { + //Ignore + } + return fileAccess +} + +async function globalTeardown() { + + let coverage = []; + let files = await fsp.readdir(`coverage`); + + //Load and merge coverage information from all test workers + for (let index = 0; index < files.length; index++) { + const f = `coverage/${files[index]}`; + if (f.toLowerCase().endsWith('.json') && fs.existsSync(f)) { + try { + //Wait and test for coverage file availability. Files may be locked if other workers are still writing + while (!isAvailable(f)) { + await new Promise(resolve => { + setTimeout(resolve, 100); + }) + if (!fs.existsSync(f)) { + break; + } + } + if (!fs.existsSync(f)) { + continue; + } + const cov = await fsp.readFile(f, 'utf-8'); + coverage = [ + ...coverage, + ...(JSON.parse(cov)) + ]; + } catch (error) { + throw new Error(`Read fail: \r\n\r\n${error.toString()}`); + } + } + + } + + const cwd = process.cwd(); + const map = libCoverage.createCoverageMap(); + for (const entry of coverage) { + try { + + //Skip any scripts that aren't from dist, we only care about code coverage related to our source code + if (entry.url === '' || !entry.url.startsWith(`http://${process.env.PLAYWRIGHT_HOST_ORIGIN ?? 'localhost'}:6006/dist`)) { + continue; + } + const scriptPath = `${cwd}${new URL(entry.url).pathname}`; + const converter = v8toIstanbul(scriptPath, 0, { source: entry.source }, (filepath) => { + const normalized = filepath.replace(/\\/g, '/'); + + // Vendor code, stories files, index files and utils do not need to have coverage checked + const ret = normalized.includes('node_modules/') || + normalized.includes('node-modules') || + normalized.includes('utils/') || + normalized.includes('.index.') || + normalized.includes('.stories.'); + return ret; + }); + await converter.load(); + converter.applyCoverage(entry.functions); + const data = converter.toIstanbul(); + map.merge(data); + } catch (error) { + console.error(entry.url, error); + } + } + const context = libReport.createContext({ coverageMap: map }); + + //Report both to html for detailed coverage report as well as console for terminal output + reports.create('html').execute(context); + reports.create('text').execute(context); + + if (!process.env.CI && !process.env.PW_SCREENSHOT_TESTING) { + console.error(chalk.yellow('No "CI" or "PW_SCREENSHOT_TESTING" environment variables set. Screenshot assertion was skipped!')); + } +} + +export default globalTeardown; \ No newline at end of file diff --git a/.tooling/readme/contributors.md b/.tooling/readme/contributors.md index 88ec8e1d0..0cf787cad 100644 --- a/.tooling/readme/contributors.md +++ b/.tooling/readme/contributors.md @@ -3,17 +3,17 @@
-
-
+
+
- chromaticWaster + BOTLANNER |
-
-
+
+
- BOTLANNER + chromaticWaster |
@@ -31,17 +31,17 @@ |
-
-
+
+
- Makhubedu + capitec-oss |
-
-
+
+
- capitec-oss + Makhubedu |
-
-
+
+
- chromaticWaster + BOTLANNER |
-
-
+
+
- BOTLANNER + chromaticWaster |
@@ -661,17 +661,17 @@ See the [`CONTRIBUTING.md`](./CONTRIBUTING.md) guide to get involved. |
-
-
+
+
- Makhubedu + capitec-oss |
-
-
+
+
- capitec-oss + Makhubedu |