diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 419f2f952..95a4f7319 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -2,40 +2,38 @@ // https://github.com/microsoft/vscode-dev-containers/tree/v0.163.1/containers/docker-existing-docker-compose // If you want to run as a non-root user in the container, see .devcontainer/docker-compose.yml. { - "name": "Existing Docker Compose (Extend)", + "name": "Existing Docker Compose (Extend)", - // Update the 'dockerComposeFile' list if you have more compose files or use different names. - // The .devcontainer/docker-compose.yml file contains any overrides you need/want to make. - "dockerComposeFile": [ - "../docker-compose.yml" - ], + // Update the 'dockerComposeFile' list if you have more compose files or use different names. + // The .devcontainer/docker-compose.yml file contains any overrides you need/want to make. + "dockerComposeFile": ["../docker-compose.yml"], - "service": "onesignal-web-sdk-dev", + "service": "onesignal-web-sdk-dev", - // The optional 'workspaceFolder' property is the path VS Code should open by default when - // connected. This is typically a file mount in .devcontainer/docker-compose.yml - "workspaceFolder": "/sdk", + // The optional 'workspaceFolder' property is the path VS Code should open by default when + // connected. This is typically a file mount in .devcontainer/docker-compose.yml + "workspaceFolder": "/sdk", - // Set *default* container specific settings.json values on container create. - "settings": { - "terminal.integrated.shell.linux": null - }, + // Set *default* container specific settings.json values on container create. + "settings": { + "terminal.integrated.shell.linux": null + }, - // Add the IDs of extensions you want installed when the container is created. - "extensions": [] + // Add the IDs of extensions you want installed when the container is created. + "extensions": [] - // Use 'forwardPorts' to make a list of ports inside the container available locally. - // "forwardPorts": [], + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], - // Uncomment the next line if you want start specific services in your Docker Compose config. - // "runServices": [], + // Uncomment the next line if you want start specific services in your Docker Compose config. + // "runServices": [], - // Uncomment the next line if you want to keep your containers running after VS Code shuts down. - // "shutdownAction": "none", + // Uncomment the next line if you want to keep your containers running after VS Code shuts down. + // "shutdownAction": "none", - // Uncomment the next line to run commands after the container is created - for example installing curl. - // "postCreateCommand": "apt-get update && apt-get install -y curl", + // Uncomment the next line to run commands after the container is created - for example installing curl. + // "postCreateCommand": "apt-get update && apt-get install -y curl", - // Uncomment to connect as a non-root user if you've added one. See https://aka.ms/vscode-remote/containers/non-root. - // "remoteUser": "vscode" + // Uncomment to connect as a non-root user if you've added one. See https://aka.ms/vscode-remote/containers/non-root. + // "remoteUser": "vscode" } diff --git a/.github/os_probot_metadata.js b/.github/os_probot_metadata.js deleted file mode 100644 index 6b2b3f92e..000000000 --- a/.github/os_probot_metadata.js +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Based on probot-metadata - https://github.com/probot/metadata - */ -const regex = /\n\n/; - -const { Octokit } = require('@octokit/action'); - -const octokit = new Octokit(); - -module.exports = (context, issue = null) => { - console.info(context); - const prefix = 'onesignal-probot'; - - if (!issue) issue = context.payload.issue; - - return { - async get(key = null) { - let body = issue.body; - - if (!body) { - body = (await octokit.issues.get(issue)).data.body || ''; - } - - const match = body.match(regex); - - if (match) { - const data = JSON.parse(match[1])[prefix]; - return key ? data && data[key] : data; - } - }, - - async set(key, value) { - let body = issue.body; - let data = {}; - - if (!body) body = (await octokit.issues.get(issue)).data.body || ''; - - body = body.replace(regex, (_, json) => { - data = JSON.parse(json); - return ''; - }); - - if (!data[prefix]) data[prefix] = {}; - - if (typeof key === 'object') { - Object.assign(data[prefix], key); - } else { - data[prefix][key] = value; - } - - body = `${body}\n\n`; - - const [owner, repo] = process.env.GITHUB_REPOSITORY.split('/'); - const issue_number = context.payload.issue.number; - return octokit.issues.update({ owner, repo, issue_number, body }); - }, - }; -}; diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 4e1566749..c8125664c 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,45 +1,56 @@ # Description + ## 1 Line Summary ## Details # Systems Affected - - [ ] WebSDK - - [ ] Backend - - [ ] Dashboard + +- [ ] WebSDK +- [ ] Backend +- [ ] Dashboard # Validation + ## Tests + ### Info ### Checklist - - [ ] All the automated tests pass or I explained why that is not possible - - [ ] I have personally tested this on my machine or explained why that is not possible - - [ ] I have included test coverage for these changes or explained why they are not needed + +- [ ] All the automated tests pass or I explained why that is not possible +- [ ] I have personally tested this on my machine or explained why that is not possible +- [ ] I have included test coverage for these changes or explained why they are not needed **Programming Checklist** Interfaces: - - [ ] Don't use default export - - [ ] New interfaces are in model files + +- [ ] Don't use default export +- [ ] New interfaces are in model files Functions: - - [ ] Don't use default export - - [ ] All function signatures have return types - - [ ] Helpers should not access any data but rather be given the data to operate on. + +- [ ] Don't use default export +- [ ] All function signatures have return types +- [ ] Helpers should not access any data but rather be given the data to operate on. Typescript: - - [ ] No Typescript warnings - - [ ] Avoid silencing null/undefined warnings with the exclamation point + +- [ ] No Typescript warnings +- [ ] Avoid silencing null/undefined warnings with the exclamation point Other: - - [ ] Iteration: refrain from using `elem of array` syntax. Prefer `forEach` or use `map` - - [ ] Avoid using global OneSignal accessor for `context` if possible. Instead, we can pass it to function/constructor so that we don't call `OneSignal.context` + +- [ ] Iteration: refrain from using `elem of array` syntax. Prefer `forEach` or use `map` +- [ ] Avoid using global OneSignal accessor for `context` if possible. Instead, we can pass it to function/constructor so that we don't call `OneSignal.context` ## Screenshots + ### Info ### Checklist - - [ ] I have included screenshots/recordings of the intended results or explained why they are not needed + +- [ ] I have included screenshots/recordings of the intended results or explained why they are not needed --- diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml deleted file mode 100644 index a7a42bdef..000000000 --- a/.github/release-drafter.yml +++ /dev/null @@ -1,27 +0,0 @@ -name-template: $RESOLVED_VERSION -tag-template: $RESOLVED_VERSION -categories: - - title: ๐Ÿš€ Features - label: Enhancement / Feature - - title: ๐Ÿ› Bug Fixes - label: Bug - - title: ๐Ÿงฐ Improvements - label: Improvement - - title: down arrow Dependency Updates - label: Dependencies -change-template: '- $TITLE (#$NUMBER)' -version-resolver: - major: - labels: - - 'major' - minor: - labels: - - 'minor' - patch: - labels: - - 'patch' - default: patch -template: | - ## Other Changes - - $CHANGES diff --git a/.github/set_response_times.js b/.github/set_response_times.js deleted file mode 100644 index fb8aa51eb..000000000 --- a/.github/set_response_times.js +++ /dev/null @@ -1,63 +0,0 @@ -function calcResponseTimeForIssueCreatedAt(createdAt) { - const issueOpenedDate = new Date(createdAt); - const issueTriagedDate = new Date(); - const businessDaysResponseTime = calcBusinessDaysBetweenDates( - issueOpenedDate, - issueTriagedDate, - ); - return businessDaysResponseTime; -} - -function calcBusinessDaysBetweenDates(openedDate, triagedDate) { - let differenceInWeeks, responseTime; - if (triagedDate < openedDate) return -1; // error code if dates transposed - let openedDay = openedDate.getDay(); // day of week - let triagedDay = triagedDate.getDay(); - openedDay = openedDay == 0 ? 7 : openedDay; // change Sunday from 0 to 7 - triagedDay = triagedDay == 0 ? 7 : triagedDay; - openedDay = openedDay > 5 ? 5 : openedDay; // only count weekdays - triagedDay = triagedDay > 5 ? 5 : triagedDay; - // calculate differnece in weeks (1000mS * 60sec * 60min * 24hrs * 7 days = 604800000) - differenceInWeeks = Math.floor( - (triagedDate.getTime() - openedDate.getTime()) / 604800000, - ); - if (openedDay < triagedDay) { - //Equal to makes it reduce 5 days - responseTime = differenceInWeeks * 5 + (triagedDay - openedDay); - } else if (openedDay == triagedDay) { - responseTime = differenceInWeeks * 5; - } else { - responseTime = (differenceInWeeks + 1) * 5 - (openedDay - triagedDay); - } - return responseTime; -} - -module.exports = async (context, osmetadata) => { - const foundResponseTime = await osmetadata(context).get( - 'response_time_in_business_days', - ); - if (foundResponseTime) { - const foundString = - 'already found response time in business days: ' + foundResponseTime; - console.info(foundString); - return foundString; - } - if ( - context.payload.comment && - context.payload.comment.author_association != 'MEMBER' && - context.payload.comment.author_association != 'OWNER' && - context.payload.comment.author_association != 'CONTRIBUTOR' - ) { - return; - } - const businessDaysResponseTime = calcResponseTimeForIssueCreatedAt( - context.payload.issue.created_at, - ); - console.info('response time in business days: ' + businessDaysResponseTime); - const result = osmetadata(context, context.payload.issue).set( - 'response_time_in_business_days', - businessDaysResponseTime, - ); - console.info('osmetadata update result: ' + result); - return 'set response time in business days: ' + businessDaysResponseTime; -}; diff --git a/.github/workflows/asana-add-comment.yml b/.github/workflows/asana-add-comment.yml deleted file mode 100644 index 1235c8f14..000000000 --- a/.github/workflows/asana-add-comment.yml +++ /dev/null @@ -1,47 +0,0 @@ -name: Github --> Asana Add Comment Workflow - -on: - issue_comment: - types: [created] - workflow_dispatch: - -jobs: - build: - runs-on: ubuntu-latest - permissions: - issues: read - steps: - - name: Get Asana Task Corresponding to Issue - env: - ISSUE_ID: ${{ github.event.issue.id }} - REPO_FULL_NAME: ${{ github.event.repository.full_name }} - WORKSPACE_ID: "780103692902078" - run: | - REPO_SCOPED_ISSUE_ID="$REPO_FULL_NAME#$ISSUE_ID" - - curl --request GET \ - --url "https://app.asana.com/api/1.0/workspaces/$WORKSPACE_ID/tasks/search?opt_fields=notes&text=$REPO_SCOPED_ISSUE_ID&sort_by=modified_at&sort_ascending=false" \ - --header 'accept: application/json' \ - --header 'authorization: Bearer ${{ secrets.ASANA_PAT }}' \ - --output response.json - TASK_GID=$(jq -r '.data[0].gid' response.json) - echo "TASK_GID=$TASK_GID" >> $GITHUB_ENV - - name: Comment on Asana Task - env: - ISSUE_COMMENT: ${{ github.event.comment.body }} - COMMENTER_NAME: ${{ github.event.comment.user.login }} - run: | - BODY_DATA=$(jq -n \ - --arg text "$ISSUE_COMMENT" \ - --arg commenter_name "$COMMENTER_NAME" \ - '{ - "data": { - "text": "\($commenter_name) left a comment:\n\n\($text)", - } - }') - curl --request POST \ - --url https://app.asana.com/api/1.0/tasks/$TASK_GID/stories \ - --header 'accept: application/json' \ - --header 'authorization: Bearer ${{ secrets.ASANA_PAT }}' \ - --header 'content-type: application/json' \ - --data "$BODY_DATA" \ No newline at end of file diff --git a/.github/workflows/asana-create-task.yml b/.github/workflows/asana-create-task.yml deleted file mode 100644 index f778f2f7a..000000000 --- a/.github/workflows/asana-create-task.yml +++ /dev/null @@ -1,119 +0,0 @@ -name: Github --> Asana Create Task Workflow - -on: - issues: - types: [opened] - workflow_dispatch: - -jobs: - build: - runs-on: ubuntu-latest - permissions: - issues: read - steps: - - name: Create Asana task - env: - ISSUE_TITLE: ${{ github.event.issue.title }} - ISSUE_BODY: ${{ github.event.issue.body }} - ISSUE_HTML_URL: ${{ github.event.issue.html_url }} - ISSUE_ID: ${{ github.event.issue.id }} - ISSUE_NUMBER: ${{ github.event.issue.number }} - REPO_FULL_NAME: ${{ github.event.repository.full_name }} - SDK_PLATFORM_GROUP: "1208961704779581" - SDK_PLATFORM_GROUP_WEB: "1208961704779583" - SDK_PLATFORM: "1208961704779592" - SDK_PLATFORM_WEB: "1208961704779597" - DSA_PRIORITY: "1208779519954980" - DSA_PRIORITY_NO_PRIORITY: "1208779521616959" - DSA_STATUS: "1210103546117753" - DSA_STATUS_TRIAGE: "1210103546117756" - DSA_REPO_TICKET_URL: "1210347857768758" - WORKSPACE_ID: "780103692902078" - PROJECT_ID_GITHUB_AND_IMPORTANT_SDK_ISSUES: "1208970714650308" - PROJECT_ID_SDK_BACKLOG: "1208777198342772" - run: | - DATA_BODY=$(jq -n \ - --arg title "$ISSUE_TITLE" \ - --arg body "$ISSUE_BODY" \ - --arg url "$ISSUE_HTML_URL" \ - --arg id "$ISSUE_ID" \ - --arg number "$ISSUE_NUMBER" \ - --arg repo_full_name "$REPO_FULL_NAME" \ - --arg sdk_platform_group "$SDK_PLATFORM_GROUP" \ - --arg sdk_platform_group_web "$SDK_PLATFORM_GROUP_WEB" \ - --arg sdk_platform "$SDK_PLATFORM" \ - --arg sdk_platform_web "$SDK_PLATFORM_WEB" \ - --arg dsa_priority "$DSA_PRIORITY" \ - --arg dsa_priority_no_priority "$DSA_PRIORITY_NO_PRIORITY" \ - --arg dsa_status "$DSA_STATUS" \ - --arg dsa_status_triage "$DSA_STATUS_TRIAGE" \ - --arg dsa_repo_ticket_url "$DSA_REPO_TICKET_URL" \ - --arg workspace_id "$WORKSPACE_ID" \ - --arg project_id_github_and_important_sdk_issues "$PROJECT_ID_GITHUB_AND_IMPORTANT_SDK_ISSUES" \ - --arg project_id_sdk_backlog "$PROJECT_ID_SDK_BACKLOG" \ - '{ - "data": { - "custom_fields": { - $sdk_platform_group: $sdk_platform_group_web, - $sdk_platform: $sdk_platform_web, - $dsa_priority: $dsa_priority_no_priority, - $dsa_status: $dsa_status_triage, - $dsa_repo_ticket_url: $url - }, - "name": $title, - "workspace": $workspace_id, - "projects": [$project_id_github_and_important_sdk_issues, $project_id_sdk_backlog], - "notes": "Issue ID: \($repo_full_name)#\($id)\nIssue number: \($number)\nCreated via GitHub Actions\n----\n\n\($body)" - } - }') - - curl --request POST \ - --url https://app.asana.com/api/1.0/tasks?opt_pretty=true \ - --header 'accept: application/json' \ - --header 'authorization: Bearer ${{ secrets.ASANA_PAT }}' \ - --header 'content-type: application/json' \ - --data "$DATA_BODY" \ - --output response.json - - TASK_GID=$(jq -r '.data.gid' response.json) - echo "TASK_GID=$TASK_GID" >> $GITHUB_ENV - - name: Move to "0 Unclassified" section in "Github & Important SDK Issues" project - env: - SECTION_ID_GITHUB_AND_IMPORTANT_SDK_ISSUES: "1208970755434051" - run: | - DATA_BODY=$(jq -n \ - --arg task_gid "$TASK_GID" \ - --arg section_id "$SECTION_ID_GITHUB_AND_IMPORTANT_SDK_ISSUES" \ - '{ - "data": { - "task": $task_gid, - "insert_after": "null" - } - }') - - curl --request POST \ - --url https://app.asana.com/api/1.0/sections/$section_id/addTask \ - --header 'accept: application/json' \ - --header 'authorization: Bearer ${{ secrets.ASANA_PAT }}' \ - --header 'content-type: application/json' \ - --data "$DATA_BODY" - - name: Move to "Untriaged" section in "SDK / Backlog" project - env: - SECTION_ID_SDK_BACKLOG: "1208899729378982" - run: | - DATA_BODY=$(jq -n \ - --arg task_gid "$TASK_GID" \ - --arg section_id "$SECTION_ID_SDK_BACKLOG" \ - '{ - "data": { - "task": $task_gid, - "insert_after": "null" - } - }') - - curl --request POST \ - --url https://app.asana.com/api/1.0/sections/$section_id/addTask \ - --header 'accept: application/json' \ - --header 'authorization: Bearer ${{ secrets.ASANA_PAT }}' \ - --header 'content-type: application/json' \ - --data "$DATA_BODY" \ No newline at end of file diff --git a/.github/workflows/asana-update-issue.yml b/.github/workflows/asana-update-issue.yml deleted file mode 100644 index d9dcebe97..000000000 --- a/.github/workflows/asana-update-issue.yml +++ /dev/null @@ -1,172 +0,0 @@ -name: Github --> Asana Issue Updates Workflow - -on: - issues: - types: [edited, deleted, closed, reopened, assigned, unassigned, labeled, unlabeled, milestoned, demilestoned, pinned, unpinned, locked, unlocked, transferred] - workflow_dispatch: - -jobs: - build: - runs-on: ubuntu-latest - permissions: - issues: read - steps: - - name: Get Asana Task Corresponding to Issue - env: - ISSUE_ID: ${{ github.event.issue.id }} - REPO_FULL_NAME: ${{ github.event.repository.full_name }} - WORKSPACE_ID: "780103692902078" - run: | - REPO_SCOPED_ISSUE_ID="$REPO_FULL_NAME#$ISSUE_ID" - - curl --request GET \ - --url "https://app.asana.com/api/1.0/workspaces/$WORKSPACE_ID/tasks/search?opt_fields=notes&text=$REPO_SCOPED_ISSUE_ID&sort_by=modified_at&sort_ascending=false" \ - --header 'accept: application/json' \ - --header 'authorization: Bearer ${{ secrets.ASANA_PAT }}' \ - --output response.json - TASK_GID=$(jq -r '.data[0].gid' response.json) - echo "TASK_GID=$TASK_GID" >> $GITHUB_ENV - - name: Determine Action and Post to Asana - env: - ACTION_TYPE: ${{ github.event.action }} - ACTOR_NAME: ${{ github.event.sender.login }} - ISSUE_TITLE: ${{ github.event.issue.title }} - ISSUE_NUMBER: ${{ github.event.issue.number }} - ISSUE_STATE: ${{ github.event.issue.state }} - run: | - # Map GitHub action types to human-readable descriptions - case "$ACTION_TYPE" in - "edited") - ACTION_DESC="edited the issue" - ;; - "deleted") - ACTION_DESC="deleted the issue" - ;; - "closed") - ACTION_DESC="closed the issue" - ;; - "reopened") - ACTION_DESC="reopened the issue" - ;; - "assigned") - ACTION_DESC="assigned the issue" - ;; - "unassigned") - ACTION_DESC="unassigned the issue" - ;; - "labeled") - ACTION_DESC="added labels to the issue" - ;; - "unlabeled") - ACTION_DESC="removed labels from the issue" - ;; - "milestoned") - ACTION_DESC="added the issue to a milestone" - ;; - "demilestoned") - ACTION_DESC="removed the issue from a milestone" - ;; - "pinned") - ACTION_DESC="pinned the issue" - ;; - "unpinned") - ACTION_DESC="unpinned the issue" - ;; - "locked") - ACTION_DESC="locked the issue" - ;; - "unlocked") - ACTION_DESC="unlocked the issue" - ;; - "transferred") - ACTION_DESC="transferred the issue" - ;; - *) - ACTION_DESC="performed an action on the issue" - ;; - esac - - # Add additional context for specific actions based on webhook payload - if [ "$ACTION_TYPE" = "assigned" ] && [ -n "${{ github.event.assignee.login }}" ]; then - ACTION_DESC="assigned the issue to ${{ github.event.assignee.login }}" - fi - - if [ "$ACTION_TYPE" = "unassigned" ] && [ -n "${{ github.event.assignee.login }}" ]; then - ACTION_DESC="unassigned the issue from ${{ github.event.assignee.login }}" - fi - - if [ "$ACTION_TYPE" = "labeled" ] && [ -n "${{ github.event.label.name }}" ]; then - LABEL_COLOR="${{ github.event.label.color }}" - ACTION_DESC="added label '${{ github.event.label.name }}' to the issue" - if [ -n "$LABEL_COLOR" ]; then - ACTION_DESC="$ACTION_DESC (color: #$LABEL_COLOR)" - fi - fi - - if [ "$ACTION_TYPE" = "unlabeled" ] && [ -n "${{ github.event.label.name }}" ]; then - LABEL_COLOR="${{ github.event.label.color }}" - ACTION_DESC="removed label '${{ github.event.label.name }}' from the issue" - if [ -n "$LABEL_COLOR" ]; then - ACTION_DESC="$ACTION_DESC (color: #$LABEL_COLOR)" - fi - fi - - if [ "$ACTION_TYPE" = "milestoned" ] && [ -n "${{ github.event.milestone.title }}" ]; then - MILESTONE_DUE_DATE="${{ github.event.milestone.due_on }}" - ACTION_DESC="added the issue to milestone '${{ github.event.milestone.title }}'" - if [ -n "$MILESTONE_DUE_DATE" ] && [ "$MILESTONE_DUE_DATE" != "null" ]; then - ACTION_DESC="$ACTION_DESC (due: $MILESTONE_DUE_DATE)" - fi - fi - - if [ "$ACTION_TYPE" = "demilestoned" ] && [ -n "${{ github.event.milestone.title }}" ]; then - ACTION_DESC="removed the issue from milestone '${{ github.event.milestone.title }}'" - fi - - if [ "$ACTION_TYPE" = "transferred" ] && [ -n "${{ github.event.changes.new_repository.full_name }}" ]; then - ACTION_DESC="transferred the issue to repository ${{ github.event.changes.new_repository.full_name }}" - fi - - if [ "$ACTION_TYPE" = "edited" ] && [ -n "${{ github.event.changes.title.from }}" ]; then - OLD_TITLE="${{ github.event.changes.title.from }}" - NEW_TITLE="${{ github.event.issue.title }}" - ACTION_DESC="edited the issue title from '$OLD_TITLE' to '$NEW_TITLE'" - fi - - echo "ACTION_DESC=$ACTION_DESC" >> $GITHUB_ENV - - # Only proceed if we found a task - if [ "$TASK_GID" != "null" ] && [ -n "$TASK_GID" ]; then - # Create a more detailed message with additional context - MESSAGE_TEXT="$ACTOR_NAME performed an action: $ACTION_DESC" - - # Add issue state information for state changes - if [ "$ACTION_TYPE" = "closed" ] || [ "$ACTION_TYPE" = "reopened" ]; then - MESSAGE_TEXT=$(printf "%s\nIssue state: %s" "$MESSAGE_TEXT" "$ISSUE_STATE") - fi - - # Add repository information for transferred issues - if [ "$ACTION_TYPE" = "transferred" ]; then - REPO_NAME="${{ github.event.repository.full_name }}" - MESSAGE_TEXT=$(printf "%s\nFrom repository: %s" "$MESSAGE_TEXT" "$REPO_NAME") - fi - - MESSAGE_TEXT=$(printf "%s\n\nIssue: #%s - %s" "$MESSAGE_TEXT" "$ISSUE_NUMBER" "$ISSUE_TITLE") - - BODY_DATA=$(jq -n \ - --arg text "$MESSAGE_TEXT" \ - '{ - "data": { - "text": $text - } - }') - - curl --request POST \ - --url https://app.asana.com/api/1.0/tasks/$TASK_GID/stories \ - --header 'accept: application/json' \ - --header 'authorization: Bearer ${{ secrets.ASANA_PAT }}' \ - --header 'content-type: application/json' \ - --data "$BODY_DATA" - else - echo "No corresponding Asana task found for issue ID: $ISSUE_ID" - fi \ No newline at end of file diff --git a/.github/workflows/claude-code-review.yml b/.github/workflows/claude-code-review.yml index b3916e003..5ea455089 100644 --- a/.github/workflows/claude-code-review.yml +++ b/.github/workflows/claude-code-review.yml @@ -42,4 +42,3 @@ jobs: allowed_bots: 'dependabot[bot]' # See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md # or https://code.claude.com/docs/en/cli-reference for available options - diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml index 79fe05647..d1998486a 100644 --- a/.github/workflows/claude.yml +++ b/.github/workflows/claude.yml @@ -47,4 +47,3 @@ jobs: # See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md # or https://code.claude.com/docs/en/cli-reference for available options # claude_args: '--allowed-tools Bash(gh pr:*)' - diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 83d483f02..a924fe82c 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,6 +1,3 @@ { - "recommendations": [ - "jeff-hykin.macro-commander", - "void-zero.vite-plus-extension-pack" - ] + "recommendations": ["jeff-hykin.macro-commander", "void-zero.vite-plus-extension-pack"] } diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md index 0b44ef8fa..3922fbcc1 100644 --- a/MIGRATION_GUIDE.md +++ b/MIGRATION_GUIDE.md @@ -70,10 +70,7 @@ From: To: ```html - + ``` ### Service Worker diff --git a/TESTS.md b/TESTS.md index 6a659f7e7..5a9767e6a 100644 --- a/TESTS.md +++ b/TESTS.md @@ -70,11 +70,7 @@ server.use( // or -server.use( - http.get('**/v1/notifications', () => - HttpResponse.json({ result: {}, status: 200 }), - ), -); +server.use(http.get('**/v1/notifications', () => HttpResponse.json({ result: {}, status: 200 }))); ``` ## Fake Timers and IndexedDB diff --git a/__test__/constants/constants.ts b/__test__/constants/constants.ts index b2e88ebcf..995c2f93a 100644 --- a/__test__/constants/constants.ts +++ b/__test__/constants/constants.ts @@ -3,8 +3,7 @@ import { NotificationType } from 'src/shared/subscriptions/constants'; export const APP_ID = '34fcbe85-278d-4fd2-a4ec-0f80e95072c5'; export const PUSH_TOKEN = 'https://fcm.googleapis.com/fcm/send/01010101010101'; -export const PUSH_TOKEN_2 = - 'https://fcm.googleapis.com/fcm/send/01010101010102'; +export const PUSH_TOKEN_2 = 'https://fcm.googleapis.com/fcm/send/01010101010102'; export const ONESIGNAL_ID = '1111111111-2222222222-3333333333'; export const ONESIGNAL_ID_2 = '2222222222-3333333333-4444444444'; diff --git a/__test__/constants/useragent.ts b/__test__/constants/useragent.ts index 627368166..6e3056d25 100644 --- a/__test__/constants/useragent.ts +++ b/__test__/constants/useragent.ts @@ -21,12 +21,10 @@ export const USER_AGENTS = { 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:141.0) Gecko/20100101 Firefox/141.1', FIREFOX_MAC: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 15.6; rv:141.0) Gecko/20100101 Firefox/141.0', - FIREFOX_LINUX: - 'Mozilla/5.0 (X11; Linux i686; rv:141.0) Gecko/20100101 Firefox/141.0', + FIREFOX_LINUX: 'Mozilla/5.0 (X11; Linux i686; rv:141.0) Gecko/20100101 Firefox/141.0', FIREFOX_IOS: 'Mozilla/5.0 (iPhone; CPU iPhone OS 15_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) FxiOS/141.0 Mobile/15E148 Safari/605.1.15', - FIREFOX_ANDROID: - 'Mozilla/5.0 (Android 16; Mobile; rv:141.0) Gecko/141.0 Firefox/141.0', + FIREFOX_ANDROID: 'Mozilla/5.0 (Android 16; Mobile; rv:141.0) Gecko/141.0 Firefox/141.0', SAFARI_MAC: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.4 Safari/605.1.15', SAFARI_IPHONE: @@ -53,8 +51,7 @@ export const USER_AGENTS = { 'Mozilla/5.0 (iPhone; CPU iPhone OS 12_4_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 [FBAN/FBIOS;FBAV/238.0.0.50.115;FBBV/171859800;FBDV/iPhone9,3;FBMD/iPhone;FBSN/iOS;FBSV/12.4.1;FBSS/2;FBID/phone;FBLC/en_US;FBOP/5;FBRV/172564136;FBCR/AT&T]', FACEBOOK_APP_ANDROID: 'Mozilla/5.0 (Linux; Android 8.1.0; SM-J410G Build/M1AJB; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/131.0.6778.81 Mobile Safari/537.36[FBAN/EMA;FBLC/es_LA;FBAV/457.0.0.12.82;FBCX/modulariab;]', - SAMSUNG_TABLET: - 'Dalvik/2.1.0 (Linux; U; Android 14; SM-X306B Build/UP1A.231005.007)', + SAMSUNG_TABLET: 'Dalvik/2.1.0 (Linux; U; Android 14; SM-X306B Build/UP1A.231005.007)', GOOGLE_TABLET: 'Mozilla/5.0 (Linux; Android 7.0; Pixel C Build/NRD90M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/52.0.2743.98 Safari/537.36', // AMAZON_FIRE_TV: diff --git a/__test__/setupTests.ts b/__test__/setupTests.ts index b9c87e9d5..4c805c9b8 100644 --- a/__test__/setupTests.ts +++ b/__test__/setupTests.ts @@ -1,7 +1,8 @@ import * as OneSignalApi from 'src/shared/api/page'; import { ConfigIntegrationKind } from 'src/shared/config/constants'; import { clearAll } from 'src/shared/database/client'; -import type { MockInstance } from 'vite-plus/test'; +import { vi, beforeAll, beforeEach, afterEach, afterAll, type MockInstance } from 'vite-plus/test'; + import { DEFAULT_USER_AGENT } from './constants'; import TestContext from './support/environment/TestContext'; import { server } from './support/mocks/server'; @@ -52,11 +53,8 @@ Object.defineProperty(navigator, 'userAgent', { let downloadSpy: MockInstance; export const mockJsonp = () => { - const serverConfig = TestContext.getFakeServerAppConfig( - ConfigIntegrationKind._Custom, - ); + const serverConfig = TestContext.getFakeServerAppConfig(ConfigIntegrationKind._Custom); - if (!downloadSpy) - downloadSpy = vi.spyOn(OneSignalApi, 'downloadServerAppConfig'); + if (!downloadSpy) downloadSpy = vi.spyOn(OneSignalApi, 'downloadServerAppConfig'); downloadSpy.mockResolvedValue(serverConfig); }; diff --git a/__test__/support/environment/TestContext.ts b/__test__/support/environment/TestContext.ts index 77b31c772..9671eb070 100644 --- a/__test__/support/environment/TestContext.ts +++ b/__test__/support/environment/TestContext.ts @@ -13,6 +13,7 @@ import type { } from 'src/shared/config/types'; import type { RecursivePartial } from 'src/shared/context/types'; import { DelayedPromptType } from 'src/shared/prompts/constants'; + import { APP_ID } from '../../constants'; import type { TestEnvironmentConfig } from './TestEnvironment'; @@ -113,8 +114,7 @@ export default class TestContext { message: 'This is an example notification message.', acceptButton: 'Continue', cancelButton: 'No Thanks', - actionMessage: - "We'd like to send you notifications for the latest news and updates.", + actionMessage: "We'd like to send you notifications for the latest news and updates.", autoAcceptTitle: 'Click Allow', customizeTextEnabled: true, }, @@ -256,8 +256,7 @@ export default class TestContext { message: 'This is an example notification message.', acceptButton: 'Continue', cancelButton: 'No Thanks', - actionMessage: - "We'd like to send you notifications for the latest news and updates.", + actionMessage: "We'd like to send you notifications for the latest news and updates.", autoAcceptTitle: 'Click Allow', customizeTextEnabled: true, }, @@ -272,8 +271,7 @@ export default class TestContext { text: { subscribe: 'Subscribe to push notifications', unsubscribe: 'Unsubscribe from push notifications', - explanation: - 'Get updates from all sorts of things that matter to you', + explanation: 'Get updates from all sorts of things that matter to you', }, unsubscribeEnabled: true, }, @@ -282,8 +280,7 @@ export default class TestContext { name: 'My Website', origin: 'https://www.site.com', proxyOrigin: undefined, - defaultIconUrl: - 'https://onesignal.com/images/notification_logo.png', + defaultIconUrl: 'https://onesignal.com/images/notification_logo.png', proxyOriginEnabled: false, }, webhooks: { @@ -322,8 +319,7 @@ export default class TestContext { 'BLJozaErc0QXdS7ykMyqniAcvfmdoziwfoSN-Mde_OckAbN_XrOC9Zt2Sfz4pD0UnYT5w3frWjF2iTTtjqEBgbE', onesignal_vapid_public_key: 'BMzCIzYqtgz2Bx7S6aPVK6lDWets7kGm-pgo2H4RixFikUaNIoPqjPBBOEWMAfeFjuT9mAvbe-lckGi6vvNEiW0', - safari_web_id: - 'web.onesignal.auto.017d7a1b-f1ef-4fce-a00c-21a546b5491d', + safari_web_id: 'web.onesignal.auto.017d7a1b-f1ef-4fce-a00c-21a546b5491d', outcomes: { direct: { enabled: true, @@ -447,11 +443,9 @@ export default class TestContext { persistNotification: false, webhooks: { cors: true, - 'notification.willDisplay': - 'https://fake-config.com/notification-displayed', + 'notification.willDisplay': 'https://fake-config.com/notification-displayed', 'notification.clicked': 'https://fake-config.com/notification-clicked', - 'notification.dismissed': - 'https://fake-config.com/notification-dismissed', + 'notification.dismissed': 'https://fake-config.com/notification-dismissed', }, notificationClickHandlerMatch: NotificationClickMatchBehavior._Origin, notificationClickHandlerAction: NotificationClickActionBehavior._Focus, diff --git a/__test__/support/environment/TestEnvironment.ts b/__test__/support/environment/TestEnvironment.ts index c52bc1657..082e807a7 100644 --- a/__test__/support/environment/TestEnvironment.ts +++ b/__test__/support/environment/TestEnvironment.ts @@ -6,6 +6,7 @@ import type { ServerAppConfig, } from 'src/shared/config/types'; import type { RecursivePartial } from 'src/shared/context/types'; + import { updateIdentityModel, updatePropertiesModel } from '../helpers/setup'; import { initOSGlobals, stubNotification } from './TestEnvironmentHelpers'; diff --git a/__test__/support/environment/TestEnvironmentHelpers.ts b/__test__/support/environment/TestEnvironmentHelpers.ts index 043b7e239..4c7ee7086 100644 --- a/__test__/support/environment/TestEnvironmentHelpers.ts +++ b/__test__/support/environment/TestEnvironmentHelpers.ts @@ -3,6 +3,7 @@ import { SubscriptionModel } from 'src/core/models/SubscriptionModel'; import { ModelChangeTags } from 'src/core/types/models'; import { setPushToken } from 'src/shared/database/subscription'; import { SubscriptionType } from 'src/shared/subscriptions/constants'; + import { CoreModuleDirector } from '../../../src/core/CoreModuleDirector'; import NotificationsNamespace from '../../../src/onesignal/NotificationsNamespace'; import OneSignal from '../../../src/onesignal/OneSignal'; @@ -36,9 +37,7 @@ export function initOSGlobals(config: TestEnvironmentConfig = {}) { const userNamespace = new UserNamespace(!!config.initUserAndPushSubscription); // TO DO: pass in subscription, and permission global.OneSignal.User = userNamespace; - global.OneSignal.Notifications = new NotificationsNamespace( - config.permission, - ); + global.OneSignal.Notifications = new NotificationsNamespace(config.permission); return global.OneSignal; } diff --git a/__test__/support/helpers/api.ts b/__test__/support/helpers/api.ts index 30321f1e8..5b703e99f 100644 --- a/__test__/support/helpers/api.ts +++ b/__test__/support/helpers/api.ts @@ -1,22 +1,18 @@ import type { IndexableByString } from 'src/page/slidedown/types'; +import { expect } from 'vite-plus/test'; + import { ReaderManager } from '../managers/ReaderManager'; export function isAsyncFunction(fn: () => any): boolean { const fnStr = fn.toString().trim(); - return !!( - fnStr.startsWith('async ') || - fnStr.includes('await') || - fn instanceof Promise - ); + return !!(fnStr.startsWith('async ') || fnStr.includes('await') || fn instanceof Promise); } const STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/gm; const ARGUMENT_NAMES = /([^\s,]+)/g; function getParamNames(func: () => unknown): null | string[] { const fnStr = func.toString().replace(STRIP_COMMENTS, ''); - return fnStr - .slice(fnStr.indexOf('(') + 1, fnStr.indexOf(')')) - .match(ARGUMENT_NAMES); + return fnStr.slice(fnStr.indexOf('(') + 1, fnStr.indexOf(')')).match(ARGUMENT_NAMES); } export const matchNestedProperties = ( @@ -33,19 +29,13 @@ export const matchNestedProperties = ( // Check if all properties are present in the SDK for (const prop of nestedProperties) { - const propertyDescriptor = Object.getOwnPropertyDescriptor( - classPrototype, - prop.name, - ); + const propertyDescriptor = Object.getOwnPropertyDescriptor(classPrototype, prop.name); const isPropertyDefined = propertyDescriptor && - (propertyDescriptor.value !== undefined || - propertyDescriptor.get !== undefined); + (propertyDescriptor.value !== undefined || propertyDescriptor.get !== undefined); if (!isPropertyDefined) { - throw new Error( - `Property ${prop.name} for namespace ${namespaceName} not found`, - ); + throw new Error(`Property ${prop.name} for namespace ${namespaceName} not found`); } } }; @@ -93,9 +83,7 @@ export const matchNestedFunctions = ( }; export const matchApiToSpec = async (parent: object, namespace: string) => { - const rawJson = await ReaderManager.readFile( - __dirname + '/../../../api.json', - ); + const rawJson = await ReaderManager.readFile(__dirname + '/../../../api.json'); const api = JSON.parse(rawJson); matchNestedProperties(api, parent, namespace); matchNestedNamespaces(api, parent, namespace); diff --git a/__test__/support/helpers/configHelper.ts b/__test__/support/helpers/configHelper.ts index 093d77ff7..16ccfde6d 100644 --- a/__test__/support/helpers/configHelper.ts +++ b/__test__/support/helpers/configHelper.ts @@ -1,6 +1,7 @@ import { getServerAppConfig } from 'src/shared/config/app'; import { ConfigIntegrationKind } from 'src/shared/config/constants'; import type { AppConfig, ServerAppConfig } from 'src/shared/config/types'; + import TestContext from '../environment/TestContext'; /** @@ -10,12 +11,8 @@ import TestContext from '../environment/TestContext'; * 2) `convertConfigToVersion2` - the resulting prompt options config should be config schema v2 * @param fakeUserConfig */ -export async function getFinalAppConfig( - fakeUserConfig: any, -): Promise { - const fakeServerConfig = TestContext.getFakeServerAppConfig( - ConfigIntegrationKind._Custom, - ); +export async function getFinalAppConfig(fakeUserConfig: any): Promise { + const fakeServerConfig = TestContext.getFakeServerAppConfig(ConfigIntegrationKind._Custom); fakeUserConfig.appId = fakeServerConfig.app_id; const getFakeServerConfig = () => new Promise((resolve) => { diff --git a/__test__/support/helpers/core.ts b/__test__/support/helpers/core.ts index 019efe63d..59d05a6d7 100644 --- a/__test__/support/helpers/core.ts +++ b/__test__/support/helpers/core.ts @@ -1,5 +1,6 @@ import { SubscriptionModel } from 'src/core/models/SubscriptionModel'; import { SubscriptionType } from 'src/shared/subscriptions/constants'; + import CoreModule from '../../../src/core/CoreModule'; import { CoreModuleDirector } from '../../../src/core/CoreModuleDirector'; diff --git a/__test__/support/helpers/general.ts b/__test__/support/helpers/general.ts index 2f5e1f913..46874ffee 100644 --- a/__test__/support/helpers/general.ts +++ b/__test__/support/helpers/general.ts @@ -1,3 +1,4 @@ +import { vi } from 'vite-plus/test'; /** * DON'T use with vi.useFakeTimers() * Flushes promises within some window diff --git a/__test__/support/helpers/requests.ts b/__test__/support/helpers/requests.ts index efb254842..7e79b30e5 100644 --- a/__test__/support/helpers/requests.ts +++ b/__test__/support/helpers/requests.ts @@ -1,6 +1,8 @@ import { http, HttpResponse } from 'msw'; import type { ISubscription, IUserProperties } from 'src/core/types/api'; import type { NotificationIcons } from 'src/shared/notifications/types'; +import { vi } from 'vite-plus/test'; + import { APP_ID, ONESIGNAL_ID, SUB_ID } from '../../constants'; import { server } from '../mocks/server'; @@ -8,14 +10,11 @@ import { server } from '../mocks/server'; // configs export const mockPageStylesCss = () => { server.use( - http.get( - 'https://onesignal.com/sdks/web/v16/OneSignalSDK.page.styles.css', - () => { - return new HttpResponse('/* CSS */', { - headers: { 'Content-Type': 'text/css' }, - }); - }, - ), + http.get('https://onesignal.com/sdks/web/v16/OneSignalSDK.page.styles.css', () => { + return new HttpResponse('/* CSS */', { + headers: { 'Content-Type': 'text/css' }, + }); + }), ); }; @@ -46,9 +45,7 @@ export const getHandler = ({ return HttpResponse.json(response, { status, - headers: retryAfter - ? { 'Retry-After': retryAfter?.toString() } - : undefined, + headers: retryAfter ? { 'Retry-After': retryAfter?.toString() } : undefined, }); }), ); @@ -62,9 +59,7 @@ const getDeleteAliasUri = (onesignalId: string = ONESIGNAL_ID) => `**/apps/${APP_ID}/users/by/onesignal_id/${onesignalId}/identity/*`; export const addAliasFn = vi.fn(); -export const setAddAliasResponse = ({ - onesignalId, -}: { onesignalId?: string } = {}) => +export const setAddAliasResponse = ({ onesignalId }: { onesignalId?: string } = {}) => getHandler({ uri: getSetAliasUri(onesignalId), method: 'patch', @@ -88,9 +83,7 @@ export const setAddAliasError = ({ }); export const deleteAliasFn = vi.fn(); -export const setDeleteAliasResponse = ({ - onesignalId, -}: { onesignalId?: string } = {}) => +export const setDeleteAliasResponse = ({ onesignalId }: { onesignalId?: string } = {}) => getHandler({ uri: getDeleteAliasUri(onesignalId), method: 'delete', @@ -332,8 +325,7 @@ export const setCreateUserError = ({ }: { status: number; retryAfter?: number; -}) => - getHandler({ uri: getCreateUserUri(), method: 'post', status, retryAfter }); +}) => getHandler({ uri: getCreateUserUri(), method: 'post', status, retryAfter }); // update user export const updateUserFn = vi.fn(); diff --git a/__test__/support/helpers/sdkVersion.ts b/__test__/support/helpers/sdkVersion.ts index 89a428884..970c07b4c 100644 --- a/__test__/support/helpers/sdkVersion.ts +++ b/__test__/support/helpers/sdkVersion.ts @@ -1,11 +1,10 @@ +import { vi, expect } from 'vite-plus/test'; export function expectHeaderToBeSent() { vi.mocked(window.fetch).mock.calls.forEach((params) => { expect(typeof params[0]).toBe('string'); const requestInit = params[1] as RequestInit; const headers = requestInit.headers as Headers; - expect(headers.get('sdk-version')).toMatch( - new RegExp(/^onesignal\/web\/[0-9]{6}$/), - ); + expect(headers.get('sdk-version')).toMatch(new RegExp(/^onesignal\/web\/[0-9]{6}$/)); }); } diff --git a/__test__/support/helpers/setup.ts b/__test__/support/helpers/setup.ts index f030a43bf..e376609d4 100644 --- a/__test__/support/helpers/setup.ts +++ b/__test__/support/helpers/setup.ts @@ -11,6 +11,7 @@ import type { SubscriptionSchema, } from 'src/shared/database/types'; import { RawPushSubscription } from 'src/shared/models/RawPushSubscription'; +import { vi } from 'vite-plus/test'; export const setIsPushEnabled = async (isPushEnabled: boolean) => { await db.put('Options', { key: 'isPushEnabled', value: isPushEnabled }); @@ -68,9 +69,7 @@ export const getDbSubscriptions = async (length: number) => { /** * Update identity model but not trigger action to trigger api call. */ -export const setupIdentityModel = async ( - onesignalID: string = ONESIGNAL_ID, -) => { +export const setupIdentityModel = (onesignalID: string = ONESIGNAL_ID) => { const newIdentityModel = new IdentityModel(); newIdentityModel._onesignalId = onesignalID; OneSignal._coreDirector._identityModelStore._replace( @@ -82,9 +81,7 @@ export const setupIdentityModel = async ( /** * Update properties model but not trigger action to trigger api call. */ -export const setupPropertiesModel = async ( - onesignalID: string = ONESIGNAL_ID, -) => { +export const setupPropertiesModel = async (onesignalID: string = ONESIGNAL_ID) => { const newPropertiesModel = new PropertiesModel(); newPropertiesModel._onesignalId = onesignalID; OneSignal._coreDirector._propertiesModelStore._replace( @@ -99,9 +96,7 @@ export const setupPropertiesModel = async ( /** * Update identity model but not trigger action to trigger api call. */ -export const updateIdentityModel = async < - T extends keyof IdentitySchema & string, ->( +export const updateIdentityModel = ( property: T, value?: IdentitySchema[T], ) => { @@ -112,7 +107,7 @@ export const updateIdentityModel = async < /** * Update properties model but not trigger action to trigger api call. */ -export const updatePropertiesModel = async < +export const updatePropertiesModel = < T extends Exclude< keyof PropertiesSchema, 'modelId' | 'modelName' | 'first_active' | 'last_active' @@ -128,10 +123,7 @@ export const updatePropertiesModel = async < /** * Update subscription model but not trigger action to trigger api call. */ -export const setupSubscriptionModel = async ( - id: string | undefined, - token: string | undefined, -) => { +export const setupSubscriptionModel = (id: string | undefined, token: string | undefined) => { const subscriptionModel = new SubscriptionModel(); subscriptionModel.id = id || ''; subscriptionModel.token = token || ''; @@ -144,11 +136,10 @@ export const setupSubscriptionModel = async ( /** * In case some action triggers a call to loadSdkStylesheet, we need to mock it. */ -export const setupLoadStylesheet = async () => { - vi.spyOn( - OneSignal._context._dynamicResourceLoader, - '_loadSdkStylesheet', - ).mockResolvedValue(ResourceLoadState._Loaded); +export const setupLoadStylesheet = () => { + vi.spyOn(OneSignal._context._dynamicResourceLoader, '_loadSdkStylesheet').mockResolvedValue( + ResourceLoadState._Loaded, + ); }; export const getRawPushSubscription = () => { diff --git a/__test__/support/mocks/MockNotification.ts b/__test__/support/mocks/MockNotification.ts index 08b26f76d..314759248 100644 --- a/__test__/support/mocks/MockNotification.ts +++ b/__test__/support/mocks/MockNotification.ts @@ -93,12 +93,12 @@ export default class MockNotification implements Notification { _options?: boolean | EventListenerOptions, ): void {} - static async requestPermission( - callback?: NotificationPermissionCallback | undefined, + static requestPermission( + callback?: NotificationPermissionCallback, ): Promise { if (callback) { callback(MockNotification.permission); } - return MockNotification.permission; + return Promise.resolve(MockNotification.permission); } } diff --git a/__test__/support/mocks/MockServiceWorker.ts b/__test__/support/mocks/MockServiceWorker.ts index 69e91bf09..866cde023 100644 --- a/__test__/support/mocks/MockServiceWorker.ts +++ b/__test__/support/mocks/MockServiceWorker.ts @@ -1,3 +1,5 @@ +import { vi } from 'vite-plus/test'; + import { PUSH_TOKEN } from '../../constants'; export const mockPushSubscription: PushSubscription = { @@ -15,9 +17,7 @@ export const mockPushSubscription: PushSubscription = { export const mockPushManager = { permissionState: vi.fn().mockResolvedValue('granted'), subscribe: vi.fn().mockResolvedValue(mockPushSubscription), - getSubscription: vi - .fn<() => Promise>() - .mockResolvedValue(mockPushSubscription), + getSubscription: vi.fn<() => Promise>().mockResolvedValue(mockPushSubscription), } satisfies PushManager; const registration: Partial = { diff --git a/__test__/support/utils/DispatchEventUtil.ts b/__test__/support/utils/DispatchEventUtil.ts index 6c5ed17e5..90bfe512f 100644 --- a/__test__/support/utils/DispatchEventUtil.ts +++ b/__test__/support/utils/DispatchEventUtil.ts @@ -1,8 +1,7 @@ import type { EventHandler } from '../../../src/shared/libraries/Emitter'; export class DispatchEventUtil { - private listeners: Map> = - new Map(); + private listeners: Map> = new Map(); public addEventListener( type: string, @@ -25,9 +24,11 @@ export class DispatchEventUtil { if (!handlers) return false; for (const handler of handlers) { - const eventListenerObj = (handler as EventListenerObject).handleEvent; - if (eventListenerObj) eventListenerObj(evt); - else (handler as EventHandler)(evt); + if (typeof handler === 'function') { + handler(evt); + } else { + handler.handleEvent(evt); + } } return true; } diff --git a/__test__/support/utils/Random.ts b/__test__/support/utils/Random.ts deleted file mode 100644 index 08f03c2c6..000000000 --- a/__test__/support/utils/Random.ts +++ /dev/null @@ -1,38 +0,0 @@ -export default class Random { - /** - * Generates a random string of the specified length with the allowed characters. - * @param length The length of the random string. - * @param characterSet A string of characters to allow. Each character of the random string will - * be one character of this set chosen at random. - */ - public static getRandomString( - length: number, - characterSet = 'abcdefghijklmnopqrstuvwxyz0123456789', - ) { - return this.getRandomArray(length, characterSet.length, 0) - .map((n) => characterSet[n]) - .join(''); - } - - public static getRandomNumber(length: number): number { - return Number(Random.getRandomArray(length, 10, 0).join('')); - } - - public static getRandomUint8Array(length: number) { - return new Uint8Array(Random.getRandomArray(length, 255, 0)); - } - - public static getRandomArray( - length: number, - exclusiveMax: number, - inclusiveMin: number, - ) { - return new Array(length) - .fill(0) - .map(() => - Math.floor( - Math.random() * (exclusiveMax - inclusiveMin) + inclusiveMin, - ), - ); - } -} diff --git a/__test__/unit/api/apiJson.test.ts b/__test__/unit/api/apiJson.test.ts index c420d452b..42cc3249d 100644 --- a/__test__/unit/api/apiJson.test.ts +++ b/__test__/unit/api/apiJson.test.ts @@ -1,3 +1,5 @@ +import { describe, test } from 'vite-plus/test'; + import OneSignal from '../../../src/onesignal/OneSignal'; import { matchApiToSpec } from '../../support/helpers/api'; diff --git a/__test__/unit/core/coreModuleDirector.test.ts b/__test__/unit/core/coreModuleDirector.test.ts index a7cfdc236..2b5562e48 100644 --- a/__test__/unit/core/coreModuleDirector.test.ts +++ b/__test__/unit/core/coreModuleDirector.test.ts @@ -1,9 +1,8 @@ +import { describe, test, expect, beforeEach, vi } from 'vite-plus/test'; + import { CoreModuleDirector } from '../../../src/core/CoreModuleDirector'; import { TestEnvironment } from '../../support/environment/TestEnvironment'; -import { - generateNewSubscription, - getCoreModuleDirector, -} from '../../support/helpers/core'; +import { generateNewSubscription, getCoreModuleDirector } from '../../support/helpers/core'; describe('CoreModuleDirector tests', () => { beforeEach(() => { diff --git a/__test__/unit/events/eventsUnique.test.ts b/__test__/unit/events/eventsUnique.test.ts index d7dd50845..eed1f1f49 100644 --- a/__test__/unit/events/eventsUnique.test.ts +++ b/__test__/unit/events/eventsUnique.test.ts @@ -1,3 +1,5 @@ +import { test, expect } from 'vite-plus/test'; + import { ONESIGNAL_EVENTS } from '../../../src/onesignal/OneSignalEvents'; test('Test uniqueness of OneSignal event names', () => { diff --git a/__test__/unit/http/sdkVersion.test.ts b/__test__/unit/http/sdkVersion.test.ts index ebefea0db..408d75afd 100644 --- a/__test__/unit/http/sdkVersion.test.ts +++ b/__test__/unit/http/sdkVersion.test.ts @@ -13,6 +13,8 @@ import { updateSubscriptionById, updateUserByAlias, } from 'src/core/requests/api'; +import { describe, test, beforeAll } from 'vite-plus/test'; + import { expectHeaderToBeSent } from '../../support/helpers/sdkVersion'; describe('Sdk Version Header Tests', () => { @@ -20,12 +22,12 @@ describe('Sdk Version Header Tests', () => { test('POST /users: SDK-Version header is sent', () => { // @ts-expect-error - partial identity object - createNewUser({ appId: APP_ID }, {}); + void createNewUser({ appId: APP_ID }, {}); expectHeaderToBeSent(); }); test('POST /users: header is sent', () => { - createNewUser( + void createNewUser( { appId: APP_ID }, // @ts-expect-error - partial identity object { refresh_device_metadata: true }, @@ -34,7 +36,7 @@ describe('Sdk Version Header Tests', () => { }); test('POST /users: header is sent with subscription id', () => { - createNewUser( + void createNewUser( { appId: APP_ID, subscriptionId: SUB_ID }, // @ts-expect-error - partial identity object {}, @@ -43,7 +45,7 @@ describe('Sdk Version Header Tests', () => { }); test('GET /users/by//: header is sent', () => { - getUserByAlias( + void getUserByAlias( { appId: APP_ID }, { label: IdentityConstants._ExternalID, @@ -54,7 +56,7 @@ describe('Sdk Version Header Tests', () => { }); test('PATCH /users/by//: header is sent', () => { - updateUserByAlias( + void updateUserByAlias( { appId: APP_ID }, { label: IdentityConstants._ExternalID, @@ -66,7 +68,7 @@ describe('Sdk Version Header Tests', () => { }); test('PATCH /users/by//: header is sent with subscription id', () => { - updateUserByAlias( + void updateUserByAlias( { appId: APP_ID, subscriptionId: SUB_ID }, { label: IdentityConstants._ExternalID, @@ -78,7 +80,7 @@ describe('Sdk Version Header Tests', () => { }); test('DELETE /users/by//: header is sent', () => { - deleteUserByAlias( + void deleteUserByAlias( { appId: APP_ID }, { label: IdentityConstants._ExternalID, @@ -89,7 +91,7 @@ describe('Sdk Version Header Tests', () => { }); test('POST /users/by///subscription: header is sent', () => { - createSubscriptionByAlias( + void createSubscriptionByAlias( { appId: APP_ID }, { label: IdentityConstants._ExternalID, @@ -104,7 +106,7 @@ describe('Sdk Version Header Tests', () => { }); test('GET /users/by///identity: header is sent', () => { - getUserIdentity( + void getUserIdentity( { appId: APP_ID }, { label: IdentityConstants._ExternalID, @@ -115,7 +117,7 @@ describe('Sdk Version Header Tests', () => { }); test('DELETE /users/by///identity/: header is sent', () => { - deleteAlias( + void deleteAlias( { appId: APP_ID }, { label: IdentityConstants._ExternalID, @@ -127,7 +129,7 @@ describe('Sdk Version Header Tests', () => { }); test('PATCH /subscriptions/: header is sent', () => { - updateSubscriptionById( + void updateSubscriptionById( { appId: APP_ID }, EXTERNAL_ID, // @ts-expect-error - partial identity object @@ -137,7 +139,7 @@ describe('Sdk Version Header Tests', () => { }); test('DELETE /subscriptions/: header is sent', () => { - deleteSubscriptionById({ appId: APP_ID }, EXTERNAL_ID); + void deleteSubscriptionById({ appId: APP_ID }, EXTERNAL_ID); expectHeaderToBeSent(); }); }); diff --git a/__test__/unit/models/path.test.ts b/__test__/unit/models/path.test.ts index b84c0e36b..5728ed184 100644 --- a/__test__/unit/models/path.test.ts +++ b/__test__/unit/models/path.test.ts @@ -1,3 +1,5 @@ +import { describe, test, expect } from 'vite-plus/test'; + import Path from '../../../src/shared/models/Path'; describe('Path tests', () => { @@ -10,9 +12,7 @@ describe('Path tests', () => { test(`should return correct components for a file-based path`, () => { const path = new Path('file:///c:/web-folder/assets/service-worker.js'); expect(path._getFileName()).toBe('service-worker.js'); - expect(path._getFullPath()).toBe( - 'file:///c:/web-folder/assets/service-worker.js', - ); + expect(path._getFullPath()).toBe('file:///c:/web-folder/assets/service-worker.js'); }); test(`should return case-sensitive correct components for a file-based path`, () => { @@ -24,9 +24,7 @@ describe('Path tests', () => { test(`should return correct components for a double-extension path`, () => { const path = new Path('/web-folder/assets/service-worker.js.php'); expect(path._getFileName()).toBe('service-worker.js.php'); - expect(path._getFullPath()).toBe( - '/web-folder/assets/service-worker.js.php', - ); + expect(path._getFullPath()).toBe('/web-folder/assets/service-worker.js.php'); }); test(`should return correct components for a root-relative path`, () => { @@ -38,24 +36,16 @@ describe('Path tests', () => { test(`should return correct components for an absolute web path`, () => { const path = new Path('https://site.com/web-folder/service-worker.js'); expect(path._getFileName()).toBe('service-worker.js'); - expect(path._getFullPath()).toBe( - 'https://site.com/web-folder/service-worker.js', - ); + expect(path._getFullPath()).toBe('https://site.com/web-folder/service-worker.js'); }); test('should include query string in full path', () => { - const path = new Path( - 'https://site.com/web-folder/service-worker.js?appId=12345', - ); - expect(path._getFullPath()).toBe( - 'https://site.com/web-folder/service-worker.js?appId=12345', - ); + const path = new Path('https://site.com/web-folder/service-worker.js?appId=12345'); + expect(path._getFullPath()).toBe('https://site.com/web-folder/service-worker.js?appId=12345'); }); test(`should not include query string in path filename`, () => { - const path = new Path( - 'https://site.com/web-folder/service-worker.js?appId=12345', - ); + const path = new Path('https://site.com/web-folder/service-worker.js?appId=12345'); expect(path._getFileName()).toBe('service-worker.js'); }); }); diff --git a/__test__/unit/push/registerForPush.test.ts b/__test__/unit/push/registerForPush.test.ts index 68978fcc7..038877a10 100644 --- a/__test__/unit/push/registerForPush.test.ts +++ b/__test__/unit/push/registerForPush.test.ts @@ -1,3 +1,5 @@ +import { describe, test, expect, beforeEach, afterEach, vi } from 'vite-plus/test'; + import * as InitHelper from '../../../src/shared/helpers/init'; import OneSignalEvent from '../../../src/shared/services/OneSignalEvent'; import { TestEnvironment } from '../../support/environment/TestEnvironment'; @@ -43,9 +45,7 @@ describe('Register for push', () => { global.OneSignal._initialized = true; global.OneSignal._initCalled = false; - await expect(InitHelper.registerForPushNotifications()).resolves.toBe( - false, - ); + await expect(InitHelper.registerForPushNotifications()).resolves.toBe(false); expect(spy).toHaveBeenCalledTimes(1); }); }); diff --git a/__test__/unit/pushSubscription/nativePermissionChange.test.ts b/__test__/unit/pushSubscription/nativePermissionChange.test.ts index 4f6de8876..45f047ed1 100644 --- a/__test__/unit/pushSubscription/nativePermissionChange.test.ts +++ b/__test__/unit/pushSubscription/nativePermissionChange.test.ts @@ -1,10 +1,4 @@ -import { - PUSH_TOKEN, - PUSH_TOKEN_2, - SUB_ID, - SUB_ID_2, - SUB_ID_3, -} from '__test__/constants'; +import { PUSH_TOKEN, PUSH_TOKEN_2, SUB_ID, SUB_ID_2, SUB_ID_3 } from '__test__/constants'; import { TestEnvironment } from '__test__/support/environment/TestEnvironment'; import { createPushSub } from '__test__/support/environment/TestEnvironmentHelpers'; import { MockServiceWorker } from '__test__/support/mocks/MockServiceWorker'; @@ -15,12 +9,10 @@ import { checkAndTriggerNotificationPermissionChanged } from 'src/shared/helpers import * as PermissionUtils from 'src/shared/helpers/permissions'; import Emitter from 'src/shared/libraries/Emitter'; import { checkAndTriggerSubscriptionChanged } from 'src/shared/listeners'; +import { describe, test, expect, beforeEach, afterEach, vi } from 'vite-plus/test'; vi.mock('src/shared/libraries/Log'); -const triggerNotificationSpy = vi.spyOn( - PermissionUtils, - 'triggerNotificationPermissionChanged', -); +const triggerNotificationSpy = vi.spyOn(PermissionUtils, 'triggerNotificationPermissionChanged'); describe('Notification Types are set correctly on subscription change', () => { beforeEach(() => { @@ -61,10 +53,7 @@ describe('Notification Types are set correctly on subscription change', () => { const permChangeStringListener = vi.fn(); const permChangeListener = vi.fn(); - OneSignal.Notifications.addEventListener( - 'permissionChange', - permChangeListener, - ); + OneSignal.Notifications.addEventListener('permissionChange', permChangeListener); OneSignal.Notifications.addEventListener( // @ts-expect-error - we dont expose it in the types @@ -75,16 +64,14 @@ describe('Notification Types are set correctly on subscription change', () => { await checkAndTriggerNotificationPermissionChanged(); // should update the db - const dbPermission = await getOptionsValue( - 'notificationPermission', - ); + const dbPermission = await getOptionsValue('notificationPermission'); expect(dbPermission).toBe('granted'); expect(permChangeListener).toHaveBeenCalledWith(true); expect(permChangeStringListener).toHaveBeenCalledWith('granted'); }); }); - describe('checkAndTriggerSubscriptionChanged', async () => { + describe('checkAndTriggerSubscriptionChanged', () => { const setAppState = async (appState: Partial) => { const currentAppState = (await getOptionsValue('appState'))!; await setDBAppState({ diff --git a/__test__/unit/user/user.test.ts b/__test__/unit/user/user.test.ts index a0554938b..c52234ef3 100644 --- a/__test__/unit/user/user.test.ts +++ b/__test__/unit/user/user.test.ts @@ -1,3 +1,5 @@ +import { describe, test, expect, vi } from 'vite-plus/test'; + import User from '../../../src/onesignal/User'; import { TestEnvironment } from '../../support/environment/TestEnvironment'; diff --git a/api.json b/api.json index 6165e07bb..0a1d9cd91 100644 --- a/api.json +++ b/api.json @@ -50,7 +50,7 @@ }, { "name": "setConsentRequired", - "isAsync": true, + "isAsync": false, "args": [ { "name": "requiresConsent", @@ -58,7 +58,7 @@ "optional": false } ], - "returnType": "Promise" + "returnType": "void" } ], "namespaces": ["Slidedown", "Notifications", "Session", "User", "Debug"] diff --git a/build/scripts/validate.js b/build/scripts/validate.js index 9256bce7c..7143ea363 100644 --- a/build/scripts/validate.js +++ b/build/scripts/validate.js @@ -1,7 +1,8 @@ #!/usr/bin/env node -import { indexedDB } from 'fake-indexeddb'; -import 'fake-indexeddb/auto'; import { readFileSync } from 'fs'; + +import 'fake-indexeddb/auto'; +import { indexedDB } from 'fake-indexeddb'; import { JSDOM } from 'jsdom'; const loadJson = (path) => JSON.parse(readFileSync(path, 'utf-8')); @@ -40,9 +41,7 @@ const validateNamespace = (obj, spec, apiSpec, path = 'OneSignal') => { }); spec.properties?.forEach(({ name }) => { - const exists = - name in obj || - Object.getOwnPropertyDescriptor(Object.getPrototypeOf(obj), name); + const exists = name in obj || Object.getOwnPropertyDescriptor(Object.getPrototypeOf(obj), name); if (exists) { if (SHOW_VERBOSE) console.log(`โœ“ ${path}.${name}`); } else { @@ -56,12 +55,7 @@ const validateNamespace = (obj, spec, apiSpec, path = 'OneSignal') => { const nestedSpec = apiSpec[name]; if (nestedSpec) { - const nestedErrors = validateNamespace( - obj[name], - nestedSpec, - apiSpec, - `${path}.${name}`, - ); + const nestedErrors = validateNamespace(obj[name], nestedSpec, apiSpec, `${path}.${name}`); errors.push(...nestedErrors); } } else { @@ -76,10 +70,7 @@ const validateBundle = async () => { console.log('๐Ÿ” Validating OneSignal bundle...\n'); const apiSpec = loadJson('api.json'); - const bundle = readFileSync( - 'build/releases/OneSignalSDK.page.es6.js', - 'utf-8', - ); + const bundle = readFileSync('build/releases/OneSignalSDK.page.es6.js', 'utf-8'); const window = setupEnvironment(); const script = window.document.createElement('script'); @@ -90,11 +81,7 @@ const validateBundle = async () => { if (!window.OneSignal) throw new Error('OneSignal not found'); - const errors = validateNamespace( - window.OneSignal, - apiSpec.OneSignal, - apiSpec, - ); + const errors = validateNamespace(window.OneSignal, apiSpec.OneSignal, apiSpec); if (errors.length > 0) { console.log('โŒ Validation failures:'); diff --git a/docker-compose.yml b/docker-compose.yml index ce1a20a86..363b0cdaa 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,19 +1,19 @@ services: onesignal-web-sdk-dev: - image: onesignal/web-sdk-dev - container_name: web-sdk-dev - build: . - volumes: - - .:/sdk:cached - - /sdk/node_modules - - /sdk/preview/node_modules - - ./build/releases:/sdk/build/releases - ports: - - published: 4001 - target: 4001 - - published: 4002 - target: 4002 - command: /bin/sh -c "./docker/docker-entry-point.sh" + image: onesignal/web-sdk-dev + container_name: web-sdk-dev + build: . + volumes: + - .:/sdk:cached + - /sdk/node_modules + - /sdk/preview/node_modules + - ./build/releases:/sdk/build/releases + ports: + - published: 4001 + target: 4001 + - published: 4002 + target: 4002 + command: /bin/sh -c "./docker/docker-entry-point.sh" # Named volumes that are persisted between docker-compose rebuilds. # If you need to remove these volumes for some reason, run `docker-compose down -v` volumes: diff --git a/index.html b/index.html index ffe986662..707590647 100644 --- a/index.html +++ b/index.html @@ -18,8 +18,7 @@ max-width: 350px; z-index: 10000; animation: slideIn 0.3s ease-out; - font-family: - -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; } .in-app-notification h4 { margin: 0 0 8px 0; @@ -83,9 +82,7 @@