mirror of
https://github.com/cypress-io/cypress.git
synced 2026-02-25 18:41:08 -06:00
Merge branch 'develop' into cache-sessions
# Conflicts: # packages/app/src/runner/event-manager.ts # packages/driver/cypress.config.ts # packages/driver/src/cy/commands/sessions/index.ts # packages/reporter/src/sessions/sessions.tsx # packages/server/lib/project-base.ts # packages/server/lib/server-base.ts # system-tests/test/session_spec.ts
This commit is contained in:
3
.circleci/cache-version.txt
Normal file
3
.circleci/cache-version.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
# Bump this version to force CI to re-create the cache from scratch.
|
||||
|
||||
9-13-22
|
||||
@@ -27,6 +27,7 @@ mainBuildFilters: &mainBuildFilters
|
||||
branches:
|
||||
only:
|
||||
- develop
|
||||
- fix-ci-deps
|
||||
|
||||
# usually we don't build Mac app - it takes a long time
|
||||
# but sometimes we want to really confirm we are doing the right thing
|
||||
@@ -35,8 +36,7 @@ macWorkflowFilters: &darwin-workflow-filters
|
||||
when:
|
||||
or:
|
||||
- equal: [ develop, << pipeline.git.branch >> ]
|
||||
- equal: [ 'tbiethman/22272-globbing-working-dir', << pipeline.git.branch >> ]
|
||||
- equal: [ 'skip-or-fix-flaky-tests-2', << pipeline.git.branch >> ]
|
||||
- equal: [ 'correct-dashboard-results', << pipeline.git.branch >> ]
|
||||
- matches:
|
||||
pattern: "-release$"
|
||||
value: << pipeline.git.branch >>
|
||||
@@ -45,8 +45,6 @@ linuxArm64WorkflowFilters: &linux-arm64-workflow-filters
|
||||
when:
|
||||
or:
|
||||
- equal: [ develop, << pipeline.git.branch >> ]
|
||||
- equal: [ "lmiller/experimental-single-tab-component-testing", << pipeline.git.branch >> ]
|
||||
- equal: [ 'skip-or-fix-flaky-tests-2', << pipeline.git.branch >> ]
|
||||
- matches:
|
||||
pattern: "-release$"
|
||||
value: << pipeline.git.branch >>
|
||||
@@ -66,8 +64,7 @@ windowsWorkflowFilters: &windows-workflow-filters
|
||||
or:
|
||||
- equal: [ develop, << pipeline.git.branch >> ]
|
||||
- equal: [ linux-arm64, << pipeline.git.branch >> ]
|
||||
- equal: [ 'marktnoonan/windows-path-fix', << pipeline.git.branch >> ]
|
||||
- equal: [ 'skip-or-fix-flaky-tests-2', << pipeline.git.branch >> ]
|
||||
- equal: [ 'lmiller/fixing-flake-1', << pipeline.git.branch >> ]
|
||||
- matches:
|
||||
pattern: "-release$"
|
||||
value: << pipeline.git.branch >>
|
||||
@@ -132,7 +129,7 @@ commands:
|
||||
- run:
|
||||
name: Check current branch to persist artifacts
|
||||
command: |
|
||||
if [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "tbiethman/22272-globbing-working-dir" ]]; then
|
||||
if [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "webkit-experimental" ]]; then
|
||||
echo "Not uploading artifacts or posting install comment for this branch."
|
||||
circleci-agent step halt
|
||||
fi
|
||||
@@ -178,6 +175,14 @@ commands:
|
||||
mv ~/cypress/system-tests/node_modules /tmp/node_modules_cache/system-tests_node_modules
|
||||
mv ~/cypress/globbed_node_modules /tmp/node_modules_cache/globbed_node_modules
|
||||
|
||||
install-webkit-deps:
|
||||
steps:
|
||||
- run:
|
||||
name: Install WebKit dependencies
|
||||
command: |
|
||||
npx playwright install webkit
|
||||
npx playwright install-deps webkit
|
||||
|
||||
build-and-persist:
|
||||
description: Save entire folder as artifact for other jobs to run without reinstalling
|
||||
steps:
|
||||
@@ -219,7 +224,7 @@ commands:
|
||||
command: node ./scripts/get-platform-key.js > platform_key
|
||||
- restore_cache:
|
||||
name: Restore cache state, to check for known modules cache existence
|
||||
key: v{{ .Environment.CACHE_VERSION }}-{{ checksum "platform_key" }}-node-modules-cache-{{ checksum "circle_cache_key" }}
|
||||
key: v{{ checksum ".circleci/cache-version.txt" }}-{{ checksum "platform_key" }}-node-modules-cache-{{ checksum "circle_cache_key" }}
|
||||
- run:
|
||||
name: Move node_modules back from /tmp
|
||||
command: |
|
||||
@@ -246,7 +251,7 @@ commands:
|
||||
- restore_cache:
|
||||
name: Restore system tests node_modules cache
|
||||
keys:
|
||||
- v{{ .Environment.CACHE_VERSION }}-{{ checksum "platform_key" }}-system-tests-projects-node-modules-cache-{{ checksum "system_tests_cache_key" }}
|
||||
- v{{ checksum ".circleci/cache-version.txt" }}-{{ checksum "platform_key" }}-system-tests-projects-node-modules-cache-{{ checksum "system_tests_cache_key" }}
|
||||
|
||||
update_cached_system_tests_deps:
|
||||
description: 'Update the cached node_modules for projects in "system-tests/projects/**"'
|
||||
@@ -260,7 +265,7 @@ commands:
|
||||
- restore_cache:
|
||||
name: Restore cache state, to check for known modules cache existence
|
||||
keys:
|
||||
- v{{ .Environment.CACHE_VERSION }}-{{ checksum "platform_key" }}-state-of-system-tests-projects-node-modules-cache-{{ checksum "system_tests_cache_key" }}
|
||||
- v{{ checksum ".circleci/cache-version.txt" }}-{{ checksum "platform_key" }}-state-of-system-tests-projects-node-modules-cache-{{ checksum "system_tests_cache_key" }}
|
||||
- run:
|
||||
name: Send root honeycomb event for this CI build
|
||||
command: cd system-tests/scripts && node ./send-root-honeycomb-event.js
|
||||
@@ -274,20 +279,20 @@ commands:
|
||||
- restore_cache:
|
||||
name: Restore system tests node_modules cache
|
||||
keys:
|
||||
- v{{ .Environment.CACHE_VERSION }}-{{ checksum "platform_key" }}-system-tests-projects-node-modules-cache-{{ checksum "system_tests_cache_key" }}
|
||||
- v{{ .Environment.CACHE_VERSION }}-{{ checksum "platform_key" }}-system-tests-projects-node-modules-cache-
|
||||
- v{{ checksum ".circleci/cache-version.txt" }}-{{ checksum "platform_key" }}-system-tests-projects-node-modules-cache-{{ checksum "system_tests_cache_key" }}
|
||||
- v{{ checksum ".circleci/cache-version.txt" }}-{{ checksum "platform_key" }}-system-tests-projects-node-modules-cache-
|
||||
- run:
|
||||
name: Update system-tests node_modules cache
|
||||
command: yarn workspace @tooling/system-tests projects:yarn:install
|
||||
- save_cache:
|
||||
name: Save system tests node_modules cache
|
||||
key: v{{ .Environment.CACHE_VERSION }}-{{ checksum "platform_key" }}-system-tests-projects-node-modules-cache-{{ checksum "system_tests_cache_key" }}
|
||||
key: v{{ checksum ".circleci/cache-version.txt" }}-{{ checksum "platform_key" }}-system-tests-projects-node-modules-cache-{{ checksum "system_tests_cache_key" }}
|
||||
paths:
|
||||
- /tmp/cy-system-tests-node-modules
|
||||
- run: touch /tmp/system_tests_node_modules_installed
|
||||
- save_cache:
|
||||
name: Save system tests node_modules cache state key
|
||||
key: v{{ .Environment.CACHE_VERSION }}-{{ checksum "platform_key" }}-state-of-system-tests-projects-node-modules-cache-{{ checksum "system_tests_cache_key" }}
|
||||
key: v{{ checksum ".circleci/cache-version.txt" }}-{{ checksum "platform_key" }}-state-of-system-tests-projects-node-modules-cache-{{ checksum "system_tests_cache_key" }}
|
||||
paths:
|
||||
- /tmp/system_tests_node_modules_installed
|
||||
|
||||
@@ -307,7 +312,7 @@ commands:
|
||||
command: node ./scripts/get-platform-key.js > platform_key
|
||||
- restore_cache:
|
||||
name: Restore cache state, to check for known modules cache existence
|
||||
key: v{{ .Environment.CACHE_VERSION }}-{{ checksum "platform_key" }}-state-of-node-modules-cache-{{ checksum "circle_cache_key" }}
|
||||
key: v{{ checksum ".circleci/cache-version.txt" }}-{{ checksum "platform_key" }}-state-of-node-modules-cache-{{ checksum "circle_cache_key" }}
|
||||
- run:
|
||||
name: Bail if cache exists
|
||||
command: |
|
||||
@@ -319,7 +324,7 @@ commands:
|
||||
- restore_cache:
|
||||
name: Restore weekly yarn cache
|
||||
keys:
|
||||
- v{{ .Environment.CACHE_VERSION }}-{{ checksum "platform_key" }}-deps-root-weekly-{{ checksum "cache_date" }}
|
||||
- v{{ checksum ".circleci/cache-version.txt" }}-{{ checksum "platform_key" }}-deps-root-weekly-{{ checksum "cache_date" }}
|
||||
- run:
|
||||
name: Install Node Modules
|
||||
command: |
|
||||
@@ -336,7 +341,7 @@ commands:
|
||||
steps:
|
||||
- save_cache:
|
||||
name: Saving node modules for root, cli, and all globbed workspace packages
|
||||
key: v{{ .Environment.CACHE_VERSION }}-{{ checksum "platform_key" }}-node-modules-cache-{{ checksum "circle_cache_key" }}
|
||||
key: v{{ checksum ".circleci/cache-version.txt" }}-{{ checksum "platform_key" }}-node-modules-cache-{{ checksum "circle_cache_key" }}
|
||||
paths:
|
||||
- node_modules
|
||||
- cli/node_modules
|
||||
@@ -347,18 +352,18 @@ commands:
|
||||
steps:
|
||||
- save_cache:
|
||||
name: Saving node modules for root, cli, and all globbed workspace packages
|
||||
key: v{{ .Environment.CACHE_VERSION }}-{{ checksum "platform_key" }}-node-modules-cache-{{ checksum "circle_cache_key" }}
|
||||
key: v{{ checksum ".circleci/cache-version.txt" }}-{{ checksum "platform_key" }}-node-modules-cache-{{ checksum "circle_cache_key" }}
|
||||
paths:
|
||||
- /tmp/node_modules_cache
|
||||
- run: touch node_modules_installed
|
||||
- save_cache:
|
||||
name: Saving node-modules cache state key
|
||||
key: v{{ .Environment.CACHE_VERSION }}-{{ checksum "platform_key" }}-state-of-node-modules-cache-{{ checksum "circle_cache_key" }}
|
||||
key: v{{ checksum ".circleci/cache-version.txt" }}-{{ checksum "platform_key" }}-state-of-node-modules-cache-{{ checksum "circle_cache_key" }}
|
||||
paths:
|
||||
- node_modules_installed
|
||||
- save_cache:
|
||||
name: Save weekly yarn cache
|
||||
key: v{{ .Environment.CACHE_VERSION }}-{{ checksum "platform_key" }}-deps-root-weekly-{{ checksum "cache_date" }}
|
||||
key: v{{ checksum ".circleci/cache-version.txt" }}-{{ checksum "platform_key" }}-deps-root-weekly-{{ checksum "cache_date" }}
|
||||
paths:
|
||||
- ~/.yarn
|
||||
- ~/.cy-npm-cache
|
||||
@@ -462,6 +467,11 @@ commands:
|
||||
- install-chrome:
|
||||
channel: <<parameters.install-chrome-channel>>
|
||||
version: $(node ./scripts/get-browser-version.js chrome:<<parameters.install-chrome-channel>>)
|
||||
- when:
|
||||
condition:
|
||||
equal: [ webkit, << parameters.browser >> ]
|
||||
steps:
|
||||
- install-webkit-deps
|
||||
- run:
|
||||
name: Run driver tests in Cypress
|
||||
environment:
|
||||
@@ -470,11 +480,6 @@ commands:
|
||||
echo Current working directory is $PWD
|
||||
echo Total containers $CIRCLE_NODE_TOTAL
|
||||
|
||||
if [[ "<<parameters.browser>>" = "webkit" ]]; then
|
||||
npx playwright install webkit
|
||||
npx playwright install-deps webkit
|
||||
fi
|
||||
|
||||
if [[ -v MAIN_RECORD_KEY ]]; then
|
||||
# internal PR
|
||||
if <<parameters.experimentalSessionAndOrigin>>; then
|
||||
@@ -610,6 +615,11 @@ commands:
|
||||
steps:
|
||||
- restore_cached_workspace
|
||||
- restore_cached_system_tests_deps
|
||||
- when:
|
||||
condition:
|
||||
equal: [ webkit, << parameters.browser >> ]
|
||||
steps:
|
||||
- install-webkit-deps
|
||||
- run:
|
||||
name: Run system tests
|
||||
command: |
|
||||
@@ -1272,9 +1282,12 @@ jobs:
|
||||
run-webpack-dev-server-integration-tests,
|
||||
run-vite-dev-server-integration-tests
|
||||
- run:
|
||||
# Sometimes, even though all the circle jobs have finished, Percy times out during `build:finalize`
|
||||
# If all other jobs finish but `build:finalize` fails, we retry it once
|
||||
name: Finalize percy build - allows single retry
|
||||
command: |
|
||||
PERCY_PARALLEL_NONCE=$CIRCLE_WORKFLOW_WORKSPACE_ID \
|
||||
yarn percy build:finalize
|
||||
yarn percy build:finalize || yarn percy build:finalize
|
||||
|
||||
cli-visual-tests:
|
||||
<<: *defaults
|
||||
@@ -1324,7 +1337,7 @@ jobs:
|
||||
steps:
|
||||
- run: yarn test-scripts
|
||||
# make sure packages with TypeScript can be transpiled to JS
|
||||
- run: yarn lerna run build-prod --stream
|
||||
- run: yarn lerna run build-prod --stream --concurrency 4
|
||||
# run unit tests from each individual package
|
||||
- run: yarn test
|
||||
# run type checking for each individual package
|
||||
@@ -1448,6 +1461,13 @@ jobs:
|
||||
- run-system-tests:
|
||||
browser: firefox
|
||||
|
||||
system-tests-webkit:
|
||||
<<: *defaults
|
||||
parallelism: 8
|
||||
steps:
|
||||
- run-system-tests:
|
||||
browser: webkit
|
||||
|
||||
system-tests-non-root:
|
||||
<<: *defaults
|
||||
steps:
|
||||
@@ -1831,7 +1851,6 @@ jobs:
|
||||
|
||||
npm-react:
|
||||
<<: *defaults
|
||||
parallelism: 8
|
||||
steps:
|
||||
- restore_cached_workspace
|
||||
- run:
|
||||
@@ -2320,7 +2339,7 @@ linux-x64-workflow: &linux-x64-workflow
|
||||
requires:
|
||||
- build
|
||||
- percy-finalize:
|
||||
context: test-runner:poll-circle-workflow
|
||||
context: [test-runner:poll-circle-workflow, test-runner:percy]
|
||||
required_env_var: PERCY_TOKEN # skips job if not defined (external PR)
|
||||
requires:
|
||||
- build
|
||||
@@ -2364,6 +2383,10 @@ linux-x64-workflow: &linux-x64-workflow
|
||||
context: test-runner:performance-tracking
|
||||
requires:
|
||||
- system-tests-node-modules-install
|
||||
- system-tests-webkit:
|
||||
context: test-runner:performance-tracking
|
||||
requires:
|
||||
- system-tests-node-modules-install
|
||||
- system-tests-non-root:
|
||||
context: test-runner:performance-tracking
|
||||
executor: non-root-docker-user
|
||||
33
.github/workflows/snyk_sca_scan.yaml
vendored
Normal file
33
.github/workflows/snyk_sca_scan.yaml
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
name: Snyk Software Composition Analysis Scan
|
||||
# This git workflow leverages Snyk actions to perform a Software Composition
|
||||
# Analysis scan on our Opensource libraries upon Pull Requests to Master &
|
||||
# Develop branches. We use this as a control to prevent vulnerable packages
|
||||
# from being introduced into the codebase.
|
||||
on:
|
||||
pull_request_target:
|
||||
types:
|
||||
- opened
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
jobs:
|
||||
Snyk_SCA_Scan:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [16.x]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Setting up Node
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- name: Installing snyk-delta and dependencies
|
||||
run: npm i -g snyk-delta
|
||||
- uses: snyk/actions/setup@master
|
||||
- name: Perform SCA Scan
|
||||
continue-on-error: false
|
||||
run: |
|
||||
snyk test --yarn-workspaces --strict-out-of-sync=false --detection-depth=6 --exclude=docker,Dockerfile --severity-threshold=critical
|
||||
env:
|
||||
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
|
||||
29
.github/workflows/snyk_static_analysis_scan.yaml
vendored
Normal file
29
.github/workflows/snyk_static_analysis_scan.yaml
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
name: Snyk Static Analysis Scan
|
||||
# This git workflow leverages Snyk actions to perform a Static Application
|
||||
# Testing scan (SAST) on our first-party code upon Pull Requests to Master &
|
||||
# Develop branches. We use this as a control to prevent vulnerabilities
|
||||
# from being introduced into the codebase.
|
||||
on:
|
||||
pull_request_target:
|
||||
types:
|
||||
- opened
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
jobs:
|
||||
Snyk_SAST_Scan :
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: snyk/actions/setup@master
|
||||
- name: Perform Static Analysis Test
|
||||
continue-on-error: true
|
||||
run: |
|
||||
snyk code test --yarn-workspaces --strict-out-of-sync=false --detection-depth=6 --exclude=docker,Dockerfile --severity-threshold=high
|
||||
env:
|
||||
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
|
||||
# The Following Requires Advanced Security License
|
||||
# - name: Upload results to Github Code Scanning
|
||||
# uses: github/codeql-action/upload-sarif@v1
|
||||
# with:
|
||||
# sarif_file: snyk_sarif
|
||||
@@ -1,10 +1,10 @@
|
||||
name: Add issue/PR to project
|
||||
name: 'Triage: add issue/PR to project'
|
||||
|
||||
on:
|
||||
issues:
|
||||
types:
|
||||
- opened
|
||||
pull_request:
|
||||
pull_request_target:
|
||||
types:
|
||||
- opened
|
||||
|
||||
39
.github/workflows/triage_add_to_routed_project.yml
vendored
Normal file
39
.github/workflows/triage_add_to_routed_project.yml
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
name: 'Triage: route to team project board'
|
||||
on:
|
||||
issues:
|
||||
types:
|
||||
- labeled
|
||||
jobs:
|
||||
route-to-e2e:
|
||||
if: github.event.label.name == 'routed-to-e2e'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Get project data
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.ADD_TO_PROJECT_TOKEN }}
|
||||
ORGANIZATION: 'cypress-io'
|
||||
PROJECT_NUMBER: 10
|
||||
run: |
|
||||
gh api graphql -f query='
|
||||
query($org: String!, $number: Int!) {
|
||||
organization(login: $org){
|
||||
projectV2(number: $number) {
|
||||
id
|
||||
}
|
||||
}
|
||||
}' -f org=$ORGANIZATION -F number=$PROJECT_NUMBER > project_data.json
|
||||
|
||||
echo 'PROJECT_ID='$(jq -r '.data.organization.projectV2.id' project_data.json) >> $GITHUB_ENV
|
||||
- name: add issue to e2e project
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.ADD_TO_PROJECT_TOKEN }}
|
||||
ISSUE_ID: ${{ github.event.issue.node_id }}
|
||||
run: |
|
||||
gh api graphql -f query='
|
||||
mutation($project:ID!, $issue:ID!) {
|
||||
addProjectV2ItemById(input: {projectId: $project, contentId: $issue}) {
|
||||
item {
|
||||
id
|
||||
}
|
||||
}
|
||||
}' -f project=$PROJECT_ID -f issue=$ISSUE_ID
|
||||
93
.github/workflows/triage_closed_issue_comment.yml
vendored
Normal file
93
.github/workflows/triage_closed_issue_comment.yml
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
name: 'Triage: closed issue comment'
|
||||
on:
|
||||
issue_comment:
|
||||
types:
|
||||
- created
|
||||
jobs:
|
||||
move-to-new-issue-status:
|
||||
if: |
|
||||
!github.event.issue.pull_request &&
|
||||
github.event.issue.state == 'closed' &&
|
||||
github.event.comment.created_at != github.event.issue.closed_at &&
|
||||
github.event.sender.login != 'cypress-bot'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Get project data
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.ADD_TO_PROJECT_TOKEN }}
|
||||
ORGANIZATION: 'cypress-io'
|
||||
REPOSITORY: 'cypress'
|
||||
PROJECT_NUMBER: 9
|
||||
ISSUE_NUMBER: ${{ github.event.issue.number }}
|
||||
run: |
|
||||
gh api graphql -f query='
|
||||
query($org: String!, $repo: String!, $project: Int!, $issue: Int!) {
|
||||
organization(login: $org) {
|
||||
repository(name: $repo) {
|
||||
issue(number: $issue) {
|
||||
projectItems(first: 10, includeArchived: false) {
|
||||
nodes {
|
||||
id
|
||||
fieldValueByName(name: "Status") {
|
||||
... on ProjectV2ItemFieldSingleSelectValue {
|
||||
name
|
||||
field {
|
||||
... on ProjectV2SingleSelectField {
|
||||
project {
|
||||
... on ProjectV2 {
|
||||
id
|
||||
number
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
projectV2(number: $project) {
|
||||
field(name: "Status") {
|
||||
... on ProjectV2SingleSelectField {
|
||||
id
|
||||
options {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}' -f org=$ORGANIZATION -f repo=$REPOSITORY -F issue=$ISSUE_NUMBER -F project=$PROJECT_NUMBER > project_data.json
|
||||
|
||||
echo 'PROJECT_ID='$(jq -r '.data.organization.repository.issue.projectItems.nodes[].fieldValueByName.field.project | select(.number == ${{ env.PROJECT_NUMBER }}) | .id' project_data.json) >> $GITHUB_ENV
|
||||
echo 'PROJECT_ITEM_ID='$(jq -r '.data.organization.repository.issue.projectItems.nodes[] | select(.fieldValueByName.field.project.number == ${{ env.PROJECT_NUMBER }}) | .id' project_data.json) >> $GITHUB_ENV
|
||||
echo 'STATUS_FIELD_ID='$(jq -r '.data.organization.projectV2.field | .id' project_data.json) >> $GITHUB_ENV
|
||||
echo 'STATUS='$(jq -r '.data.organization.repository.issue.projectItems.nodes[].fieldValueByName | select(.field.project.number == ${{ env.PROJECT_NUMBER }}) | .name' project_data.json) >> $GITHUB_ENV
|
||||
echo 'NEW_ISSUE_OPTION_ID='$(jq -r '.data.organization.projectV2.field.options[] | select(.name== "New Issue") | .id' project_data.json) >> $GITHUB_ENV
|
||||
- name: Move issue to New Issue status
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.ADD_TO_PROJECT_TOKEN }}
|
||||
if: env.STATUS == 'Closed'
|
||||
run: |
|
||||
gh api graphql -f query='
|
||||
mutation (
|
||||
$project: ID!
|
||||
$item: ID!
|
||||
$status_field: ID!
|
||||
$status_value: String!
|
||||
) {
|
||||
updateProjectV2ItemFieldValue(input: {
|
||||
projectId: $project
|
||||
itemId: $item
|
||||
fieldId: $status_field
|
||||
value: {
|
||||
singleSelectOptionId: $status_value
|
||||
}
|
||||
}) {
|
||||
projectV2Item {
|
||||
id
|
||||
}
|
||||
}
|
||||
}' -f project=$PROJECT_ID -f item=$PROJECT_ITEM_ID -f status_field=$STATUS_FIELD_ID -f status_value=$NEW_ISSUE_OPTION_ID
|
||||
114
.github/workflows/triage_issue_metrics.yml
vendored
Normal file
114
.github/workflows/triage_issue_metrics.yml
vendored
Normal file
@@ -0,0 +1,114 @@
|
||||
name: 'Triage: issue metrics'
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
startDate:
|
||||
description: 'Start date (YYYY-MM-DD)'
|
||||
type: date
|
||||
endDate:
|
||||
description: 'End date (YYYY-MM-DD)'
|
||||
type: date
|
||||
jobs:
|
||||
seven-day-close:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/github-script@v6
|
||||
env:
|
||||
ORGANIZATION: 'cypress-io'
|
||||
REPOSITORY: 'cypress'
|
||||
PROJECT_NUMBER: 9
|
||||
with:
|
||||
github-token: ${{ secrets.ADD_TO_PROJECT_TOKEN }}
|
||||
script: |
|
||||
const ROUTED_TO_LABELS = ['routed-to-e2e', 'routed-to-ct']
|
||||
const MS_PER_DAY = 1000 * 60 * 60 * 24
|
||||
const { REPOSITORY, ORGANIZATION, PROJECT_NUMBER } = process.env
|
||||
|
||||
const issues = []
|
||||
|
||||
const determineDateRange = () => {
|
||||
const inputStartDate = '${{ inputs.startDate }}'
|
||||
const inputEndDate = '${{ inputs.endDate }}'
|
||||
|
||||
if (inputStartDate && inputEndDate) {
|
||||
return { startDate: inputStartDate, endDate: inputEndDate }
|
||||
}
|
||||
|
||||
if (inputStartDate || inputEndDate) {
|
||||
core.setFailed('Both startDate and endDate are required if one is provided.')
|
||||
}
|
||||
|
||||
const startDate = new Date()
|
||||
|
||||
startDate.setDate(startDate.getDate() - 6)
|
||||
|
||||
return { startDate: startDate.toISOString().split('T')[0], endDate: (new Date()).toISOString().split('T')[0] }
|
||||
}
|
||||
|
||||
const dateRange = determineDateRange()
|
||||
const query = `is:issue+repo:${ORGANIZATION}/${REPOSITORY}+project:${ORGANIZATION}/${PROJECT_NUMBER}+created:${dateRange.startDate}..${dateRange.endDate}`
|
||||
|
||||
const findLabelDateTime = async (issueNumber) => {
|
||||
const iterator = github.paginate.iterator(github.rest.issues.listEventsForTimeline, {
|
||||
owner: ORGANIZATION,
|
||||
repo: REPOSITORY,
|
||||
issue_number: issueNumber,
|
||||
})
|
||||
|
||||
for await (const { data: timelineData } of iterator) {
|
||||
for (const timelineItem of timelineData) {
|
||||
if (timelineItem.event === 'labeled' && ROUTED_TO_LABELS.includes(timelineItem.label.name)) {
|
||||
return timelineItem.created_at
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const calculateElapsedDays = (createdAt, routedOrClosedAt) => {
|
||||
return Math.round((new Date(routedOrClosedAt) - new Date(createdAt)) / MS_PER_DAY, 0)
|
||||
}
|
||||
|
||||
const iterator = github.paginate.iterator(github.rest.search.issuesAndPullRequests, {
|
||||
q: query,
|
||||
per_page: 100,
|
||||
})
|
||||
|
||||
for await (const { data } of iterator) {
|
||||
for (const issue of data) {
|
||||
let routedOrClosedAt
|
||||
|
||||
if (!issue.pull_request) {
|
||||
const routedLabel = issue.labels.find((label) => ROUTED_TO_LABELS.includes(label.name))
|
||||
|
||||
if (routedLabel) {
|
||||
routedOrClosedAt = await findLabelDateTime(issue.number)
|
||||
} else if (issue.state === 'closed') {
|
||||
routedOrClosedAt = issue.closed_at
|
||||
}
|
||||
|
||||
let elapsedDays
|
||||
|
||||
if (routedOrClosedAt) {
|
||||
elapsedDays = calculateElapsedDays(issue.created_at, routedOrClosedAt)
|
||||
}
|
||||
|
||||
issues.push({
|
||||
number: issue.number,
|
||||
title: issue.title,
|
||||
state: issue.state,
|
||||
url: issue.html_url,
|
||||
createdAt: issue.created_at,
|
||||
routedOrClosedAt,
|
||||
elapsedDays,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const issuesRoutedOrClosedIn7Days = issues.filter((issue) => issue.elapsedDays <= 7).length
|
||||
const percentage = Number(issues.length > 0 ? issuesRoutedOrClosedIn7Days / issues.length : 0).toLocaleString(undefined, { style: 'percent', minimumFractionDigits: 2 })
|
||||
|
||||
console.log(`Triage Metrics (${dateRange.startDate} - ${dateRange.endDate})`)
|
||||
console.log('Total issues:', issues.length)
|
||||
console.log(`Issues routed/closed within 7 days: ${issuesRoutedOrClosedIn7Days} (${percentage})`)
|
||||
@@ -461,7 +461,7 @@ There are also a set of system tests in [`system-tests`](system-tests) which att
|
||||
|
||||
Additionally, we test the code by running it against various other example projects in CI. See CI badges and links at the top of this document.
|
||||
|
||||
If you're curious how we manage all of these tests in CI check out our [`circle.yml`](circle.yml) file found in the root `cypress` directory.
|
||||
If you're curious how we manage all of these tests in CI check out our [CircleCI config](.circleci/config.yml).
|
||||
|
||||
#### Docker
|
||||
|
||||
@@ -474,7 +474,7 @@ Sometimes tests pass locally, but fail in CI. Our CI environment is dockerized.
|
||||
$ yarn docker
|
||||
```
|
||||
|
||||
There is a script [scripts/run-docker-local.sh](scripts/run-docker-local.sh) that runs the cypress image (see [circle.yml](circle.yml) for the current image name).
|
||||
There is a script [scripts/run-docker-local.sh](scripts/run-docker-local.sh) that runs the cypress image (see [CircleCI config](.circleci/config.yml) for the current image name).
|
||||
|
||||
The image will start and will map the root of the repository to `/cypress` inside the image. Now you can modify the files using your favorite environment and rerun tests inside the docker environment.
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"chrome:beta": "105.0.5195.28",
|
||||
"chrome:stable": "104.0.5112.101"
|
||||
"chrome:stable": "104.0.5112.101",
|
||||
"chrome:minimum": "64.0.3282.0"
|
||||
}
|
||||
|
||||
3
cli/.gitignore
vendored
3
cli/.gitignore
vendored
@@ -19,4 +19,5 @@ vue
|
||||
vue2
|
||||
react*
|
||||
mount-utils
|
||||
angular
|
||||
angular
|
||||
svelte
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
"dayjs": "^1.10.4",
|
||||
"debug": "^4.3.2",
|
||||
"enquirer": "^2.3.6",
|
||||
"eventemitter2": "^6.4.3",
|
||||
"eventemitter2": "6.4.7",
|
||||
"execa": "4.1.0",
|
||||
"executable": "^4.1.1",
|
||||
"extract-zip": "2.0.1",
|
||||
@@ -108,7 +108,8 @@
|
||||
"react",
|
||||
"vue2",
|
||||
"react18",
|
||||
"angular"
|
||||
"angular",
|
||||
"svelte"
|
||||
],
|
||||
"bin": {
|
||||
"cypress": "bin/cypress"
|
||||
@@ -155,6 +156,11 @@
|
||||
"import": "./angular/dist/index.js",
|
||||
"require": "./angular/dist/index.js",
|
||||
"types": "./angular/dist/index.d.ts"
|
||||
},
|
||||
"./svelte": {
|
||||
"import": "./svelte/dist/cypress-svelte.esm-bundler.js",
|
||||
"require": "./svelte/dist/cypress-svelte.cjs.js",
|
||||
"types": "./svelte/dist/index.d.ts"
|
||||
}
|
||||
},
|
||||
"workspaces": {
|
||||
|
||||
@@ -13,6 +13,7 @@ const npmModulesToCopy = [
|
||||
'vue',
|
||||
'vue2',
|
||||
'angular',
|
||||
'svelte',
|
||||
]
|
||||
|
||||
npmModulesToCopy.forEach((folder) => {
|
||||
|
||||
4
cli/types/cypress-type-helpers.d.ts
vendored
4
cli/types/cypress-type-helpers.d.ts
vendored
@@ -1,2 +1,4 @@
|
||||
// type helpers
|
||||
type Nullable<T> = T | null
|
||||
declare namespace Cypress {
|
||||
type Nullable<T> = T | null
|
||||
}
|
||||
|
||||
68
cli/types/cypress.d.ts
vendored
68
cli/types/cypress.d.ts
vendored
@@ -1,5 +1,6 @@
|
||||
/// <reference path="./cypress-npm-api.d.ts" />
|
||||
/// <reference path="./cypress-eventemitter.d.ts" />
|
||||
/// <reference path="./cypress-type-helpers.d.ts" />
|
||||
|
||||
declare namespace Cypress {
|
||||
type FileContents = string | any[] | object
|
||||
@@ -81,7 +82,7 @@ declare namespace Cypress {
|
||||
|
||||
type BrowserChannel = 'stable' | 'canary' | 'beta' | 'dev' | 'nightly' | string
|
||||
|
||||
type BrowserFamily = 'chromium' | 'firefox'
|
||||
type BrowserFamily = 'chromium' | 'firefox' | 'webkit'
|
||||
|
||||
/**
|
||||
* Describes a browser Cypress can control
|
||||
@@ -2720,6 +2721,13 @@ declare namespace Cypress {
|
||||
* @default 60000
|
||||
*/
|
||||
pageLoadTimeout: number
|
||||
/**
|
||||
* Whether Cypress will search for and replace
|
||||
* obstructive JS code in .js or .html files.
|
||||
*
|
||||
* @see https://on.cypress.io/configuration#modifyObstructiveCode
|
||||
*/
|
||||
modifyObstructiveCode: boolean
|
||||
/**
|
||||
* Time, in milliseconds, to wait for an XHR request to go out in a [cy.wait()](https://on.cypress.io/wait) command
|
||||
* @default 5000
|
||||
@@ -2867,10 +2875,20 @@ declare namespace Cypress {
|
||||
*/
|
||||
experimentalModifyObstructiveThirdPartyCode: boolean
|
||||
/**
|
||||
* Generate and save commands directly to your test suite by interacting with your app as an end user would.
|
||||
* Enables AST-based JS/HTML rewriting. This may fix issues caused by the existing regex-based JS/HTML replacement algorithm.
|
||||
* @default false
|
||||
*/
|
||||
experimentalSourceRewriting: boolean
|
||||
/**
|
||||
* Generate and save commands directly to your test suite by interacting with your app as an end user would.
|
||||
* @default false
|
||||
*/
|
||||
experimentalStudio: boolean
|
||||
/**
|
||||
* Adds support for testing in the WebKit browser engine used by Safari. See https://on.cypress.io/webkit-experiment for more information.
|
||||
* @default false
|
||||
*/
|
||||
experimentalWebKitSupport: boolean
|
||||
/**
|
||||
* Number of times to retry a failed test.
|
||||
* If a number is set, tests will retry in both runMode and openMode.
|
||||
@@ -2963,13 +2981,6 @@ declare namespace Cypress {
|
||||
* Whether Cypress was launched via 'cypress open' (interactive mode)
|
||||
*/
|
||||
isInteractive: boolean
|
||||
/**
|
||||
* Whether Cypress will search for and replace
|
||||
* obstructive JS code in .js or .html files.
|
||||
*
|
||||
* @see https://on.cypress.io/configuration#modifyObstructiveCode
|
||||
*/
|
||||
modifyObstructiveCode: boolean
|
||||
/**
|
||||
* The platform Cypress is running on.
|
||||
*/
|
||||
@@ -3048,19 +3059,32 @@ declare namespace Cypress {
|
||||
|
||||
type PickConfigOpt<T> = T extends keyof DefineDevServerConfig ? DefineDevServerConfig[T] : any
|
||||
|
||||
interface AngularDevServerProjectConfig {
|
||||
root: string,
|
||||
sourceRoot: string,
|
||||
buildOptions: Record<string, any>
|
||||
}
|
||||
|
||||
type DevServerFn<ComponentDevServerOpts = any> = (cypressDevServerConfig: DevServerConfig, devServerConfig: ComponentDevServerOpts) => ResolvedDevServerConfig | Promise<ResolvedDevServerConfig>
|
||||
|
||||
type DevServerConfigOptions = {
|
||||
bundler: 'webpack'
|
||||
framework: 'react' | 'vue' | 'vue-cli' | 'nuxt' | 'create-react-app' | 'next' | 'angular'
|
||||
framework: 'react' | 'vue' | 'vue-cli' | 'nuxt' | 'create-react-app' | 'next' | 'svelte'
|
||||
webpackConfig?: PickConfigOpt<'webpackConfig'>
|
||||
} | {
|
||||
bundler: 'vite'
|
||||
framework: 'react' | 'vue'
|
||||
framework: 'react' | 'vue' | 'svelte'
|
||||
viteConfig?: Omit<Exclude<PickConfigOpt<'viteConfig'>, undefined>, 'base' | 'root'>
|
||||
} | {
|
||||
bundler: 'webpack',
|
||||
framework: 'angular',
|
||||
webpackConfig?: PickConfigOpt<'webpackConfig'>,
|
||||
options?: {
|
||||
projectConfig: AngularDevServerProjectConfig
|
||||
}
|
||||
}
|
||||
|
||||
interface ComponentConfigOptions<ComponentDevServerOpts = any> extends Omit<CoreConfigOptions, 'baseUrl' | 'experimentalSessionAndOrigin'> {
|
||||
interface ComponentConfigOptions<ComponentDevServerOpts = any> extends Omit<CoreConfigOptions, 'baseUrl' | 'experimentalSessionAndOrigin' | 'experimentalStudio'> {
|
||||
devServer: DevServerFn<ComponentDevServerOpts> | DevServerConfigOptions
|
||||
devServerConfig?: ComponentDevServerOpts
|
||||
/**
|
||||
@@ -5784,6 +5808,26 @@ declare namespace Cypress {
|
||||
* cy.clock().invoke('restore')
|
||||
*/
|
||||
restore(): void
|
||||
/**
|
||||
* Change the time without invoking any timers.
|
||||
*
|
||||
* Default value with no argument or undefined is 0.
|
||||
*
|
||||
* This can be useful if you need to change the time by an hour
|
||||
* while there is a setInterval registered that may otherwise run thousands
|
||||
* of times.
|
||||
* @see https://on.cypress.io/clock
|
||||
* @example
|
||||
* cy.clock()
|
||||
* cy.visit('/')
|
||||
* ...
|
||||
* cy.clock().then(clock => {
|
||||
* clock.setSystemTime(60 * 60 * 1000)
|
||||
* })
|
||||
* // or use this shortcut
|
||||
* cy.clock().invoke('setSystemTime', 60 * 60 * 1000)
|
||||
*/
|
||||
setSystemTime(now?: number | Date): void
|
||||
}
|
||||
|
||||
interface Cookie {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// type tests for Cypress NPM module
|
||||
// https://on.cypress.io/module-api
|
||||
import cypress from 'cypress'
|
||||
import cypress, { defineConfig } from 'cypress'
|
||||
|
||||
cypress.run // $ExpectType (options?: Partial<CypressRunOptions> | undefined) => Promise<CypressRunResult | CypressFailedRunResult>
|
||||
cypress.open // $ExpectType (options?: Partial<CypressOpenOptions> | undefined) => Promise<void>
|
||||
@@ -51,6 +51,10 @@ cypress.run().then(results => {
|
||||
}
|
||||
})
|
||||
|
||||
const config = defineConfig({
|
||||
modifyObstructiveCode: true
|
||||
})
|
||||
|
||||
// component options
|
||||
const componentConfigNextWebpack: Cypress.ConfigOptions = {
|
||||
component: {
|
||||
|
||||
@@ -682,6 +682,24 @@ namespace CypressClockTests {
|
||||
})
|
||||
// restoring the clock shortcut
|
||||
cy.clock().invoke('restore')
|
||||
// setting system time with no argument
|
||||
cy.clock().then(clock => {
|
||||
clock.setSystemTime()
|
||||
})
|
||||
// setting system time with timestamp
|
||||
cy.clock().then(clock => {
|
||||
clock.setSystemTime(1000)
|
||||
})
|
||||
// setting system time with date object
|
||||
cy.clock().then(clock => {
|
||||
clock.setSystemTime(new Date(2019, 3, 2))
|
||||
})
|
||||
// setting system time with no argument and shortcut
|
||||
cy.clock().invoke('setSystemTime')
|
||||
// setting system time with timestamp and shortcut
|
||||
cy.clock().invoke('setSystemTime', 1000)
|
||||
// setting system time with date object and shortcut
|
||||
cy.clock().invoke('setSystemTime', new Date(2019, 3, 2))
|
||||
}
|
||||
|
||||
namespace CypressContainsTests {
|
||||
|
||||
@@ -77,7 +77,7 @@ In the following instructions, "X.Y.Z" is used to denote the [next version of Cy
|
||||
|
||||
3. If there is a new [`cypress-example-kitchensink`](https://github.com/cypress-io/cypress-example-kitchensink/releases) version, update the corresponding dependency in [`packages/example`](../packages/example) to that new version.
|
||||
|
||||
4. Once the `develop` branch is passing for all test projects with the new changes and the `linux-x64` binary is present at `https://cdn.cypress.io/beta/binary/X.Y.Z/linux-x64/<sha>/cypress.zip`, and the `linux-x64` cypress npm package is present at `https://cdn.cypress.io/beta/binary/X.Y.Z/linux-x64/<sha>/cypress.tgz`, publishing can proceed.
|
||||
4. Once the `develop` branch is passing for all test projects with the new changes and the `linux-x64` binary is present at `https://cdn.cypress.io/beta/binary/X.Y.Z/linux-x64/develop-<sha>/cypress.zip`, and the `linux-x64` cypress npm package is present at `https://cdn.cypress.io/beta/npm/X.Y.Z/linux-x64/develop-<sha>/cypress.tgz`, publishing can proceed.
|
||||
|
||||
5. Install and test the pre-release version to make sure everything is working.
|
||||
- Get the pre-release version that matches your system from the latest develop commit.
|
||||
@@ -169,7 +169,7 @@ In the following instructions, "X.Y.Z" is used to denote the [next version of Cy
|
||||
git pull origin develop
|
||||
git log --pretty=oneline
|
||||
# copy sha of the previous commit
|
||||
git tag -a vX.Y.Z <sha>
|
||||
git tag -a vX.Y.Z -m vX.Y.Z <sha>
|
||||
git push origin vX.Y.Z
|
||||
```
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Testing other projects
|
||||
|
||||
In `develop`, `master`, and any other branch configured in [`circle.yml`](../circle.yml), the Cypress binary and npm package are built and uploaded to `cdn.cypress.io`. Then, tests are run, using a variety of real-world example repositories.
|
||||
In `develop`, `master`, and any other branch configured in [the CircleCI config](../.circleci/config.yml), the Cypress binary and npm package are built and uploaded to `cdn.cypress.io`. Then, tests are run, using a variety of real-world example repositories.
|
||||
|
||||
Two main strategies are used to spawn these test projects:
|
||||
|
||||
@@ -9,7 +9,7 @@ Two main strategies are used to spawn these test projects:
|
||||
|
||||
## `test-binary-against-repo` jobs
|
||||
|
||||
A number of CI jobs in `circle.yml` clone test projects and run tests as part of `cypress-io/cypress`'s CI pipeline.
|
||||
A number of CI jobs in `.circleci/config.yml` clone test projects and run tests as part of `cypress-io/cypress`'s CI pipeline.
|
||||
|
||||
You can find a list of test projects that do this by searching for usage of the `test-binary-against-repo` step.
|
||||
|
||||
@@ -19,4 +19,4 @@ One advantage to local CI is that it does not require creating commits to anothe
|
||||
|
||||
## `binary-system-tests`
|
||||
|
||||
System tests in `/system-tests/test-binary` are run against the built Cypress App in CI. For more details, see the [README](../system-tests/README.md).
|
||||
System tests in `/system-tests/test-binary` are run against the built Cypress App in CI. For more details, see the [README](../system-tests/README.md).
|
||||
|
||||
@@ -1,3 +1,46 @@
|
||||
# [@cypress/angular-v1.1.0](https://github.com/cypress-io/cypress/compare/@cypress/angular-v1.0.0...@cypress/angular-v1.1.0) (2022-08-30)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* angular 14.2 mount compilation error ([#23593](https://github.com/cypress-io/cypress/issues/23593)) ([2f337db](https://github.com/cypress-io/cypress/commit/2f337dbfa2bb212754c8fa82e3f4548a2f3a07a4))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* adding svelte component testing support ([#23553](https://github.com/cypress-io/cypress/issues/23553)) ([f6eaad4](https://github.com/cypress-io/cypress/commit/f6eaad40e1836fa9db87c60defa5ae6f390c8fd8))
|
||||
|
||||
# @cypress/angular-v1.0.0 (2022-08-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **angular:** set rxjs versions > 6.6.0 as dependency ([#16676](https://github.com/cypress-io/cypress/issues/16676)) ([46de81e](https://github.com/cypress-io/cypress/commit/46de81e75fd18bc37cb884e9a751106fff4d08ad))
|
||||
* remove dependency causing semantic-release to fail ([#23142](https://github.com/cypress-io/cypress/issues/23142)) ([20f89bf](https://github.com/cypress-io/cypress/commit/20f89bfa32636baa8922896e719962c703129abd))
|
||||
* scaffold correct config file ([#19776](https://github.com/cypress-io/cypress/issues/19776)) ([8f32960](https://github.com/cypress-io/cypress/commit/8f32960ef803f539f065d41f01fff33bfe33ed5d))
|
||||
* scope config to current testing type ([#20677](https://github.com/cypress-io/cypress/issues/20677)) ([61f7cfc](https://github.com/cypress-io/cypress/commit/61f7cfc59284a2938e0a1c15d74ee75215ba5f8b))
|
||||
* terminal error message for non migrated config ([#21467](https://github.com/cypress-io/cypress/issues/21467)) ([3274da7](https://github.com/cypress-io/cypress/commit/3274da7842f5ef1ddad62b1c630d0ff9120e4289))
|
||||
* update scaffold template to use correct path ([#20047](https://github.com/cypress-io/cypress/issues/20047)) ([6e80359](https://github.com/cypress-io/cypress/commit/6e803597a379222cf936e5977c8314d693ee1912))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add devServer to config file ([#18962](https://github.com/cypress-io/cypress/issues/18962)) ([2573375](https://github.com/cypress-io/cypress/commit/2573375b5b6616efd2d213a94cd55fd8e0385864))
|
||||
* add template support, teardown & standalone ([#23117](https://github.com/cypress-io/cypress/issues/23117)) ([d201b37](https://github.com/cypress-io/cypress/commit/d201b37b3d6b1e37a15a8d21d853acca47bfc666))
|
||||
* **angular:** angular mount ([#22858](https://github.com/cypress-io/cypress/issues/22858)) ([4131b1f](https://github.com/cypress-io/cypress/commit/4131b1fa8482ae08113bef337965baa1ac12f66c))
|
||||
* Deprecate run-ct / open-ct, and update all examples to use --ct instead ([#18422](https://github.com/cypress-io/cypress/issues/18422)) ([196e8f6](https://github.com/cypress-io/cypress/commit/196e8f62cc6d27974f235945cb5700624b3dae41))
|
||||
* enable Angular CT support ([#23089](https://github.com/cypress-io/cypress/issues/23089)) ([94e78eb](https://github.com/cypress-io/cypress/commit/94e78eba0430eae97529058c40611e5f24dbf140))
|
||||
* ProjectLifecycleManager & general launchpad cleanup ([#19347](https://github.com/cypress-io/cypress/issues/19347)) ([4626f74](https://github.com/cypress-io/cypress/commit/4626f7481c9904fec484aa167a02e0197a3095c4))
|
||||
* remove testFiles reference ([#20565](https://github.com/cypress-io/cypress/issues/20565)) ([5670344](https://github.com/cypress-io/cypress/commit/567034459089d9d53dfab5556cb9369fb335c3db))
|
||||
* support specPattern, deprecate integrationFolder and componentFolder ([#19319](https://github.com/cypress-io/cypress/issues/19319)) ([792980a](https://github.com/cypress-io/cypress/commit/792980ac12746ef47b9c944ebe4c6c353a187ab2))
|
||||
* support webpack-dev-server v4 ([#17918](https://github.com/cypress-io/cypress/issues/17918)) ([16e4759](https://github.com/cypress-io/cypress/commit/16e4759e0196f68c5f0525efb020211337748f94))
|
||||
* swap the #__cy_root id selector to become data-cy-root for component mounting ([#20951](https://github.com/cypress-io/cypress/issues/20951)) ([0e7b555](https://github.com/cypress-io/cypress/commit/0e7b555f93fb403f431c5de4a07ae7ad6ac89ba2))
|
||||
* Use .config files ([#18578](https://github.com/cypress-io/cypress/issues/18578)) ([081dd19](https://github.com/cypress-io/cypress/commit/081dd19cc6da3da229a7af9c84f62730c85a5cd6))
|
||||
* use devServer instad of startDevServer ([#20092](https://github.com/cypress-io/cypress/issues/20092)) ([8a6768f](https://github.com/cypress-io/cypress/commit/8a6768fee6f46b908c5a9daf23da8b804a6c627f))
|
||||
* use hoisted yarn install in binary build ([#17285](https://github.com/cypress-io/cypress/issues/17285)) ([e4f5b10](https://github.com/cypress-io/cypress/commit/e4f5b106d49d6ac0857c5fdac886f83b99558c88))
|
||||
* Use plugins on config files ([#18798](https://github.com/cypress-io/cypress/issues/18798)) ([bb8251b](https://github.com/cypress-io/cypress/commit/bb8251b752ac44f1184f9160194cf12d41fc867f))
|
||||
* use supportFile by testingType ([#19364](https://github.com/cypress-io/cypress/issues/19364)) ([0366d4f](https://github.com/cypress-io/cypress/commit/0366d4fa8971e5e5189c6fd6450cc3c8d72dcfe1))
|
||||
|
||||
# @cypress/angular-v1.0.0 (2022-08-04)
|
||||
|
||||
|
||||
|
||||
@@ -1,57 +1,37 @@
|
||||
> A little helper to unit test React components in the open source [Cypress.io](https://www.cypress.io/) test runner **v7.0.0+**
|
||||
# @cypress/angular
|
||||
|
||||
**Jump to:** [Comparison](#comparison), [Blog posts](#blog-posts), [Install](#install), Examples: [basic](#basic-examples), [advanced](#advanced-examples), [full](#full-examples), [external](#external-examples), [Style options](#options), [Code coverage](#code-coverage), [Visual testing](#visual-testing), [Common problems](#common-problems), [Chat](#chat)
|
||||
Mount Angular components in the open source [Cypress.io](https://www.cypress.io/) test runner **v7.0.0+**
|
||||
|
||||
## TLDR
|
||||
|
||||
- What is this? This package allows you to use [Cypress](https://www.cypress.io/) test runner to unit test your Angular components with zero effort. Here is a typical component testing, notice there is not external URL shown, since it is mounting the component directly.
|
||||
|
||||

|
||||
|
||||
- How is this different from [Angular Testing](https://angular.io/guide/testing) or [ATL](https://testing-library.com/docs/angular-testing-library/intro/)? It is similar in functionality BUT runs the component in the real browser with full power of Cypress E2E test runner: [live GUI, full API, screen recording, CI support, cross-platform](https://www.cypress.io/features/), and [visual testing](https://on.cypress.io/visual-testing).
|
||||
- Read [My Vision for Component Tests in Cypress](https://glebbahmutov.com/blog/my-vision-for-component-tests/) by Gleb Bahmutov
|
||||
|
||||
## Comparison
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
Feature | Jest / Karma / ATL | Cypress + `@cypress/angular`
|
||||
--- | --- | ---
|
||||
Test runs in real browser | ❌ | ✅
|
||||
Supports shallow mount | ✅ | ❌
|
||||
Supports full mount | ✅ | ✅
|
||||
Test speed | 🏎 | [as fast as the app works in the browser](#fast-enough)
|
||||
Test can use additional plugins | maybe | use any [Cypress plugin](https://on.cypress.io/plugins)
|
||||
Test can interact with component | synthetic limited API | use any [Cypress command](https://on.cypress.io/api)
|
||||
Test can be debugged | via terminal and Node debugger | use browser DevTools
|
||||
Built-in time traveling debugger | ❌ | Cypress time traveling debugger
|
||||
Re-run tests on file or test change | ✅ | ✅
|
||||
Test output on CI | terminal | terminal, screenshots, videos
|
||||
Tests can be run in parallel | ✅ | ✅ via [parallelization](https://on.cypress.io/parallelization)
|
||||
Test against interface | if using `@testing-library/angular` | ✅ and can use `@testing-library/cypress`
|
||||
Spying and stubbing methods | Jest mocks | [Sinon library](https://on.cypress.io/stubs-spies-and-clocks)
|
||||
Stubbing imports | ✅ | ✅
|
||||
Stubbing clock | ✅ | ✅
|
||||
Code coverage | ✅ | ✅
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
If you are coming from Jest + ATL world, read [Test The Interface Not The Implementation](https://glebbahmutov.com/blog/test-the-interface/).
|
||||
|
||||
## Blog posts
|
||||
|
||||
- [My Vision for Component Tests in Cypress](https://glebbahmutov.com/blog/my-vision-for-component-tests/)
|
||||
> **Note:** This package is bundled with the `cypress` package and should not need to be installed separately. See the [Angular Component Testing Docs](https://docs.cypress.io/guides/component-testing/quickstart-angular#Configuring-Component-Testing) for mounting Angular components. Installing and importing `mount` from `@cypress/angular` should only be used for advanced use-cases.
|
||||
|
||||
## Install
|
||||
|
||||
Requires [Node](https://nodejs.org/en/) version 12 or above.
|
||||
- Requires Cypress v7.0.0 or later
|
||||
- Requires [Node](https://nodejs.org/en/) version 12 or above
|
||||
|
||||
```sh
|
||||
npm install --save-dev cypress @cypress/angular @cypress/webpack-dev-server
|
||||
npm install --save-dev @cypress/angular
|
||||
```
|
||||
|
||||
## Run
|
||||
|
||||
Open cypress test runner
|
||||
```
|
||||
npx cypress open --component
|
||||
```
|
||||
|
||||
If you need to run test in CI
|
||||
```
|
||||
npx cypress run --component
|
||||
```
|
||||
|
||||
For more information, please check the official docs for [running Cypress](https://on.cypress.io/guides/getting-started/opening-the-app#Quick-Configuration) and for [component testing](https://on.cypress.io/guides/component-testing/writing-your-first-component-test).
|
||||
|
||||
## API
|
||||
|
||||
- `mount` allows you to mount a given Angular component as a mini web application and interact with it using Cypress commands
|
||||
- `mount` is the most important function, allows to mount a given Angular component as a mini web application and interact with it using Cypress commands
|
||||
- `MountConfig` Configuration used to configure your test
|
||||
- `createOutputSpy` factory function that creates new EventEmitter for your component and spies on it's `emit` method.
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -68,87 +48,38 @@ describe('HelloWorldComponent', () => {
|
||||
})
|
||||
```
|
||||
|
||||
Look at the examples in [cypress/component](cypress/component) folder. Here is the list of examples showing various testing scenarios.
|
||||
```ts
|
||||
import { mount } from '@cypress/angular'
|
||||
import { HelloWorldComponent } from './hello-world.component'
|
||||
|
||||
### Basic examples
|
||||
Coming Soon...
|
||||
|
||||
|
||||
### Advanced examples
|
||||
Coming Soon...
|
||||
|
||||
### Full examples
|
||||
Coming Soon...
|
||||
|
||||
### External examples
|
||||
Coming Soon...
|
||||
|
||||
## Options
|
||||
|
||||
|
||||
## Code coverage
|
||||
|
||||
In order to use code coverage you can follow the instructions from [docs](https://github.com/cypress-io/code-coverage). In most of cases you need to install 2 dependencies:
|
||||
|
||||
```
|
||||
npm i @cypress/code-coverage babel-plugin-istanbul
|
||||
|
||||
yarn add @cypress/code-coverage babel-plugin-istanbul
|
||||
describe('HelloWorldComponent', () => {
|
||||
it('works', () => {
|
||||
mount('<app-hello-world></app-hello-world>', {
|
||||
declarations: [HelloWorldComponent]
|
||||
})
|
||||
// now use standard Cypress commands
|
||||
cy.contains('Hello World!').should('be.visible')
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
If you are using [plugins/cra-v3](plugins/cra-v3) it instruments the code on the fly using `babel-plugin-istanbul` and generates report using dependency [cypress-io/code-coverage](https://github.com/cypress-io/code-coverage) (included). If you want to disable code coverage instrumentation and reporting, use `--env coverage=false` or `CYPRESS_coverage=false` or set in your `cypress.json` file
|
||||
|
||||
```json
|
||||
{
|
||||
"env": {
|
||||
"coverage": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Visual testing
|
||||
|
||||
You can use any Cypress [Visual Testing plugin](https://on.cypress.io/plugins#visual-testing) to perform [visual testing](https://on.cypress.io/visual-testing) from the component tests. This repo has several example projects, see [visual-sudoku](examples/visual-sudoku), [visual-testing-with-percy](examples/visual-testing-with-percy), [visual-testing-with-happo](examples/visual-testing-with-happo), and [visual-testing-with-applitools](examples/visual-testing-with-applitools).
|
||||
|
||||
For a larger Do-It-Yourself example with an hour long list of explanation videos, see [bahmutov/sudoku](https://github.com/bahmutov/sudoku) repository. I explain how to write visual testing using open source tools in this [blog post](https://glebbahmutov.com/blog/open-source-visual-testing-of-components/), [video talk](https://www.youtube.com/watch?v=00BNExlJUU8), and [slides](https://slides.com/bahmutov/i-see-what-is-going-on).
|
||||
|
||||
## Common problems
|
||||
Look at the examples in [cypress-component-testing-apps](https://github.com/cypress-io/cypress-component-testing-apps) repo. Here in the `angular` and `angular-standalone` folders are the two example applications showing various testing scenarios.
|
||||
|
||||
|
||||
## Chat
|
||||
## Compatibility
|
||||
|
||||
Come chat with us [on discord](https://discord.gg/7ZHYhZSW) in the #component-testing channel.
|
||||
| @cypress/angular | cypress |
|
||||
| -------------- | ------- |
|
||||
| >= v1 | >= v10.5 |
|
||||
|
||||
## Development
|
||||
|
||||
See [docs/development.md](./docs/development.md)
|
||||
Run `yarn build` to compile and sync packages to the `cypress` cli package.
|
||||
|
||||
## Debugging
|
||||
## License
|
||||
|
||||
You can see verbose logs from this plugin by running with environment variable
|
||||
[](https://github.com/cypress-io/cypress/blob/master/LICENSE)
|
||||
|
||||
```
|
||||
DEBUG=@cypress/angular
|
||||
```
|
||||
This project is licensed under the terms of the [MIT license](/LICENSE).
|
||||
|
||||
Because finding and modifying Webpack settings while running this plugin is done by [find-webpack](https://github.com/bahmutov/find-webpack) module, you might want to enable its debug messages too.
|
||||
|
||||
```
|
||||
DEBUG=@cypress/angular,find-webpack
|
||||
```
|
||||
|
||||
## Changelog
|
||||
|
||||
[Changelog](./CHANGELOG.md)
|
||||
|
||||
## Related tools
|
||||
|
||||
Same feature for unit testing components from other frameworks using Cypress
|
||||
|
||||
- [@cypress/react](https://github.com/cypress-io/cypress/tree/develop/npm/react)
|
||||
- [@cypress/vue](https://github.com/cypress-io/cypress/tree/develop/npm/vue)
|
||||
- [cypress-cycle-unit-test](https://github.com/bahmutov/cypress-cycle-unit-test)
|
||||
- [cypress-svelte-unit-test](https://github.com/bahmutov/cypress-svelte-unit-test)
|
||||
- [@cypress/angular](https://github.com/bahmutov/@cypress/angular)
|
||||
- [cypress-hyperapp-unit-test](https://github.com/bahmutov/cypress-hyperapp-unit-test)
|
||||
- [cypress-angularjs-unit-test](https://github.com/bahmutov/cypress-angularjs-unit-test)
|
||||
## [Changelog](./CHANGELOG.md)
|
||||
|
||||
@@ -2,23 +2,21 @@
|
||||
"name": "@cypress/angular",
|
||||
"version": "0.0.0-development",
|
||||
"description": "Test Angular Components using Cypress",
|
||||
"private": true,
|
||||
"main": "dist/index.js",
|
||||
"scripts": {
|
||||
"prebuild": "rimraf dist",
|
||||
"build": "rollup -c rollup.config.js",
|
||||
"build": "rollup -c rollup.config.mjs",
|
||||
"postbuild": "node ../../scripts/sync-exported-npm-with-cli.js",
|
||||
"build-prod": "yarn build",
|
||||
"check-ts": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"@angular/common": "^14.0.6",
|
||||
"@angular/core": "^14.0.6",
|
||||
"@angular/platform-browser-dynamic": "^14.0.6",
|
||||
"@rollup/plugin-node-resolve": "^11.1.1",
|
||||
"rollup-plugin-typescript2": "^0.29.0",
|
||||
"typescript": "~4.2.3",
|
||||
"@angular/common": "^14.2.0",
|
||||
"@angular/core": "^14.2.0",
|
||||
"@angular/platform-browser-dynamic": "^14.2.0",
|
||||
"@cypress/mount-utils": "0.0.0-development",
|
||||
"typescript": "^4.7.4",
|
||||
"zone.js": "~0.11.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
import ts from 'rollup-plugin-typescript2'
|
||||
import resolve from '@rollup/plugin-node-resolve'
|
||||
|
||||
import pkg from './package.json'
|
||||
|
||||
const banner = `
|
||||
/**
|
||||
* ${pkg.name} v${pkg.version}
|
||||
* (c) ${new Date().getFullYear()} Cypress.io
|
||||
* Released under the MIT License
|
||||
*/
|
||||
`
|
||||
|
||||
function createEntry () {
|
||||
const input = 'src/index.ts'
|
||||
const format = 'es'
|
||||
|
||||
const config = {
|
||||
input,
|
||||
external: [
|
||||
'@angular/core',
|
||||
'@angular/core/testing',
|
||||
'@angular/common',
|
||||
'@angular/platform-browser-dynamic/testing',
|
||||
'zone.js',
|
||||
'zone.js/testing',
|
||||
],
|
||||
plugins: [
|
||||
resolve(),
|
||||
],
|
||||
output: {
|
||||
banner,
|
||||
name: 'CypressAngular',
|
||||
file: pkg.module,
|
||||
format,
|
||||
exports: 'auto',
|
||||
},
|
||||
}
|
||||
|
||||
console.log(`Building ${format}: ${config.output.file}`)
|
||||
|
||||
config.plugins.push(
|
||||
ts({
|
||||
check: true,
|
||||
tsconfigOverride: {
|
||||
compilerOptions: {
|
||||
declaration: true,
|
||||
target: 'es6', // not sure what this should be?
|
||||
module: 'esnext',
|
||||
},
|
||||
exclude: [],
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
export default [
|
||||
createEntry(),
|
||||
]
|
||||
14
npm/angular/rollup.config.mjs
Normal file
14
npm/angular/rollup.config.mjs
Normal file
@@ -0,0 +1,14 @@
|
||||
import { createEntries } from '@cypress/mount-utils/create-rollup-entry.mjs'
|
||||
|
||||
const config = {
|
||||
external: [
|
||||
'@angular/core',
|
||||
'@angular/core/testing',
|
||||
'@angular/common',
|
||||
'@angular/platform-browser-dynamic/testing',
|
||||
'zone.js',
|
||||
'zone.js/testing',
|
||||
],
|
||||
}
|
||||
|
||||
export default createEntries({ formats: ['es'], input: 'src/index.ts', config })
|
||||
@@ -12,8 +12,8 @@ import { Component, EventEmitter, Type } from '@angular/core'
|
||||
import {
|
||||
ComponentFixture,
|
||||
getTestBed,
|
||||
TestBed,
|
||||
TestModuleMetadata,
|
||||
TestBed,
|
||||
} from '@angular/core/testing'
|
||||
import {
|
||||
BrowserDynamicTestingModule,
|
||||
@@ -144,12 +144,12 @@ function initTestBed<T> (
|
||||
|
||||
const componentFixture = createComponentFixture(component) as Type<T>
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
getTestBed().configureTestingModule({
|
||||
...bootstrapModule(componentFixture, configRest),
|
||||
})
|
||||
|
||||
if (providers != null) {
|
||||
TestBed.overrideComponent(componentFixture, {
|
||||
getTestBed().overrideComponent(componentFixture, {
|
||||
add: {
|
||||
providers,
|
||||
},
|
||||
@@ -172,6 +172,8 @@ function createComponentFixture<T> (
|
||||
component: Type<T> | string,
|
||||
): Type<T | WrapperComponent> {
|
||||
if (typeof component === 'string') {
|
||||
// getTestBed().overrideTemplate is available in v14+
|
||||
// The static TestBed.overrideTemplate is available across versions
|
||||
TestBed.overrideTemplate(WrapperComponent, component)
|
||||
|
||||
return WrapperComponent
|
||||
@@ -192,7 +194,7 @@ function setupFixture<T> (
|
||||
component: Type<T>,
|
||||
config: MountConfig<T>,
|
||||
): ComponentFixture<T> {
|
||||
const fixture = TestBed.createComponent(component)
|
||||
const fixture = getTestBed().createComponent(component)
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.autoDetectChanges(config.autoDetectChanges ?? true)
|
||||
@@ -309,6 +311,6 @@ getTestBed().initTestEnvironment(
|
||||
|
||||
setupHooks(() => {
|
||||
// Not public, we need to call this to remove the last component from the DOM
|
||||
TestBed['tearDownTestingModule']()
|
||||
TestBed.resetTestingModule()
|
||||
getTestBed()['tearDownTestingModule']()
|
||||
getTestBed().resetTestingModule()
|
||||
})
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
"mock-fs": "5.1.1",
|
||||
"shx": "0.3.3",
|
||||
"snap-shot-it": "7.9.3",
|
||||
"typescript": "^4.2.3"
|
||||
"typescript": "^4.7.4"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
# Ignores TypeScript files, but keeps definitions.
|
||||
*.ts
|
||||
!*.d.ts
|
||||
|
||||
*.js.map
|
||||
!src/**/__files__/**/*.js
|
||||
!src/**/files/**/*.ts
|
||||
*.vscode
|
||||
sandbox
|
||||
@@ -1,3 +1,29 @@
|
||||
# [@cypress/schematic-v2.1.1](https://github.com/cypress-io/cypress/compare/@cypress/schematic-v2.1.0...@cypress/schematic-v2.1.1) (2022-08-31)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **cypress-schematic:** suffix template files so they are not ignored ([#23645](https://github.com/cypress-io/cypress/issues/23645)) ([3fd56bc](https://github.com/cypress-io/cypress/commit/3fd56bc1f21224150569434e94b64e781b22008d))
|
||||
|
||||
# [@cypress/schematic-v2.1.0](https://github.com/cypress-io/cypress/compare/@cypress/schematic-v2.0.3...@cypress/schematic-v2.1.0) (2022-08-30)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* angular 14.2 mount compilation error ([#23593](https://github.com/cypress-io/cypress/issues/23593)) ([2f337db](https://github.com/cypress-io/cypress/commit/2f337dbfa2bb212754c8fa82e3f4548a2f3a07a4))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **cypress/schematic:** add support for component testing ([#23385](https://github.com/cypress-io/cypress/issues/23385)) ([99562af](https://github.com/cypress-io/cypress/commit/99562af65a10abb0fab211fd97b13f98e2b0f959))
|
||||
|
||||
# [@cypress/schematic-v2.0.3](https://github.com/cypress-io/cypress/compare/@cypress/schematic-v2.0.2...@cypress/schematic-v2.0.3) (2022-08-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* cypress-schematic add exception for nguniversal ssr dev server ([#23348](https://github.com/cypress-io/cypress/issues/23348)) ([1f05ff0](https://github.com/cypress-io/cypress/commit/1f05ff0fd75db2d02a777bd497b30179b4b407f5))
|
||||
|
||||
# [@cypress/schematic-v2.0.2](https://github.com/cypress-io/cypress/compare/@cypress/schematic-v2.0.1...@cypress/schematic-v2.0.2) (2022-08-10)
|
||||
|
||||
|
||||
|
||||
@@ -19,26 +19,36 @@
|
||||
|
||||
✅ Install Cypress
|
||||
|
||||
✅ Add npm scripts for running Cypress in `run` mode and `open` mode
|
||||
✅ Add npm scripts for running Cypress e2e tests in `run` mode and `open` mode
|
||||
|
||||
✅ Scaffold base Cypress files and directories
|
||||
|
||||
✅ Provide the ability to add new e2e files easily using `ng-generate`
|
||||
✅ Provide the ability to add new e2e and component specs easily using `ng-generate`
|
||||
|
||||
✅ Optional: prompt you to add or update the default `ng e2e` command to use Cypress.
|
||||
✅ Optional: prompt you to add or update the default `ng e2e` command to use Cypress for e2e tests.
|
||||
|
||||
✅ Optional: prompt you to add a `ng ct` command to use Cypress component testing.
|
||||
|
||||
## Requirements
|
||||
|
||||
- Angular 12+
|
||||
- Angular 13+
|
||||
|
||||
## Usage ⏯
|
||||
|
||||
Install the schematic:
|
||||
### Adding E2E and Component Testing
|
||||
|
||||
To install the schematic via prompts:
|
||||
|
||||
```shell
|
||||
ng add @cypress/schematic
|
||||
```
|
||||
|
||||
To install the schematic via cli arguments (installs both e2e and component testing):
|
||||
|
||||
```shell
|
||||
ng add @cypress/schematic --e2e --component
|
||||
```
|
||||
|
||||
To run Cypress in `open` mode within your project:
|
||||
|
||||
```shell script
|
||||
@@ -57,11 +67,49 @@ If you have chosen to add or update the `ng e2e` command, you can also run Cypre
|
||||
ng e2e
|
||||
```
|
||||
|
||||
To generate new e2e spec files:
|
||||
If you have chosen to add Cypress component testing, you can run component tests in `open` mode using this:
|
||||
|
||||
```shell script
|
||||
ng generate @cypress/schematic:e2e
|
||||
```
|
||||
ng run {project-name}:ct
|
||||
```
|
||||
|
||||
### Generating New Cypress Spec Files
|
||||
|
||||
To generate a new e2e spec file:
|
||||
|
||||
```shell script
|
||||
ng generate @cypress/schematic:spec
|
||||
```
|
||||
|
||||
or (without cli prompt)
|
||||
|
||||
```shell script
|
||||
ng generate @cypress/schematic:spec {name}
|
||||
```
|
||||
|
||||
To generate a new component spec file:
|
||||
|
||||
```shell script
|
||||
ng generate @cypress/schematic:spec --component
|
||||
```
|
||||
|
||||
or (without cli prompt)
|
||||
|
||||
```shell script
|
||||
ng generate @cypress/schematic:spec {component name} --component
|
||||
```
|
||||
|
||||
To generate a new component spec file in a specific folder:
|
||||
|
||||
```shell script
|
||||
ng generate @cypress/schematic:spec {component name} --component --path {path relative to project root}
|
||||
```
|
||||
|
||||
To generate new component spec files alongside all component files in a project:
|
||||
|
||||
```shell script
|
||||
ng generate @cypress/schematic:specs-ct
|
||||
```
|
||||
|
||||
## Builder Options 🛠
|
||||
|
||||
@@ -109,7 +157,7 @@ We recommend setting your [Cypress Dashboard](https://on.cypress.io/features-das
|
||||
|
||||
Read our docs to learn more about [recording test results](https://on.cypress.io/recording-project-runs) to the [Cypress Dashboard](https://on.cypress.io/features-dashboard).
|
||||
|
||||
### Specifying a custom `cypress.json` config file
|
||||
### Specifying a custom config file
|
||||
|
||||
It may be useful to have different Cypress configuration files per environment (ie. development, staging, production).
|
||||
|
||||
@@ -223,12 +271,22 @@ In order to prevent the application from building, add the following to the end
|
||||
|
||||
## Generator Options
|
||||
|
||||
### Specify Filename (bypassing CLI prompt)
|
||||
### Specify Testing Type
|
||||
|
||||
In order to bypass the prompt asking for your e2e spec name, simply add a `--name=` flag like this:
|
||||
The default generated spec is E2E. In order to generate a component test you can run:
|
||||
|
||||
```shell script
|
||||
ng generate @cypress/schematic:e2e --name=login
|
||||
ng generate @cypress/schematic:spec --name=button -t component
|
||||
```
|
||||
|
||||
`-t` is an alias for `testing-type`. It accepts `e2e` or `component` as arguments. If you are using the CLI tool, a prompt will appear asking which spec type you want to generate.
|
||||
|
||||
### Specify Filename (bypassing CLI prompt)
|
||||
|
||||
In order to bypass the prompt asking for your spec name add a `--name=` flag like this:
|
||||
|
||||
```shell script
|
||||
ng generate @cypress/schematic:spec --name=login
|
||||
```
|
||||
|
||||
This will create a new spec file named `login.cy.ts` in the default Cypress folder location.
|
||||
@@ -238,17 +296,33 @@ This will create a new spec file named `login.cy.ts` in the default Cypress fold
|
||||
Add a `--project=` flag to specify the project:
|
||||
|
||||
```shell script
|
||||
ng generate @cypress/schematic:e2e --name=login --project=sandbox
|
||||
ng generate @cypress/schematic:spec --name=login --project=sandbox
|
||||
```
|
||||
### Specify Path
|
||||
|
||||
Add a `--path=` flag to specify the project:
|
||||
|
||||
```shell script
|
||||
ng generate @cypress/schematic:e2e --name=login --path=src/app/tests
|
||||
ng generate @cypress/schematic:spec --name=login --path=src/app/tests
|
||||
```
|
||||
|
||||
This will create the e2e spec file in your specific location, creating folders as needed.
|
||||
This will create a spec file in your specific location, creating folders as needed. By default, new specs are created in either `cypress/e2e` for E2E specs or `cypress/ct` for component specs.
|
||||
|
||||
### Generate Tests for All Components
|
||||
|
||||
You can scaffold component test specs alongside all your components in the default project by using:
|
||||
|
||||
```shell script
|
||||
ng generate @cypress/schematic:specs-ct -g
|
||||
```
|
||||
|
||||
This will identify files ending in `component.ts`. It will then create spec files alongside them - if they don't exist.
|
||||
|
||||
If you would like to specify a project, you can use the command:
|
||||
|
||||
```shell script
|
||||
ng generate @cypress/schematic:specs-ct -g -p {project-name}
|
||||
```
|
||||
|
||||
## Migrating from Protractor to Cypress?
|
||||
|
||||
|
||||
@@ -9,26 +9,26 @@
|
||||
"test": "mocha -r @packages/ts/register --reporter mocha-multi-reporters --reporter-options configFile=../../mocha-reporter-config.json src/**/*.spec.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@angular-devkit/architect": "^0.1401.0",
|
||||
"@angular-devkit/core": "^14.1.0",
|
||||
"@angular-devkit/schematics": "^14.1.0",
|
||||
"@schematics/angular": "^14.1.0",
|
||||
"@angular-devkit/architect": "^0.1402.1",
|
||||
"@angular-devkit/core": "^14.2.1",
|
||||
"@angular-devkit/schematics": "^14.2.1",
|
||||
"@schematics/angular": "^14.2.1",
|
||||
"jsonc-parser": "^3.0.0",
|
||||
"rxjs": "~6.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/schematics-cli": "^14.1.0",
|
||||
"@angular/cli": "^14.1.0",
|
||||
"@angular-devkit/schematics-cli": "^14.2.1",
|
||||
"@angular/cli": "^14.2.1",
|
||||
"@types/chai-enzyme": "0.6.7",
|
||||
"@types/mocha": "8.0.3",
|
||||
"@types/node": "^18.0.6",
|
||||
"chai": "4.2.0",
|
||||
"mocha": "3.5.3",
|
||||
"typescript": "~4.2.3"
|
||||
"typescript": "^4.7.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/cli": ">=14.1.0",
|
||||
"@angular/core": ">=14.1.0"
|
||||
"@angular/cli": ">=12",
|
||||
"@angular/core": ">=12"
|
||||
},
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
|
||||
@@ -6,6 +6,7 @@ export interface CypressBuilderOptions extends JsonObject {
|
||||
browser: 'electron' | 'chrome' | 'chromium' | 'canary' | 'firefox' | 'edge' | string
|
||||
devServerTarget: string
|
||||
e2e: boolean
|
||||
component: boolean
|
||||
env: Record<string, string>
|
||||
quiet: boolean
|
||||
exit: boolean
|
||||
@@ -20,4 +21,5 @@ export interface CypressBuilderOptions extends JsonObject {
|
||||
spec: string
|
||||
tsConfig: string
|
||||
watch: boolean
|
||||
testingType: 'e2e' | 'component'
|
||||
}
|
||||
|
||||
@@ -16,10 +16,10 @@ import { CypressBuilderOptions } from './cypressBuilderOptions'
|
||||
type CypressOptions = Partial<CypressCommandLine.CypressRunOptions> &
|
||||
Partial<CypressCommandLine.CypressOpenOptions>;
|
||||
|
||||
type StartDevServerProps = {
|
||||
type CypressStartDevServerProps = {
|
||||
devServerTarget: string
|
||||
watch: boolean
|
||||
context: any
|
||||
context: BuilderContext
|
||||
}
|
||||
|
||||
function runCypress (
|
||||
@@ -75,6 +75,10 @@ function initCypress (userOptions: CypressBuilderOptions): Observable<BuilderOut
|
||||
spec: '',
|
||||
}
|
||||
|
||||
if (userOptions.component || userOptions.testingType === 'component') {
|
||||
userOptions.e2e = false
|
||||
}
|
||||
|
||||
const options: CypressOptions = {
|
||||
...defaultOptions,
|
||||
...userOptions,
|
||||
@@ -98,20 +102,33 @@ function initCypress (userOptions: CypressBuilderOptions): Observable<BuilderOut
|
||||
export function startDevServer ({
|
||||
devServerTarget,
|
||||
watch,
|
||||
context }: StartDevServerProps): Observable<string> {
|
||||
const overrides = {
|
||||
watch,
|
||||
}
|
||||
context }: CypressStartDevServerProps): Observable<string> {
|
||||
const buildTarget = targetFromTargetString(devServerTarget)
|
||||
|
||||
//@ts-ignore
|
||||
return scheduleTargetAndForget(context, targetFromTargetString(devServerTarget), overrides).pipe(
|
||||
//@ts-ignore
|
||||
map((output: any) => {
|
||||
if (!output.success && !watch) {
|
||||
throw new Error('Could not compile application files')
|
||||
return from(context.getBuilderNameForTarget(buildTarget)).pipe(
|
||||
switchMap((builderName) => {
|
||||
let overrides = {}
|
||||
|
||||
// @NOTE: Do not forward watch option if not supported by the target dev server,
|
||||
// this is relevant for running Cypress against dev server target that does not support this option,
|
||||
// for instance @nguniversal/builders:ssr-dev-server.
|
||||
// see https://github.com/nrwl/nx/blob/f930117ed6ab13dccc40725c7e9551be081cc83d/packages/cypress/src/executors/cypress/cypress.impl.ts
|
||||
if (builderName !== '@nguniversal/builders:ssr-dev-server') {
|
||||
console.info(`Passing watch mode to DevServer - watch mode is ${watch}`)
|
||||
overrides = {
|
||||
watch,
|
||||
}
|
||||
}
|
||||
|
||||
return output.baseUrl as string
|
||||
return scheduleTargetAndForget(context, targetFromTargetString(devServerTarget), overrides).pipe(
|
||||
map((output: any) => {
|
||||
if (!output.success && !watch) {
|
||||
throw new Error('Could not compile application files')
|
||||
}
|
||||
|
||||
return output.baseUrl as string
|
||||
}),
|
||||
)
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -39,6 +39,11 @@
|
||||
"description": "Run end to end tests",
|
||||
"default": true
|
||||
},
|
||||
"component": {
|
||||
"type": "boolean",
|
||||
"description": "Run component tests",
|
||||
"default": false
|
||||
},
|
||||
"env": {
|
||||
"type": "object",
|
||||
"description": "A key-value pair of environment variables to pass to Cypress runner"
|
||||
@@ -87,6 +92,13 @@
|
||||
"type": "boolean",
|
||||
"description": "Recompile and run tests when files change.",
|
||||
"default": false
|
||||
},
|
||||
"testingType": {
|
||||
"enum": [
|
||||
"e2e",
|
||||
"component"
|
||||
],
|
||||
"description": "Specify the type of tests to execute; either e2e or component. Defaults to e2e."
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
|
||||
53
npm/cypress-schematic/src/ct.spec.ts
Normal file
53
npm/cypress-schematic/src/ct.spec.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import Fixtures, { ProjectFixtureDir } from '@tooling/system-tests'
|
||||
import * as FixturesScaffold from '@tooling/system-tests/lib/dep-installer'
|
||||
import execa from 'execa'
|
||||
import path from 'path'
|
||||
import * as fs from 'fs-extra'
|
||||
|
||||
const scaffoldAngularProject = async (project: string) => {
|
||||
const projectPath = Fixtures.projectPath(project)
|
||||
|
||||
Fixtures.removeProject(project)
|
||||
await Fixtures.scaffoldProject(project)
|
||||
await FixturesScaffold.scaffoldProjectNodeModules(project)
|
||||
await fs.remove(path.join(projectPath, 'cypress.config.ts'))
|
||||
await fs.remove(path.join(projectPath, 'cypress'))
|
||||
|
||||
return projectPath
|
||||
}
|
||||
|
||||
const runCommandInProject = (command: string, projectPath: string) => {
|
||||
const [ex, ...args] = command.split(' ')
|
||||
|
||||
return execa(ex, args, { cwd: projectPath, stdio: 'inherit' })
|
||||
}
|
||||
|
||||
// Since the schematic downloads a new version of cypress, the latest changes of
|
||||
// @cypress/angular won't exist in the tmp project. To fix this, we replace the
|
||||
// contents of the <project-path>/node_modules/cypress/angular with the latest
|
||||
// contents of cli/angular
|
||||
const copyAngularMount = async (projectPath: string) => {
|
||||
await fs.copy(
|
||||
path.join(__dirname, '..', '..', '..', 'cli', 'angular'),
|
||||
path.join(projectPath, 'node_modules', 'cypress', 'angular'),
|
||||
)
|
||||
}
|
||||
|
||||
const cypressSchematicPackagePath = path.join(__dirname, '..')
|
||||
|
||||
const ANGULAR_PROJECTS: ProjectFixtureDir[] = ['angular-13', 'angular-14']
|
||||
|
||||
describe('ng add @cypress/schematic / e2e and ct', function () {
|
||||
this.timeout(1000 * 60 * 4)
|
||||
|
||||
for (const project of ANGULAR_PROJECTS) {
|
||||
it('should install ct files with option and no component specs', async () => {
|
||||
const projectPath = await scaffoldAngularProject(project)
|
||||
|
||||
await runCommandInProject(`yarn add @cypress/schematic@file:${cypressSchematicPackagePath}`, projectPath)
|
||||
await runCommandInProject('yarn ng add @cypress/schematic --e2e --component', projectPath)
|
||||
await copyAngularMount(projectPath)
|
||||
await runCommandInProject('yarn ng run angular:ct --watch false --spec src/app/app.component.cy.ts', projectPath)
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -26,16 +26,16 @@ const cypressSchematicPackagePath = path.join(__dirname, '..')
|
||||
|
||||
const ANGULAR_PROJECTS: ProjectFixtureDir[] = ['angular-13', 'angular-14']
|
||||
|
||||
describe('cypress-schematic-e2e', function () {
|
||||
describe('ng add @cypress/schematic / only e2e', function () {
|
||||
this.timeout(1000 * 60 * 4)
|
||||
|
||||
for (const project of ANGULAR_PROJECTS) {
|
||||
it('should', async () => {
|
||||
it('should install e2e files by default', async () => {
|
||||
const projectPath = await scaffoldAngularProject(project)
|
||||
|
||||
await runCommandInProject(`yarn add @cypress/schematic@file:${cypressSchematicPackagePath}`, projectPath)
|
||||
await runCommandInProject('yarn ng add @cypress/schematic --e2eUpdate', projectPath)
|
||||
await runCommandInProject('yarn ng e2e angular --watch false', projectPath)
|
||||
await runCommandInProject('yarn ng add @cypress/schematic --e2e --component false --add-ct-specs false', projectPath)
|
||||
await runCommandInProject('yarn ng e2e --watch false', projectPath)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -6,13 +6,15 @@
|
||||
"factory": "./ng-add/index",
|
||||
"schema": "./ng-add/schema.json"
|
||||
},
|
||||
"e2e": {
|
||||
"description": "Create an e2e spec file",
|
||||
"factory": "./ng-generate/e2e/index",
|
||||
"schema": "./ng-generate/e2e/schema.json",
|
||||
"aliases": [
|
||||
"e2e-spec"
|
||||
]
|
||||
"spec": {
|
||||
"description": "Create a single spec file",
|
||||
"factory": "./ng-generate/cypress-test/index",
|
||||
"schema": "./ng-generate/cypress-test/schema.json"
|
||||
},
|
||||
"specs-ct": {
|
||||
"description": "Create spec files for all Angular components in a project",
|
||||
"factory": "./ng-generate/cypress-ct-tests/index",
|
||||
"schema": "./ng-generate/cypress-ct-tests/schema.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import { defineConfig } from 'cypress'
|
||||
|
||||
export default defineConfig({
|
||||
<% if (e2e) { %>
|
||||
e2e: {
|
||||
'baseUrl': '<%= baseUrl%>',
|
||||
supportFile: false
|
||||
},
|
||||
<% } %>
|
||||
<% if (component) { %>
|
||||
component: {
|
||||
devServer: {
|
||||
framework: 'angular',
|
||||
bundler: 'webpack',
|
||||
},
|
||||
specPattern: '**/*.cy.ts'
|
||||
}
|
||||
<% } %>
|
||||
})
|
||||
@@ -1,5 +1,5 @@
|
||||
// ***********************************************************
|
||||
// This example support/component.ts is processed and
|
||||
// This example support/e2e.ts is processed and
|
||||
// loaded automatically before your test files.
|
||||
//
|
||||
// This is a great place to put global configuration and
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"extends": "<%= relativeToWorkspace %>/tsconfig.json",
|
||||
"extends": "../tsconfig.json",
|
||||
"include": ["**/*.ts"],
|
||||
"compilerOptions": {
|
||||
"sourceMap": false,
|
||||
@@ -0,0 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<title>Components App</title>
|
||||
</head>
|
||||
<body>
|
||||
<div data-cy-root></div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,39 @@
|
||||
// ***********************************************************
|
||||
// This example support/component.ts is processed and
|
||||
// loaded automatically before your test files.
|
||||
//
|
||||
// This is a great place to put global configuration and
|
||||
// behavior that modifies Cypress.
|
||||
//
|
||||
// You can change the location of this file or turn off
|
||||
// automatically serving support files with the
|
||||
// 'supportFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/configuration
|
||||
// ***********************************************************
|
||||
|
||||
// Import commands.js using ES2015 syntax:
|
||||
import './commands'
|
||||
|
||||
// Alternatively you can use CommonJS syntax:
|
||||
// require('./commands')
|
||||
|
||||
import { mount } from 'cypress/angular'
|
||||
|
||||
// Augment the Cypress namespace to include type definitions for
|
||||
// your custom command.
|
||||
// Alternatively, can be defined in cypress/support/component.d.ts
|
||||
// with a <reference path="./component" /> at the top of your spec.
|
||||
declare global {
|
||||
namespace Cypress {
|
||||
interface Chainable {
|
||||
mount: typeof mount
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Cypress.Commands.add('mount', mount)
|
||||
|
||||
// Example use:
|
||||
// cy.mount(MyComponent)
|
||||
@@ -31,8 +31,8 @@ describe('@cypress/schematic: ng-add', () => {
|
||||
appTree = await schematicRunner.runExternalSchematicAsync('@schematics/angular', 'application', appOptions, appTree).toPromise()
|
||||
})
|
||||
|
||||
it('should create cypress files', async () => {
|
||||
return schematicRunner.runSchematicAsync('ng-add', {}, appTree).toPromise().then((tree) => {
|
||||
it('should create cypress files for e2e testing by default', async () => {
|
||||
await schematicRunner.runSchematicAsync('ng-add', {}, appTree).toPromise().then((tree: UnitTestTree) => {
|
||||
const files = tree.files
|
||||
|
||||
expect(files).to.contain('/projects/sandbox/cypress/e2e/spec.cy.ts')
|
||||
@@ -43,4 +43,19 @@ describe('@cypress/schematic: ng-add', () => {
|
||||
expect(files).to.contain('/projects/sandbox/cypress/fixtures/example.json')
|
||||
})
|
||||
})
|
||||
|
||||
it('should create cypress files for component testing', async () => {
|
||||
await schematicRunner.runSchematicAsync('ng-add', { 'component': true }, appTree).toPromise().then((tree: UnitTestTree) => {
|
||||
const files = tree.files
|
||||
|
||||
expect(files).to.contain('/projects/sandbox/cypress/support/component.ts')
|
||||
expect(files).to.contain('/projects/sandbox/cypress/support/component-index.html')
|
||||
expect(files).to.contain('/projects/sandbox/cypress/e2e/spec.cy.ts')
|
||||
expect(files).to.contain('/projects/sandbox/cypress/support/e2e.ts')
|
||||
expect(files).to.contain('/projects/sandbox/cypress/support/commands.ts')
|
||||
expect(files).to.contain('/projects/sandbox/cypress/tsconfig.json')
|
||||
expect(files).to.contain('/projects/sandbox/cypress.config.ts')
|
||||
expect(files).to.contain('/projects/sandbox/cypress/fixtures/example.json')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
Rule,
|
||||
SchematicContext,
|
||||
SchematicsException,
|
||||
template,
|
||||
applyTemplates,
|
||||
Tree,
|
||||
url,
|
||||
} from '@angular-devkit/schematics'
|
||||
@@ -17,20 +17,32 @@ import { concatMap, map } from 'rxjs/operators'
|
||||
|
||||
import { addPackageJsonDependency, NodeDependencyType } from '../utils/dependencies'
|
||||
import {
|
||||
getAngularJsonValue,
|
||||
getAngularVersion,
|
||||
getLatestNodeVersion,
|
||||
NodePackage,
|
||||
getDirectoriesAndCreateSpecs,
|
||||
} from '../utils'
|
||||
import { relative, resolve } from 'path'
|
||||
import { JSONFile, JSONPath } from '../utils/jsonFile'
|
||||
|
||||
type HandleFilesType = {
|
||||
projects: any
|
||||
options: any
|
||||
applyPath: string
|
||||
movePath?: string
|
||||
relativeToWorkspacePath: string
|
||||
}
|
||||
|
||||
export default function (_options: any): Rule {
|
||||
return (tree: Tree, _context: SchematicContext) => {
|
||||
_options = { ..._options, __version__: getAngularVersion(tree) }
|
||||
|
||||
return chain([
|
||||
updateDependencies(),
|
||||
addCypressFiles(),
|
||||
addCypressCoreFiles(_options),
|
||||
addCypressComponentTestingFiles(_options),
|
||||
addCtSpecs(_options),
|
||||
addCypressTestScriptsToPackageJson(),
|
||||
modifyAngularJson(_options),
|
||||
])(tree, _context)
|
||||
@@ -79,33 +91,79 @@ function addCypressTestScriptsToPackageJson (): Rule {
|
||||
}
|
||||
}
|
||||
|
||||
function addCypressFiles (): Rule {
|
||||
function handleFiles (tree: Tree, context: SchematicContext, { projects, options, applyPath, movePath, relativeToWorkspacePath }: HandleFilesType): any {
|
||||
return chain(
|
||||
Object.keys(projects).map((name) => {
|
||||
const project = projects[name]
|
||||
const projectPath = resolve(getSystemPath(normalize(project.root)))
|
||||
const workspacePath = resolve(getSystemPath(normalize('')))
|
||||
|
||||
const relativeToWorkspace = relative(`${projectPath}${relativeToWorkspacePath}`, workspacePath)
|
||||
|
||||
const baseUrl = getBaseUrl(project)
|
||||
|
||||
return mergeWith(
|
||||
apply(url(applyPath), [
|
||||
move(movePath ? `${project.root}${movePath}` : project.root),
|
||||
applyTemplates({
|
||||
...options,
|
||||
...strings,
|
||||
root: project.root ? `${project.root}/` : project.root,
|
||||
baseUrl,
|
||||
relativeToWorkspace,
|
||||
}),
|
||||
]),
|
||||
)
|
||||
}),
|
||||
)(tree, context)
|
||||
}
|
||||
|
||||
function addCypressCoreFiles (options: any): Rule {
|
||||
return (tree: Tree, context: SchematicContext) => {
|
||||
context.logger.debug('Adding cypress files')
|
||||
const angularJsonValue = getAngularJsonValue(tree)
|
||||
const { projects } = angularJsonValue
|
||||
|
||||
return chain(
|
||||
return handleFiles(tree, context, {
|
||||
projects,
|
||||
options,
|
||||
applyPath: './files-core',
|
||||
relativeToWorkspacePath: `/`,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function addCypressComponentTestingFiles (options: any): Rule {
|
||||
return (tree: Tree, context: SchematicContext) => {
|
||||
if (options.component) {
|
||||
context.logger.debug('Adding cypress component testing files')
|
||||
const angularJsonValue = getAngularJsonValue(tree)
|
||||
const { projects } = angularJsonValue
|
||||
|
||||
return handleFiles(tree, context, {
|
||||
projects,
|
||||
options,
|
||||
applyPath: './files-ct',
|
||||
movePath: '/cypress/support',
|
||||
relativeToWorkspacePath: `/cypress`,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function addCtSpecs (options: any): Rule {
|
||||
return (tree: Tree) => {
|
||||
if (options.addCtSpecs) {
|
||||
const angularJsonValue = getAngularJsonValue(tree)
|
||||
const { projects } = angularJsonValue
|
||||
|
||||
Object.keys(projects).map((name) => {
|
||||
const project = projects[name]
|
||||
const projectPath = resolve(getSystemPath(normalize(project.root)))
|
||||
const workspacePath = resolve(getSystemPath(normalize('')))
|
||||
const relativeToWorkspace = relative(`${projectPath}/cypress`, workspacePath)
|
||||
const baseUrl = getBaseUrl(project)
|
||||
const appPath = `${project.root}/${project.sourceRoot}/${project.prefix}`
|
||||
|
||||
return mergeWith(
|
||||
apply(url('./files'), [
|
||||
move(project.root),
|
||||
template({
|
||||
...strings,
|
||||
root: project.root ? `${project.root}/` : project.root,
|
||||
baseUrl,
|
||||
relativeToWorkspace,
|
||||
}),
|
||||
]),
|
||||
)
|
||||
}),
|
||||
)(tree, context)
|
||||
return getDirectoriesAndCreateSpecs({ tree, appPath })
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,26 +187,26 @@ function addNewCypressCommands (
|
||||
runJson: JsonObject,
|
||||
openJson: JsonObject,
|
||||
e2eJson: JsonObject,
|
||||
e2eUpdate: boolean,
|
||||
e2e: boolean,
|
||||
componentJson: JsonObject,
|
||||
component: boolean,
|
||||
) {
|
||||
const projectArchitectJson = angularJsonVal['projects'][project]['architect']
|
||||
|
||||
projectArchitectJson['cypress-run'] = runJson
|
||||
projectArchitectJson['cypress-open'] = openJson
|
||||
|
||||
if (e2eUpdate || !projectArchitectJson['e2e']) {
|
||||
if (component) {
|
||||
projectArchitectJson['ct'] = componentJson
|
||||
}
|
||||
|
||||
if (e2e || !projectArchitectJson['e2e']) {
|
||||
projectArchitectJson['e2e'] = e2eJson
|
||||
}
|
||||
|
||||
return tree.overwrite('./angular.json', JSON.stringify(angularJsonVal, null, 2))
|
||||
}
|
||||
|
||||
function getAngularJsonValue (tree: Tree) {
|
||||
const angularJson = new JSONFile(tree, './angular.json')
|
||||
|
||||
return angularJson.get([]) as any
|
||||
}
|
||||
|
||||
function modifyAngularJson (options: any): Rule {
|
||||
return (tree: Tree, context: SchematicContext) => {
|
||||
if (tree.exists('./angular.json')) {
|
||||
@@ -195,6 +253,21 @@ function modifyAngularJson (options: any): Rule {
|
||||
},
|
||||
}
|
||||
|
||||
const componentJson = {
|
||||
builder,
|
||||
options: {
|
||||
devServerTarget: `${project}:serve`,
|
||||
watch: true,
|
||||
headless: false,
|
||||
testingType: 'component',
|
||||
},
|
||||
configurations: {
|
||||
development: {
|
||||
devServerTarget: `${project}:serve:development`,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const configFile = getCypressConfigFile(angularJsonVal, project)
|
||||
|
||||
if (configFile) {
|
||||
@@ -202,7 +275,7 @@ function modifyAngularJson (options: any): Rule {
|
||||
Object.assign(openJson.options, { configFile })
|
||||
}
|
||||
|
||||
if (options.e2eUpdate) {
|
||||
if (options.e2e) {
|
||||
context.logger.debug(`Replacing e2e command with cypress-run in angular.json`)
|
||||
removeE2ELinting(tree, angularJsonVal, project)
|
||||
}
|
||||
@@ -220,7 +293,9 @@ function modifyAngularJson (options: any): Rule {
|
||||
runJson,
|
||||
openJson,
|
||||
e2eJson,
|
||||
options.e2eUpdate,
|
||||
options.e2e,
|
||||
componentJson,
|
||||
options.component,
|
||||
)
|
||||
})
|
||||
} else {
|
||||
|
||||
@@ -4,11 +4,23 @@
|
||||
"title": "Cypress Install Schema",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"e2eUpdate": {
|
||||
"e2e": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "When true, `ng e2e` will be added or updated to use Cypress",
|
||||
"description": "When true, `ng e2e` will be added or updated to use Cypress.",
|
||||
"x-prompt": "Would you like the default `ng e2e` command to use Cypress? [ Protractor to Cypress Migration Guide: https://on.cypress.io/protractor-to-cypress?cli=true ]"
|
||||
},
|
||||
"component": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "When true, your project will set up for Cypress component testing.",
|
||||
"x-prompt": "Would you like to add Cypress component testing? This will add all files needed for Cypress component testing."
|
||||
},
|
||||
"addCtSpecs": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"alias": "a",
|
||||
"description": "When true, Cypress component tests will be added alongside `.component` files."
|
||||
}
|
||||
},
|
||||
"required": []
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing'
|
||||
import { join } from 'path'
|
||||
import { expect } from 'chai'
|
||||
import { take } from 'rxjs/operators'
|
||||
|
||||
describe('@cypress/schematic:e2e ng-generate', () => {
|
||||
describe('ng-generate @cypress/schematic:specs-ct', () => {
|
||||
const schematicRunner = new SchematicTestRunner(
|
||||
'schematics',
|
||||
join(__dirname, '../../collection.json'),
|
||||
@@ -30,11 +31,7 @@ describe('@cypress/schematic:e2e ng-generate', () => {
|
||||
appTree = await schematicRunner.runExternalSchematicAsync('@schematics/angular', 'application', appOptions, appTree).toPromise()
|
||||
})
|
||||
|
||||
it('should create cypress spec file', () => {
|
||||
return schematicRunner.runSchematicAsync('e2e', { name: 'foo', project: 'sandbox' }, appTree).toPromise().then((tree) => {
|
||||
const files = tree.files
|
||||
|
||||
expect(files).to.contain('/projects/sandbox/cypress/e2e/foo.cy.ts')
|
||||
})
|
||||
it('should create cypress component tests alongside components', async () => {
|
||||
return schematicRunner.runSchematicAsync('specs-ct', { project: 'sandbox' }, appTree).pipe(take(1)).subscribe((tree: UnitTestTree) => expect(tree.files).to.contain('/projects/sandbox/app/src/app.component.cy.ts'))
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,20 @@
|
||||
import { Rule, Tree, SchematicsException } from '@angular-devkit/schematics'
|
||||
|
||||
import { getAngularJsonValue, getDirectoriesAndCreateSpecs } from '../../utils'
|
||||
import { Schema } from './schema'
|
||||
|
||||
export default function (options: Schema): Rule {
|
||||
return (tree: Tree) => {
|
||||
if (!options.project) {
|
||||
throw new SchematicsException(`Invalid project name: ${options.project}`)
|
||||
}
|
||||
|
||||
const angularJsonValue = getAngularJsonValue(tree)
|
||||
const { projects } = angularJsonValue
|
||||
const project = projects[options.project]
|
||||
|
||||
const appPath = `${project.sourceRoot}`
|
||||
|
||||
return getDirectoriesAndCreateSpecs({ tree, appPath })
|
||||
}
|
||||
}
|
||||
@@ -1,33 +1,23 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema",
|
||||
"$id": "cypress-schematics-e2e-spec",
|
||||
"$id": "cypress-schematics-generate-ct-specs",
|
||||
"title": "Cypress E2E Spec Options Schema",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"path": {
|
||||
"type": "string",
|
||||
"format": "path",
|
||||
"description": "The path to create the component.",
|
||||
"description": "The path where the spec will be created.",
|
||||
"visible": false
|
||||
},
|
||||
"project": {
|
||||
"type": "string",
|
||||
"description": "The name of the project.",
|
||||
"alias": "p",
|
||||
"$default": {
|
||||
"$source": "projectName"
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "The name of the e2e test.",
|
||||
"$default": {
|
||||
"$source": "argv",
|
||||
"index": 0
|
||||
},
|
||||
"x-prompt": "What is the name of the e2e test?"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name"
|
||||
]
|
||||
"required": []
|
||||
}
|
||||
@@ -1,10 +1,7 @@
|
||||
export interface Schema {
|
||||
// The name of the spec.
|
||||
name: string
|
||||
// The name of the project.
|
||||
project?: string
|
||||
|
||||
// The path to create the spec.
|
||||
path?: string
|
||||
|
||||
// The name of the project.
|
||||
project?: string
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/// <reference path="../../../../../../cli/types/mocha/index.d.ts" />
|
||||
|
||||
import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing'
|
||||
import { join } from 'path'
|
||||
import { expect } from 'chai'
|
||||
|
||||
describe('ng-generate @cypress/schematic:spec', () => {
|
||||
const schematicRunner = new SchematicTestRunner(
|
||||
'schematics',
|
||||
join(__dirname, '../../collection.json'),
|
||||
)
|
||||
let appTree: UnitTestTree
|
||||
|
||||
const workspaceOptions = {
|
||||
name: 'workspace',
|
||||
newProjectRoot: 'projects',
|
||||
version: '12.0.0',
|
||||
}
|
||||
|
||||
const appOptions: Parameters<typeof schematicRunner['runExternalSchematicAsync']>[2] = {
|
||||
name: 'sandbox',
|
||||
inlineTemplate: false,
|
||||
routing: false,
|
||||
skipTests: false,
|
||||
skipPackageJson: false,
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
appTree = await schematicRunner.runExternalSchematicAsync('@schematics/angular', 'workspace', workspaceOptions).toPromise()
|
||||
appTree = await schematicRunner.runExternalSchematicAsync('@schematics/angular', 'application', appOptions, appTree).toPromise()
|
||||
})
|
||||
|
||||
it('should create cypress e2e spec file by default', async () => {
|
||||
await schematicRunner.runSchematicAsync('spec', { name: 'foo', project: 'sandbox' }, appTree).toPromise().then((tree: UnitTestTree) => expect(tree.files).to.contain('/projects/sandbox/cypress/e2e/foo.cy.ts'))
|
||||
})
|
||||
|
||||
it('should create cypress ct spec file when testingType is component', async () => {
|
||||
await schematicRunner.runSchematicAsync('spec', { name: 'foo', project: 'sandbox', component: true }, appTree).toPromise().then((tree: UnitTestTree) => expect(tree.files).to.contain('/projects/sandbox/src/app/foo.component.cy.ts'))
|
||||
})
|
||||
})
|
||||
@@ -1,13 +1,13 @@
|
||||
import {
|
||||
Rule, Tree, SchematicsException,
|
||||
apply, url, applyTemplates, move,
|
||||
chain, mergeWith,
|
||||
Rule, Tree, SchematicsException, chain, mergeWith,
|
||||
} from '@angular-devkit/schematics'
|
||||
|
||||
import { strings, normalize, virtualFs, workspaces } from '@angular-devkit/core'
|
||||
import { virtualFs, workspaces } from '@angular-devkit/core'
|
||||
|
||||
import { Schema } from './schema'
|
||||
|
||||
import { createTemplate } from '../../utils'
|
||||
|
||||
function createSpec (tree: Tree): workspaces.WorkspaceHost {
|
||||
return {
|
||||
async readFile (path: string): Promise<string> {
|
||||
@@ -35,6 +35,7 @@ export default function (options: Schema): Rule {
|
||||
return async (tree: Tree) => {
|
||||
const host = createSpec(tree)
|
||||
const { workspace } = await workspaces.readWorkspace('/', host)
|
||||
const testType = options.component ? 'component' : 'e2e'
|
||||
|
||||
let project
|
||||
|
||||
@@ -53,17 +54,13 @@ export default function (options: Schema): Rule {
|
||||
}
|
||||
|
||||
if (options.path === undefined) {
|
||||
options.path = `${project.root}/cypress/e2e`
|
||||
options.path = testType === 'component' ? `${project.sourceRoot}/${project.prefix}` : `${project.root}/cypress/e2e`
|
||||
}
|
||||
|
||||
const templateSource = apply(url('../files/__path__'), [
|
||||
applyTemplates({
|
||||
classify: strings.classify,
|
||||
dasherize: strings.dasherize,
|
||||
name: options.name,
|
||||
}),
|
||||
move(normalize(options.path as string)),
|
||||
])
|
||||
console.log(`Creating new ${testType} spec named: ${options.name}`)
|
||||
|
||||
const templatePath = testType === 'component' ? '../files/ct/__path__' : '../files/e2e/__path__'
|
||||
const templateSource = createTemplate({ templatePath, options })
|
||||
|
||||
return chain([
|
||||
mergeWith(templateSource),
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema",
|
||||
"$id": "cypress-schematics-generate-spec",
|
||||
"title": "Cypress Generate Spec Options Schema",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"filename": {
|
||||
"type": "string",
|
||||
"description": "Allows users to specify a custom filename.",
|
||||
"visible": false
|
||||
},
|
||||
"path": {
|
||||
"type": "string",
|
||||
"format": "path",
|
||||
"description": "The path where the spec will be created.",
|
||||
"visible": false
|
||||
},
|
||||
"project": {
|
||||
"type": "string",
|
||||
"description": "The name of the project to create the spec in.",
|
||||
"alias": "p",
|
||||
"$default": {
|
||||
"$source": "projectName"
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "The name of the spec.",
|
||||
"alias": "n",
|
||||
"$default": {
|
||||
"$source": "argv",
|
||||
"index": 0
|
||||
},
|
||||
"x-prompt": "What should the spec be named?"
|
||||
},
|
||||
"component": {
|
||||
"type": "boolean",
|
||||
"alias": "c",
|
||||
"default": false,
|
||||
"description": "When true, the spec created will be a component spec."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
export interface Schema {
|
||||
// Custom filename.
|
||||
filename?: string
|
||||
|
||||
// The name of the spec.
|
||||
name: string
|
||||
|
||||
// The path to create the spec.
|
||||
path?: string
|
||||
|
||||
// The name of the project.
|
||||
project?: string
|
||||
|
||||
// Create a component spec
|
||||
component?: boolean
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import { <%= classify(name) %> } from './<%= fileName %>.component'
|
||||
|
||||
describe('<%= classify(name) %>', () => {
|
||||
it('should mount', () => {
|
||||
cy.mount(<%= classify(name) %>)
|
||||
})
|
||||
})
|
||||
@@ -4,7 +4,7 @@
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
* https://github.com/angular/angular-cli/blob/master/packages/schematics/angular/utility/dependencies.ts
|
||||
* https://github.com/angular/angular-cli/blob/master/packages/schematics/angular/utils/dependencies.ts
|
||||
*/
|
||||
|
||||
import { Tree } from '@angular-devkit/schematics'
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import { Tree } from '@angular-devkit/schematics'
|
||||
import { readdirSync } from 'fs'
|
||||
import { resolve } from 'path'
|
||||
import { getSystemPath, normalize, strings } from '@angular-devkit/core'
|
||||
import { Tree, apply, url, applyTemplates, move, Rule } from '@angular-devkit/schematics'
|
||||
import { get } from 'http'
|
||||
import { Schema } from '../ng-generate/cypress-test/schema'
|
||||
|
||||
import { getPackageJsonDependency } from './dependencies'
|
||||
import { JSONFile } from './jsonFile'
|
||||
|
||||
export interface NodePackage {
|
||||
name: string
|
||||
@@ -46,3 +51,68 @@ export function getLatestNodeVersion (packageName: string): Promise<NodePackage>
|
||||
return { name, version }
|
||||
}
|
||||
}
|
||||
|
||||
const ctSpecContent = ({ componentName, componentFilename }: {componentName: string, componentFilename: string}): string => {
|
||||
return `import { ${componentName} } from './${componentFilename}.component'\n
|
||||
describe('${componentName}', () => {
|
||||
it('should mount', () => {
|
||||
cy.mount(${componentName})
|
||||
})
|
||||
})
|
||||
`
|
||||
}
|
||||
|
||||
function generateCTSpec ({ tree, appPath, component }: { tree: Tree, appPath: string, component: any}): Rule | void {
|
||||
const buffer = tree.read(`${appPath}/${component['name']}`)
|
||||
const componentString = buffer?.toString()
|
||||
const componentMatch = componentString?.match(/(?<=class )\S+/g)
|
||||
const componentFilename = component['name'].split('.')[0]
|
||||
const componentName = componentMatch ? componentMatch[0] : componentFilename
|
||||
|
||||
console.log(`Creating new component spec for: ${componentName}\n`)
|
||||
|
||||
return tree.create(`${appPath}/${componentFilename}.component.cy.ts`, ctSpecContent({ componentName, componentFilename }))
|
||||
}
|
||||
|
||||
export function getDirectoriesAndCreateSpecs ({ appPath, tree }: { appPath: string, tree: Tree}) {
|
||||
let components = []
|
||||
let directories = []
|
||||
|
||||
const projectPath = resolve(getSystemPath(normalize('')))
|
||||
const contents = readdirSync(resolve(`${projectPath}/${appPath}`), { withFileTypes: true })
|
||||
|
||||
if (contents) {
|
||||
components = contents.filter((file) => file['name'].endsWith(`component.ts`))
|
||||
directories = contents.filter((file) => file.isDirectory())
|
||||
|
||||
if (components) {
|
||||
components.map((component) => {
|
||||
return generateCTSpec({ tree, appPath, component })
|
||||
})
|
||||
}
|
||||
|
||||
if (directories) {
|
||||
directories.forEach((directory: any) => {
|
||||
return getDirectoriesAndCreateSpecs({ tree, appPath: `${appPath}/${directory['name']}` })
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function createTemplate ({ templatePath, options }: {templatePath: string, options: Schema}): any {
|
||||
return apply(url(templatePath), [
|
||||
applyTemplates({
|
||||
classify: strings.classify,
|
||||
dasherize: strings.dasherize,
|
||||
name: options.component ? `${options.name}Component` : options.name,
|
||||
fileName: options.filename || options.name,
|
||||
}),
|
||||
move(normalize(options.path as string)),
|
||||
])
|
||||
}
|
||||
|
||||
export function getAngularJsonValue (tree: Tree) {
|
||||
const angularJson = new JSONFile(tree, './angular.json')
|
||||
|
||||
return angularJson.get([]) as any
|
||||
}
|
||||
|
||||
@@ -27,5 +27,5 @@
|
||||
"include": [
|
||||
"src/**/*"
|
||||
],
|
||||
"exclude": ["src/**/files/**/*", "src/**/*.spec.ts"]
|
||||
"exclude": ["src/**/files-core/**/*", "src/**/files-ct/**/*", "src/**/*.spec.ts"]
|
||||
}
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
# [@cypress/mount-utils-v2.1.0](https://github.com/cypress-io/cypress/compare/@cypress/mount-utils-v2.0.1...@cypress/mount-utils-v2.1.0) (2022-08-30)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* adding svelte component testing support ([#23553](https://github.com/cypress-io/cypress/issues/23553)) ([f6eaad4](https://github.com/cypress-io/cypress/commit/f6eaad40e1836fa9db87c60defa5ae6f390c8fd8))
|
||||
|
||||
# [@cypress/mount-utils-v2.0.1](https://github.com/cypress-io/cypress/compare/@cypress/mount-utils-v2.0.0...@cypress/mount-utils-v2.0.1) (2022-08-11)
|
||||
|
||||
|
||||
|
||||
71
npm/mount-utils/create-rollup-entry.mjs
Normal file
71
npm/mount-utils/create-rollup-entry.mjs
Normal file
@@ -0,0 +1,71 @@
|
||||
// CommonJS to easily share across packages
|
||||
import ts from 'rollup-plugin-typescript2'
|
||||
import resolve from '@rollup/plugin-node-resolve'
|
||||
import commonjs from '@rollup/plugin-commonjs'
|
||||
import _ from 'lodash'
|
||||
import { readFileSync } from 'fs'
|
||||
|
||||
const pkg = JSON.parse(readFileSync('./package.json'))
|
||||
|
||||
/** @type {(options: { formats: string[], input: string, config: {} }) => []} */
|
||||
export function createEntries (options) {
|
||||
const {
|
||||
formats,
|
||||
input,
|
||||
config = {},
|
||||
} = options
|
||||
|
||||
const banner = `
|
||||
/**
|
||||
* ${pkg.name} v${pkg.version}
|
||||
* (c) ${new Date().getFullYear()} Cypress.io
|
||||
* Released under the MIT License
|
||||
*/
|
||||
`
|
||||
|
||||
return formats.map((format) => {
|
||||
const baseConfig = {
|
||||
input,
|
||||
plugins: [
|
||||
resolve({ preferBuiltins: true }),
|
||||
commonjs(),
|
||||
ts({
|
||||
check: format === 'es',
|
||||
tsconfigOverride: {
|
||||
compilerOptions: {
|
||||
declaration: format === 'es',
|
||||
target: 'es6',
|
||||
module: format === 'cjs' ? 'es2015' : 'esnext',
|
||||
},
|
||||
exclude: ['tests'],
|
||||
},
|
||||
}),
|
||||
],
|
||||
output: {
|
||||
banner,
|
||||
name: 'CypressReact',
|
||||
file: pkg.unpkg,
|
||||
format,
|
||||
},
|
||||
}
|
||||
|
||||
const finalConfig = _.mergeWith({}, baseConfig, config, (objValue, srcValue) => {
|
||||
if (_.isArray(objValue)) {
|
||||
return objValue.concat(srcValue)
|
||||
}
|
||||
})
|
||||
|
||||
if (format === 'es') {
|
||||
finalConfig.output.file = pkg.module
|
||||
}
|
||||
|
||||
if (format === 'cjs') {
|
||||
finalConfig.output.file = pkg.main
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`Building ${format}: ${finalConfig.output.file}`)
|
||||
|
||||
return finalConfig
|
||||
})
|
||||
}
|
||||
@@ -12,7 +12,11 @@
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"typescript": "^4.2.3"
|
||||
"@rollup/plugin-commonjs": "^17.1.0",
|
||||
"@rollup/plugin-node-resolve": "^11.1.1",
|
||||
"rollup": "^2.38.5",
|
||||
"rollup-plugin-typescript2": "^0.29.0",
|
||||
"typescript": "^4.7.4"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
# [@cypress/react-v6.2.0](https://github.com/cypress-io/cypress/compare/@cypress/react-v6.1.1...@cypress/react-v6.2.0) (2022-08-30)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* adding svelte component testing support ([#23553](https://github.com/cypress-io/cypress/issues/23553)) ([f6eaad4](https://github.com/cypress-io/cypress/commit/f6eaad40e1836fa9db87c60defa5ae6f390c8fd8))
|
||||
|
||||
# [@cypress/react-v6.1.1](https://github.com/cypress-io/cypress/compare/@cypress/react-v6.1.0...@cypress/react-v6.1.1) (2022-08-15)
|
||||
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"description": "Test React components using Cypress",
|
||||
"main": "dist/cypress-react.cjs.js",
|
||||
"scripts": {
|
||||
"build": "rimraf dist && rollup -c rollup.config.js",
|
||||
"build": "rimraf dist && rollup -c rollup.config.mjs",
|
||||
"postbuild": "node ../../scripts/sync-exported-npm-with-cli.js",
|
||||
"build-prod": "yarn build",
|
||||
"cy:open": "node ../../scripts/cypress.js open --component",
|
||||
@@ -16,8 +16,6 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cypress/mount-utils": "0.0.0-development",
|
||||
"@rollup/plugin-commonjs": "^17.1.0",
|
||||
"@rollup/plugin-node-resolve": "^11.1.1",
|
||||
"@types/semver": "7.3.9",
|
||||
"@vitejs/plugin-react": "1.3.1",
|
||||
"axios": "0.21.2",
|
||||
@@ -27,11 +25,9 @@
|
||||
"react-dom": "16.8.6",
|
||||
"react-router": "6.0.0-alpha.1",
|
||||
"react-router-dom": "6.0.0-alpha.1",
|
||||
"rollup": "^2.38.5",
|
||||
"rollup-plugin-typescript2": "^0.29.0",
|
||||
"semver": "^7.3.2",
|
||||
"typescript": "^4.2.3",
|
||||
"vite": "3.0.3",
|
||||
"typescript": "^4.7.4",
|
||||
"vite": "3.1.0",
|
||||
"vite-plugin-require-transform": "1.0.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
// CommonJS to easily share across packages
|
||||
const ts = require('rollup-plugin-typescript2')
|
||||
const { default: resolve } = require('@rollup/plugin-node-resolve')
|
||||
const commonjs = require('@rollup/plugin-commonjs')
|
||||
|
||||
const pkg = require('./package.json')
|
||||
|
||||
const banner = `
|
||||
/**
|
||||
* ${pkg.name} v${pkg.version}
|
||||
* (c) ${new Date().getFullYear()} Cypress.io
|
||||
* Released under the MIT License
|
||||
*/
|
||||
`
|
||||
|
||||
function createEntry (options) {
|
||||
const {
|
||||
format,
|
||||
input,
|
||||
} = options
|
||||
|
||||
const config = {
|
||||
input,
|
||||
external: [
|
||||
'react',
|
||||
'react-dom',
|
||||
'react-dom/client',
|
||||
],
|
||||
plugins: [
|
||||
resolve(),
|
||||
commonjs(),
|
||||
ts({
|
||||
check: format === 'es',
|
||||
tsconfigOverride: {
|
||||
compilerOptions: {
|
||||
declaration: format === 'es',
|
||||
target: 'es5',
|
||||
module: format === 'cjs' ? 'es2015' : 'esnext',
|
||||
},
|
||||
exclude: ['tests'],
|
||||
},
|
||||
}),
|
||||
],
|
||||
output: {
|
||||
banner,
|
||||
name: 'CypressReact',
|
||||
file: pkg.unpkg,
|
||||
format,
|
||||
globals: {
|
||||
react: 'React',
|
||||
'react-dom': 'ReactDOM',
|
||||
'react-dom/client': 'ReactDOM/client',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if (format === 'es') {
|
||||
config.output.file = pkg.module
|
||||
}
|
||||
|
||||
if (format === 'cjs') {
|
||||
config.output.file = pkg.main
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`Building ${format}: ${config.output.file}`)
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
module.exports = [
|
||||
createEntry({ format: 'es', input: 'src/index.ts' }),
|
||||
createEntry({ format: 'cjs', input: 'src/index.ts' }),
|
||||
]
|
||||
18
npm/react/rollup.config.mjs
Normal file
18
npm/react/rollup.config.mjs
Normal file
@@ -0,0 +1,18 @@
|
||||
import { createEntries } from '@cypress/mount-utils/create-rollup-entry.mjs'
|
||||
|
||||
const config = {
|
||||
external: [
|
||||
'react',
|
||||
'react-dom',
|
||||
'react-dom/client',
|
||||
],
|
||||
output: {
|
||||
globals: {
|
||||
react: 'React',
|
||||
'react-dom': 'ReactDOM',
|
||||
'react-dom/client': 'ReactDOM/client',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export default createEntries({ formats: ['es', 'cjs'], input: 'src/index.ts', config })
|
||||
@@ -1,3 +1,10 @@
|
||||
# [@cypress/react18-v1.1.0](https://github.com/cypress-io/cypress/compare/@cypress/react18-v1.0.1...@cypress/react18-v1.1.0) (2022-08-30)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* adding svelte component testing support ([#23553](https://github.com/cypress-io/cypress/issues/23553)) ([f6eaad4](https://github.com/cypress-io/cypress/commit/f6eaad40e1836fa9db87c60defa5ae6f390c8fd8))
|
||||
|
||||
# [@cypress/react18-v1.0.1](https://github.com/cypress-io/cypress/compare/@cypress/react18-v1.0.0...@cypress/react18-v1.0.1) (2022-08-15)
|
||||
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"description": "Test React components using Cypress",
|
||||
"main": "dist/cypress-react.cjs.js",
|
||||
"scripts": {
|
||||
"build": "rimraf dist && rollup -c rollup.config.js",
|
||||
"build": "rimraf dist && rollup -c rollup.config.mjs",
|
||||
"postbuild": "node ../../scripts/sync-exported-npm-with-cli.js",
|
||||
"build-prod": "yarn build",
|
||||
"watch": "yarn build --watch --watch.exclude ./dist/**/*"
|
||||
@@ -20,7 +20,7 @@
|
||||
"react-dom": "^16",
|
||||
"rollup": "^2.38.5",
|
||||
"rollup-plugin-typescript2": "^0.29.0",
|
||||
"typescript": "^4.2.3"
|
||||
"typescript": "^4.7.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "^18",
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
import * as RollupConfig from '../react/rollup.config'
|
||||
|
||||
export default RollupConfig
|
||||
3
npm/react18/rollup.config.mjs
Normal file
3
npm/react18/rollup.config.mjs
Normal file
@@ -0,0 +1,3 @@
|
||||
import rollupConfig from '@cypress/react/rollup.config.mjs'
|
||||
|
||||
export default rollupConfig
|
||||
@@ -11,12 +11,14 @@ import type {
|
||||
UnmountArgs,
|
||||
} from '@cypress/react'
|
||||
|
||||
let root: any
|
||||
let root: ReactDOM.Root | null
|
||||
|
||||
const cleanup = () => {
|
||||
if (root) {
|
||||
root.unmount()
|
||||
|
||||
root = null
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -27,7 +29,9 @@ export function mount (jsx: React.ReactNode, options: MountOptions = {}, rerende
|
||||
const internalOptions: InternalMountOptions = {
|
||||
reactDom: ReactDOM,
|
||||
render: (reactComponent: ReturnType<typeof React.createElement>, el: HTMLElement) => {
|
||||
root = ReactDOM.createRoot(el)
|
||||
if (!root) {
|
||||
root = ReactDOM.createRoot(el)
|
||||
}
|
||||
|
||||
return root.render(reactComponent)
|
||||
},
|
||||
|
||||
17
npm/svelte/.eslintrc
Normal file
17
npm/svelte/.eslintrc
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"plugins": [
|
||||
"cypress"
|
||||
],
|
||||
"extends": [
|
||||
"plugin:@cypress/dev/tests"
|
||||
],
|
||||
"env": {
|
||||
"cypress/globals": true
|
||||
},
|
||||
"rules": {
|
||||
"mocha/no-global-tests": "off",
|
||||
"no-unused-vars": "off",
|
||||
"no-console": "off",
|
||||
"@typescript-eslint/no-unused-vars": "off"
|
||||
}
|
||||
}
|
||||
3
npm/svelte/.npmrc
Normal file
3
npm/svelte/.npmrc
Normal file
@@ -0,0 +1,3 @@
|
||||
save-exact=true
|
||||
progress=false
|
||||
package-lock=true
|
||||
7
npm/svelte/.releaserc.js
Normal file
7
npm/svelte/.releaserc.js
Normal file
@@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
...require('../../.releaserc.base'),
|
||||
branches: [
|
||||
// this one releases v3 on master on the latest channel
|
||||
'master',
|
||||
],
|
||||
}
|
||||
6
npm/svelte/CHANGELOG.md
Normal file
6
npm/svelte/CHANGELOG.md
Normal file
@@ -0,0 +1,6 @@
|
||||
# @cypress/svelte-v1.0.0 (2022-08-30)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* adding svelte component testing support ([#23553](https://github.com/cypress-io/cypress/issues/23553)) ([f6eaad4](https://github.com/cypress-io/cypress/commit/f6eaad40e1836fa9db87c60defa5ae6f390c8fd8))
|
||||
83
npm/svelte/README.md
Normal file
83
npm/svelte/README.md
Normal file
@@ -0,0 +1,83 @@
|
||||
# @cypress/svelte
|
||||
|
||||
Mount Svelte components in the open source [Cypress.io](https://www.cypress.io/) test runner **v10.7.0+**
|
||||
|
||||
> **Note:** This package is bundled with the `cypress` package and should not need to be installed separately. See the [Svelte Component Testing Docs](https://docs.cypress.io/guides/component-testing/quickstart-svelte#Configuring-Component-Testing) for mounting Svelte components. Installing and importing `mount` from `@cypress/svelte` should only be used for advanced use-cases.
|
||||
|
||||
## Install
|
||||
|
||||
- Requires Svelte >= 3
|
||||
- Requires Cypress v10.7.0 or later
|
||||
- Requires [Node](https://nodejs.org/en/) version 12 or above
|
||||
|
||||
```sh
|
||||
npm install --save-dev @cypress/svelte
|
||||
```
|
||||
|
||||
## Run
|
||||
|
||||
Open cypress test runner
|
||||
```
|
||||
npx cypress open --component
|
||||
```
|
||||
|
||||
If you need to run test in CI
|
||||
```
|
||||
npx cypress run --component
|
||||
```
|
||||
|
||||
For more information, please check the official docs for [running Cypress](https://on.cypress.io/guides/getting-started/opening-the-app#Quick-Configuration) and for [component testing](https://on.cypress.io/guides/component-testing/writing-your-first-component-test).
|
||||
|
||||
## Example
|
||||
|
||||
```js
|
||||
import { mount } from '@cypress/svelte'
|
||||
import HelloWorld from './HelloWorld.svelte'
|
||||
|
||||
describe('HelloWorld component', () => {
|
||||
it('works', () => {
|
||||
mount(HelloWorld)
|
||||
// now use standard Cypress commands
|
||||
cy.contains('Hello World!').should('be.visible')
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
In most cases, the component already imports its own styles, thus it looks "right" during the test. If you need another CSS, the simplest way is to import it from the spec file:
|
||||
|
||||
```js
|
||||
// src/HelloWorld.svelte
|
||||
import './styles/main.css'
|
||||
import HelloWorld from './HelloWorld.svelte'
|
||||
|
||||
it('looks right', () => {
|
||||
// styles are applied
|
||||
mount(HelloWorld)
|
||||
})
|
||||
```
|
||||
|
||||
> Note: Global styles can be imported in your component support file, allowing the styles to apply to all mounted components.
|
||||
|
||||
## Compatibility
|
||||
|
||||
| @cypress/svelte | cypress |
|
||||
| -------------- | ------- |
|
||||
| >= v1 | >= v10 |
|
||||
|
||||
## Development
|
||||
|
||||
Run `yarn build` to compile and sync packages to the `cypress` cli package.
|
||||
|
||||
Run `yarn cy:open` to open Cypress component testing against real-world examples.
|
||||
|
||||
Run `yarn test` to execute headless Cypress tests.
|
||||
|
||||
## License
|
||||
|
||||
[](https://github.com/cypress-io/cypress/blob/master/LICENSE)
|
||||
|
||||
This project is licensed under the terms of the [MIT license](/LICENSE).
|
||||
|
||||
## [Changelog](./CHANGELOG.md)
|
||||
43
npm/svelte/package.json
Normal file
43
npm/svelte/package.json
Normal file
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"name": "@cypress/svelte",
|
||||
"version": "0.0.0-development",
|
||||
"description": "Browser-based Component Testing for Svelte.js with Cypress.io 🧡",
|
||||
"main": "dist/cypress-svelte.cjs.js",
|
||||
"scripts": {
|
||||
"prebuild": "rimraf dist",
|
||||
"build": "rollup -c rollup.config.mjs",
|
||||
"postbuild": "node ../../scripts/sync-exported-npm-with-cli.js",
|
||||
"build-prod": "yarn build",
|
||||
"check-ts": "tsc --noEmit"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cypress/mount-utils": "0.0.0-development",
|
||||
"svelte": "^3.49.0",
|
||||
"typescript": "^4.7.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"cypress": ">=10.6.0",
|
||||
"svelte": ">=3.0.0"
|
||||
},
|
||||
"files": [
|
||||
"dist/**/*"
|
||||
],
|
||||
"types": "dist/index.d.ts",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/cypress-io/cypress.git"
|
||||
},
|
||||
"homepage": "https://github.com/cypress-io/cypress/blob/master/npm/svelte/#readme",
|
||||
"bugs": "https://github.com/cypress-io/cypress/issues/new?assignees=&labels=npm%3A%20%40cypress%2Fsvelte&template=1-bug-report.md&title=",
|
||||
"keywords": [
|
||||
"cypress",
|
||||
"svelte",
|
||||
"testing",
|
||||
"component testing"
|
||||
],
|
||||
"module": "dist/cypress-svelte.esm-bundler.js",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
}
|
||||
3
npm/svelte/rollup.config.mjs
Normal file
3
npm/svelte/rollup.config.mjs
Normal file
@@ -0,0 +1,3 @@
|
||||
import { createEntries } from '@cypress/mount-utils/create-rollup-entry.mjs'
|
||||
|
||||
export default createEntries({ formats: ['es', 'cjs'], input: 'src/index.ts' })
|
||||
1
npm/svelte/src/index.ts
Normal file
1
npm/svelte/src/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './mount'
|
||||
100
npm/svelte/src/mount.ts
Normal file
100
npm/svelte/src/mount.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
import {
|
||||
injectStylesBeforeElement,
|
||||
StyleOptions,
|
||||
getContainerEl,
|
||||
setupHooks,
|
||||
} from '@cypress/mount-utils'
|
||||
import type { ComponentConstructorOptions, ComponentProps, SvelteComponent } from 'svelte'
|
||||
|
||||
const DEFAULT_COMP_NAME = 'unknown'
|
||||
|
||||
type SvelteConstructor<T> = new (...args: any[]) => T;
|
||||
type SvelteComponentOptions<T extends SvelteComponent> = Omit<
|
||||
ComponentConstructorOptions<ComponentProps<T>>,
|
||||
'hydrate' | 'target' | '$$inline'
|
||||
>;
|
||||
|
||||
export interface MountOptions<T extends SvelteComponent>
|
||||
extends SvelteComponentOptions<T>,
|
||||
Partial<StyleOptions> {
|
||||
log?: boolean
|
||||
}
|
||||
|
||||
export interface MountReturn<T extends SvelteComponent> {
|
||||
component: T
|
||||
}
|
||||
|
||||
let componentInstance: SvelteComponent | undefined
|
||||
|
||||
const cleanup = () => {
|
||||
componentInstance?.$destroy()
|
||||
}
|
||||
|
||||
// Extract the component name from the object passed to mount
|
||||
const getComponentDisplayName = <T extends SvelteComponent>(Component: SvelteConstructor<T>): string => {
|
||||
if (Component.name) {
|
||||
const [_, match] = /Proxy\<(\w+)\>/.exec(Component.name) || []
|
||||
|
||||
return match || Component.name
|
||||
}
|
||||
|
||||
return DEFAULT_COMP_NAME
|
||||
}
|
||||
|
||||
/**
|
||||
* Mounts a Svelte component inside the Cypress browser
|
||||
*
|
||||
* @param {SvelteConstructor<T>} Component Svelte component being mounted
|
||||
* @param {MountReturn<T extends SvelteComponent>} options options to customize the component being mounted
|
||||
* @returns Cypress.Chainable<MountReturn>
|
||||
*
|
||||
* @example
|
||||
* import Counter from './Counter.svelte'
|
||||
* import { mount } from 'cypress/svelte'
|
||||
*
|
||||
* it('should render', () => {
|
||||
* mount(Counter, { props: { count: 42 } })
|
||||
* cy.get('button').contains(42)
|
||||
* })
|
||||
*/
|
||||
export function mount<T extends SvelteComponent> (
|
||||
Component: SvelteConstructor<T>,
|
||||
options: MountOptions<T> = {},
|
||||
): Cypress.Chainable<MountReturn<T>> {
|
||||
return cy.then(() => {
|
||||
const target = getContainerEl()
|
||||
|
||||
injectStylesBeforeElement(options, document, target)
|
||||
|
||||
const ComponentConstructor = ((Component as any).default || Component) as SvelteConstructor<T>
|
||||
|
||||
componentInstance = new ComponentConstructor({
|
||||
target,
|
||||
...options,
|
||||
})
|
||||
|
||||
// by waiting, we are delaying test execution for the next tick of event loop
|
||||
// and letting hooks and component lifecycle methods to execute mount
|
||||
return cy.wait(0, { log: false }).then(() => {
|
||||
if (options.log !== false) {
|
||||
const mountMessage = `<${getComponentDisplayName(Component)} ... />`
|
||||
|
||||
Cypress.log({
|
||||
name: 'mount',
|
||||
message: [mountMessage],
|
||||
}).snapshot('mounted').end()
|
||||
}
|
||||
})
|
||||
.wrap({ component: componentInstance as T }, { log: false })
|
||||
})
|
||||
}
|
||||
|
||||
// Side effects from "import { mount } from '@cypress/<my-framework>'" are annoying, we should avoid doing this
|
||||
// by creating an explicit function/import that the user can register in their 'component.js' support file,
|
||||
// such as:
|
||||
// import 'cypress/<my-framework>/support'
|
||||
// or
|
||||
// import { registerCT } from 'cypress/<my-framework>'
|
||||
// registerCT()
|
||||
// Note: This would be a breaking change
|
||||
setupHooks(cleanup)
|
||||
22
npm/svelte/tsconfig.json
Normal file
22
npm/svelte/tsconfig.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es6",
|
||||
"module": "commonjs",
|
||||
"skipLibCheck": true,
|
||||
"lib": [
|
||||
"es2015",
|
||||
"dom"
|
||||
],
|
||||
"allowJs": true,
|
||||
"jsx": "preserve",
|
||||
"outDir": "dist",
|
||||
"strict": true,
|
||||
"baseUrl": "./",
|
||||
"types": [
|
||||
"cypress"
|
||||
],
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"esModuleInterop": true
|
||||
},
|
||||
"include": ["src"],
|
||||
}
|
||||
@@ -26,8 +26,8 @@
|
||||
"dedent": "^0.7.0",
|
||||
"mocha": "^9.2.2",
|
||||
"sinon": "^13.0.1",
|
||||
"ts-node": "^10.2.1",
|
||||
"vite": "3.0.3",
|
||||
"ts-node": "^10.9.1",
|
||||
"vite": "3.1.0",
|
||||
"vite-plugin-inspect": "0.4.3"
|
||||
},
|
||||
"files": [
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
# [@cypress/vue-v4.2.0](https://github.com/cypress-io/cypress/compare/@cypress/vue-v4.1.0...@cypress/vue-v4.2.0) (2022-08-30)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* adding svelte component testing support ([#23553](https://github.com/cypress-io/cypress/issues/23553)) ([f6eaad4](https://github.com/cypress-io/cypress/commit/f6eaad40e1836fa9db87c60defa5ae6f390c8fd8))
|
||||
|
||||
# [@cypress/vue-v4.1.0](https://github.com/cypress-io/cypress/compare/@cypress/vue-v4.0.0...@cypress/vue-v4.1.0) (2022-08-11)
|
||||
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"build-prod": "yarn build",
|
||||
"cy:open": "node ../../scripts/cypress.js open --component --project ${PWD}",
|
||||
"cy:run": "node ../../scripts/cypress.js run --component --project ${PWD}",
|
||||
"build": "rimraf dist && rollup -c rollup.config.js",
|
||||
"build": "rimraf dist && rollup -c rollup.config.mjs",
|
||||
"postbuild": "node --require @packages/ts/register ./inline-types.ts && node ../../scripts/sync-exported-npm-with-cli.js",
|
||||
"typecheck": "yarn tsd && vue-tsc --noEmit",
|
||||
"test": "yarn cy:run",
|
||||
@@ -16,8 +16,6 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cypress/mount-utils": "0.0.0-development",
|
||||
"@rollup/plugin-commonjs": "^17.1.0",
|
||||
"@rollup/plugin-node-resolve": "^11.1.1",
|
||||
"@vitejs/plugin-vue": "2.3.1",
|
||||
"@vue/compiler-sfc": "3.2.31",
|
||||
"@vue/test-utils": "2.0.2",
|
||||
@@ -25,12 +23,9 @@
|
||||
"cypress": "0.0.0-development",
|
||||
"debug": "^4.3.2",
|
||||
"globby": "^11.0.1",
|
||||
"rollup": "^2.38.5",
|
||||
"rollup-plugin-istanbul": "2.0.1",
|
||||
"rollup-plugin-typescript2": "^0.29.0",
|
||||
"tailwindcss": "1.1.4",
|
||||
"typescript": "^4.2.3",
|
||||
"vite": "3.0.3",
|
||||
"typescript": "^4.7.4",
|
||||
"vite": "3.1.0",
|
||||
"vue": "3.2.31",
|
||||
"vue-i18n": "9.0.0-rc.6",
|
||||
"vue-router": "^4.0.0",
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
import ts from 'rollup-plugin-typescript2'
|
||||
import resolve from '@rollup/plugin-node-resolve'
|
||||
import commonjs from '@rollup/plugin-commonjs'
|
||||
|
||||
import pkg from './package.json'
|
||||
|
||||
const banner = `
|
||||
/**
|
||||
* ${pkg.name} v${pkg.version}
|
||||
* (c) ${new Date().getFullYear()} Cypress.io
|
||||
* Released under the MIT License
|
||||
*/
|
||||
`
|
||||
|
||||
function createEntry (options) {
|
||||
const {
|
||||
format,
|
||||
input,
|
||||
} = options
|
||||
|
||||
const config = {
|
||||
input,
|
||||
external: [
|
||||
'vue',
|
||||
],
|
||||
plugins: [
|
||||
resolve({ preferBuiltins: true }), commonjs(),
|
||||
],
|
||||
output: {
|
||||
banner,
|
||||
name: 'CypressVue',
|
||||
file: pkg.unpkg,
|
||||
format,
|
||||
globals: {
|
||||
vue: 'Vue',
|
||||
},
|
||||
exports: 'auto',
|
||||
},
|
||||
}
|
||||
|
||||
if (input === 'src/index.ts') {
|
||||
if (format === 'es') {
|
||||
config.output.file = pkg.module
|
||||
}
|
||||
|
||||
if (format === 'cjs') {
|
||||
config.output.file = pkg.main
|
||||
}
|
||||
} else {
|
||||
config.output.file = input.replace(/^src\//, 'dist/')
|
||||
}
|
||||
|
||||
console.log(`Building ${format}: ${config.output.file}`)
|
||||
|
||||
config.plugins.push(
|
||||
ts({
|
||||
check: false,
|
||||
tsconfigOverride: {
|
||||
compilerOptions: {
|
||||
declaration: format === 'es',
|
||||
noEmit: false,
|
||||
module: format === 'cjs' ? 'es2015' : 'esnext',
|
||||
},
|
||||
exclude: ['cypress/component'],
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
export default [
|
||||
createEntry({ format: 'es', input: 'src/index.ts' }),
|
||||
createEntry({ format: 'cjs', input: 'src/index.ts' }),
|
||||
]
|
||||
14
npm/vue/rollup.config.mjs
Normal file
14
npm/vue/rollup.config.mjs
Normal file
@@ -0,0 +1,14 @@
|
||||
import { createEntries } from '@cypress/mount-utils/create-rollup-entry.mjs'
|
||||
|
||||
const config = {
|
||||
external: [
|
||||
'vue',
|
||||
],
|
||||
output: {
|
||||
globals: {
|
||||
vue: 'Vue',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export default createEntries({ formats: ['es', 'cjs'], input: 'src/index.ts', config })
|
||||
@@ -1,3 +1,10 @@
|
||||
# [@cypress/vue2-v1.1.0](https://github.com/cypress-io/cypress/compare/@cypress/vue2-v1.0.2...@cypress/vue2-v1.1.0) (2022-08-30)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* adding svelte component testing support ([#23553](https://github.com/cypress-io/cypress/issues/23553)) ([f6eaad4](https://github.com/cypress-io/cypress/commit/f6eaad40e1836fa9db87c60defa5ae6f390c8fd8))
|
||||
|
||||
# [@cypress/vue2-v1.0.2](https://github.com/cypress-io/cypress/compare/@cypress/vue2-v1.0.1...@cypress/vue2-v1.0.2) (2022-08-11)
|
||||
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"main": "dist/cypress-vue2.cjs.js",
|
||||
"scripts": {
|
||||
"typecheck": "tsc --noEmit",
|
||||
"build": "rimraf dist && yarn rollup -c rollup.config.js",
|
||||
"build": "rimraf dist && yarn rollup -c rollup.config.mjs",
|
||||
"postbuild": "node ../../scripts/sync-exported-npm-with-cli.js",
|
||||
"build-prod": "yarn build",
|
||||
"test": "echo \"Tests for @cypress/vue2 are run from system-tests\"",
|
||||
@@ -17,13 +17,10 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cypress/mount-utils": "0.0.0-development",
|
||||
"@rollup/plugin-commonjs": "^17.1.0",
|
||||
"@rollup/plugin-json": "^4.1.0",
|
||||
"@rollup/plugin-node-resolve": "^11.1.1",
|
||||
"@rollup/plugin-replace": "^2.3.1",
|
||||
"rollup-plugin-typescript2": "^0.29.0",
|
||||
"tslib": "^2.1.0",
|
||||
"typescript": "^4.2.3",
|
||||
"typescript": "^4.7.4",
|
||||
"vue": "2.6.12"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
import ts from 'rollup-plugin-typescript2'
|
||||
import resolve from '@rollup/plugin-node-resolve'
|
||||
import commonjs from '@rollup/plugin-commonjs'
|
||||
import json from '@rollup/plugin-json'
|
||||
import replace from '@rollup/plugin-replace'
|
||||
|
||||
import pkg from './package.json'
|
||||
|
||||
const banner = `
|
||||
/**
|
||||
* ${pkg.name} v${pkg.version}
|
||||
* (c) ${new Date().getFullYear()} Cypress.io
|
||||
* Released under the MIT License
|
||||
*/
|
||||
`
|
||||
|
||||
function createEntry (options) {
|
||||
const {
|
||||
format,
|
||||
input,
|
||||
isBrowser,
|
||||
} = options
|
||||
|
||||
const config = {
|
||||
input,
|
||||
external: [
|
||||
'vue',
|
||||
],
|
||||
plugins: [
|
||||
resolve({ preferBuiltins: true }),
|
||||
commonjs(),
|
||||
json(),
|
||||
/**
|
||||
* Vue 2 core tries to load require.resolve(`./package.json`) in the browser for the
|
||||
* sole purpose of throwing an error about Vue Loader.
|
||||
* Just truncate this for now for simplicity.
|
||||
*/
|
||||
replace({
|
||||
'vueVersion && vueVersion !== packageVersion': JSON.stringify(false),
|
||||
}),
|
||||
],
|
||||
output: {
|
||||
banner,
|
||||
name: 'CypressVue2',
|
||||
file: pkg.unpkg,
|
||||
format,
|
||||
globals: {
|
||||
vue: 'Vue',
|
||||
},
|
||||
exports: 'auto',
|
||||
},
|
||||
}
|
||||
|
||||
if (input === 'src/index.ts') {
|
||||
if (format === 'es') {
|
||||
config.output.file = pkg.module
|
||||
if (isBrowser) {
|
||||
config.output.file = pkg.unpkg
|
||||
}
|
||||
}
|
||||
|
||||
if (format === 'cjs') {
|
||||
config.output.file = pkg.main
|
||||
}
|
||||
} else {
|
||||
config.output.file = input.replace(/^src\//, 'dist/')
|
||||
}
|
||||
|
||||
console.log(`Building ${format}: ${config.output.file}`)
|
||||
|
||||
config.plugins.push(
|
||||
ts({
|
||||
check: format === 'es' && isBrowser,
|
||||
tsconfigOverride: {
|
||||
compilerOptions: {
|
||||
declaration: format === 'es',
|
||||
target: 'es5', // not sure what this should be?
|
||||
module: format === 'cjs' ? 'es2015' : 'esnext',
|
||||
},
|
||||
exclude: ['tests'],
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
export default [
|
||||
createEntry({ format: 'es', input: 'src/index.ts', isBrowser: false }),
|
||||
createEntry({ format: 'es', input: 'src/index.ts', isBrowser: true }),
|
||||
createEntry({ format: 'iife', input: 'src/index.ts', isBrowser: true }),
|
||||
createEntry({ format: 'cjs', input: 'src/index.ts', isBrowser: false }),
|
||||
]
|
||||
28
npm/vue2/rollup.config.mjs
Normal file
28
npm/vue2/rollup.config.mjs
Normal file
@@ -0,0 +1,28 @@
|
||||
import { createEntries } from '@cypress/mount-utils/create-rollup-entry.mjs'
|
||||
import json from '@rollup/plugin-json'
|
||||
import replace from '@rollup/plugin-replace'
|
||||
|
||||
const config = {
|
||||
external: [
|
||||
'vue',
|
||||
],
|
||||
plugins: [
|
||||
json(),
|
||||
/**
|
||||
* Vue 2 core tries to load require.resolve(`./package.json`) in the browser for the
|
||||
* sole purpose of throwing an error about Vue Loader.
|
||||
* Just truncate this for now for simplicity.
|
||||
*/
|
||||
replace({
|
||||
'vueVersion && vueVersion !== packageVersion': JSON.stringify(false),
|
||||
preventAssignment: false,
|
||||
}),
|
||||
],
|
||||
output: {
|
||||
globals: {
|
||||
vue: 'Vue',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export default createEntries({ formats: ['es', 'cjs'], input: 'src/index.ts', config })
|
||||
@@ -19,7 +19,7 @@
|
||||
"coffee-loader": "^0.9.0",
|
||||
"coffeescript": "^1.12.7",
|
||||
"pnp-webpack-plugin": "^1.7.0",
|
||||
"ts-loader": "^8.0.2",
|
||||
"ts-loader": "8.4.0",
|
||||
"tsconfig-package": "npm:tsconfig@^7.0.0",
|
||||
"tsconfig-paths-webpack-plugin": "^3.3.0",
|
||||
"webpack": "^4.44.2"
|
||||
@@ -41,7 +41,7 @@
|
||||
"graphql": "14.0.0",
|
||||
"mocha": "^8.1.1",
|
||||
"react": "^16.13.1",
|
||||
"typescript": "^4.2.3"
|
||||
"typescript": "^4.7.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@cypress/webpack-preprocessor": "^5.4.4"
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
# [@cypress/webpack-dev-server-v2.3.0](https://github.com/cypress-io/cypress/compare/@cypress/webpack-dev-server-v2.2.0...@cypress/webpack-dev-server-v2.3.0) (2022-08-30)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **webpack-dev-server:** add custom project config to handler ([a07a2a1](https://github.com/cypress-io/cypress/commit/a07a2a118d7b62b90e790ef475c86959ae894b3b))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* adding svelte component testing support ([#23553](https://github.com/cypress-io/cypress/issues/23553)) ([f6eaad4](https://github.com/cypress-io/cypress/commit/f6eaad40e1836fa9db87c60defa5ae6f390c8fd8))
|
||||
|
||||
# [@cypress/webpack-dev-server-v2.2.0](https://github.com/cypress-io/cypress/compare/@cypress/webpack-dev-server-v2.1.0...@cypress/webpack-dev-server-v2.2.0) (2022-08-15)
|
||||
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
"proxyquire": "2.1.3",
|
||||
"sinon": "^13.0.1",
|
||||
"snap-shot-it": "^7.9.6",
|
||||
"ts-node": "^10.2.1",
|
||||
"ts-node": "^10.9.1",
|
||||
"webpack": "npm:webpack@^5",
|
||||
"webpack-4": "npm:webpack@^4",
|
||||
"webpack-dev-server-3": "npm:webpack-dev-server@^3"
|
||||
|
||||
@@ -16,17 +16,24 @@ import { angularHandler } from './helpers/angularHandler'
|
||||
|
||||
const debug = debugLib('cypress:webpack-dev-server:devServer')
|
||||
|
||||
export type Frameworks = Extract<Cypress.DevServerConfigOptions, { bundler: 'webpack' }>['framework']
|
||||
|
||||
type FrameworkConfig = {
|
||||
framework?: Exclude<Frameworks, 'angular'>
|
||||
} | {
|
||||
framework: 'angular'
|
||||
options?: {
|
||||
projectConfig: Cypress.AngularDevServerProjectConfig
|
||||
}
|
||||
}
|
||||
|
||||
export type WebpackDevServerConfig = {
|
||||
specs: Cypress.Spec[]
|
||||
cypressConfig: Cypress.PluginConfigOptions
|
||||
devServerEvents: NodeJS.EventEmitter
|
||||
onConfigNotFound?: (devServer: 'webpack', cwd: string, lookedIn: string[]) => void
|
||||
} & {
|
||||
framework?: typeof ALL_FRAMEWORKS[number] // Add frameworks here as we implement
|
||||
webpackConfig?: unknown // Derived from the user's webpack
|
||||
}
|
||||
|
||||
export const ALL_FRAMEWORKS = ['create-react-app', 'nuxt', 'react', 'vue-cli', 'next', 'vue', 'angular'] as const
|
||||
} & FrameworkConfig
|
||||
|
||||
/**
|
||||
* @internal
|
||||
@@ -121,11 +128,12 @@ async function getPreset (devServerConfig: WebpackDevServerConfig): Promise<Opti
|
||||
|
||||
case 'react':
|
||||
case 'vue':
|
||||
case 'svelte':
|
||||
case undefined:
|
||||
return { sourceWebpackModulesResult: sourceDefaultWebpackDependencies(devServerConfig) }
|
||||
|
||||
default:
|
||||
throw new Error(`Unexpected framework ${devServerConfig.framework}, expected one of ${ALL_FRAMEWORKS.join(', ')}`)
|
||||
throw new Error(`Unexpected framework ${(devServerConfig as any).framework}, please visit https://docs.cypress.io/guides/component-testing/component-framework-configuration to see a list of supported frameworks`)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,19 +5,22 @@ import { pathToFileURL } from 'url'
|
||||
import type { PresetHandlerResult, WebpackDevServerConfig } from '../devServer'
|
||||
import { sourceDefaultWebpackDependencies } from './sourceRelativeWebpackModules'
|
||||
|
||||
export type BuildOptions = Record<string, any>
|
||||
|
||||
export type AngularWebpackDevServerConfig = Extract<WebpackDevServerConfig, {framework: 'angular'}>
|
||||
|
||||
type Configurations = {
|
||||
configurations?: {
|
||||
[configuration: string]: BuildOptions
|
||||
}
|
||||
}
|
||||
|
||||
export type AngularJsonProjectConfig = {
|
||||
projectType: string
|
||||
root: string
|
||||
sourceRoot: string
|
||||
architect: {
|
||||
build: {
|
||||
options: { [key: string]: any } & { polyfills?: string }
|
||||
configurations?: {
|
||||
[configuration: string]: {
|
||||
[key: string]: any
|
||||
}
|
||||
}
|
||||
}
|
||||
build: { options: BuildOptions } & Configurations
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,21 +33,7 @@ type AngularJson = {
|
||||
|
||||
const dynamicImport = new Function('specifier', 'return import(specifier)')
|
||||
|
||||
export async function angularHandler (devServerConfig: WebpackDevServerConfig): Promise<PresetHandlerResult> {
|
||||
const webpackConfig = await getAngularCliWebpackConfig(devServerConfig)
|
||||
|
||||
return { frameworkConfig: webpackConfig, sourceWebpackModulesResult: sourceDefaultWebpackDependencies(devServerConfig) }
|
||||
}
|
||||
|
||||
async function getAngularCliWebpackConfig (devServerConfig: WebpackDevServerConfig) {
|
||||
const { projectRoot } = devServerConfig.cypressConfig
|
||||
|
||||
const {
|
||||
generateBrowserWebpackConfigFromContext,
|
||||
getCommonConfig,
|
||||
getStylesConfig,
|
||||
} = await getAngularCliModules(projectRoot)
|
||||
|
||||
export async function getProjectConfig (projectRoot: string): Promise<Cypress.AngularDevServerProjectConfig> {
|
||||
const angularJson = await getAngularJson(projectRoot)
|
||||
|
||||
let { defaultProject } = angularJson
|
||||
@@ -53,30 +42,26 @@ async function getAngularCliWebpackConfig (devServerConfig: WebpackDevServerConf
|
||||
defaultProject = Object.keys(angularJson.projects).find((name) => angularJson.projects[name].projectType === 'application')
|
||||
|
||||
if (!defaultProject) {
|
||||
throw new Error('Could not find a project with projectType "application" in "angular.json"')
|
||||
throw new Error('Could not find a project with projectType "application" in "angular.json". Visit https://docs.cypress.io/guides/references/configuration#Options-API to see how to pass in a custom project configuration')
|
||||
}
|
||||
}
|
||||
|
||||
const defaultProjectConfig = angularJson.projects[defaultProject]
|
||||
|
||||
const tsConfig = await generateTsConfig(devServerConfig, defaultProjectConfig)
|
||||
const { architect, root, sourceRoot } = defaultProjectConfig
|
||||
const { build } = architect
|
||||
|
||||
const buildOptions = getAngularBuildOptions(defaultProjectConfig, tsConfig)
|
||||
|
||||
const context = createFakeContext(projectRoot, defaultProject, defaultProjectConfig)
|
||||
|
||||
const { config } = await generateBrowserWebpackConfigFromContext(
|
||||
buildOptions,
|
||||
context,
|
||||
(wco: any) => [getCommonConfig(wco), getStylesConfig(wco)],
|
||||
)
|
||||
|
||||
delete config.entry.main
|
||||
|
||||
return config
|
||||
return {
|
||||
root,
|
||||
sourceRoot,
|
||||
buildOptions: {
|
||||
...build.options,
|
||||
...build.configurations?.development || {},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export function getAngularBuildOptions (projectConfig: AngularJsonProjectConfig, tsConfig: string) {
|
||||
export function getAngularBuildOptions (buildOptions: BuildOptions, tsConfig: string) {
|
||||
// Default options are derived from the @angular-devkit/build-angular browser builder, with some options from
|
||||
// the serve builder thrown in for development.
|
||||
// see: https://github.com/angular/angular-cli/blob/main/packages/angular_devkit/build_angular/src/builders/browser/schema.json
|
||||
@@ -115,8 +100,7 @@ export function getAngularBuildOptions (projectConfig: AngularJsonProjectConfig,
|
||||
extractLicenses: false,
|
||||
sourceMap: true,
|
||||
namedChunks: true,
|
||||
...projectConfig.architect.build.options,
|
||||
...projectConfig.architect.build.configurations?.development || {},
|
||||
...buildOptions,
|
||||
tsConfig,
|
||||
aot: false,
|
||||
outputHashing: 'none',
|
||||
@@ -124,7 +108,7 @@ export function getAngularBuildOptions (projectConfig: AngularJsonProjectConfig,
|
||||
}
|
||||
}
|
||||
|
||||
export async function generateTsConfig (devServerConfig: WebpackDevServerConfig, projectConfig: AngularJsonProjectConfig): Promise<string> {
|
||||
export async function generateTsConfig (devServerConfig: AngularWebpackDevServerConfig, buildOptions: BuildOptions): Promise<string> {
|
||||
const { cypressConfig } = devServerConfig
|
||||
const { projectRoot } = cypressConfig
|
||||
|
||||
@@ -138,8 +122,8 @@ export async function generateTsConfig (devServerConfig: WebpackDevServerConfig,
|
||||
includePaths.push(toPosix(cypressConfig.supportFile))
|
||||
}
|
||||
|
||||
if (projectConfig.architect.build.options.polyfills) {
|
||||
const polyfills = getProjectFilePath(projectConfig.architect.build.options.polyfills)
|
||||
if (buildOptions.polyfills) {
|
||||
const polyfills = getProjectFilePath(buildOptions.polyfills)
|
||||
|
||||
includePaths.push(polyfills)
|
||||
}
|
||||
@@ -149,7 +133,7 @@ export async function generateTsConfig (devServerConfig: WebpackDevServerConfig,
|
||||
includePaths.push(cypressTypes)
|
||||
|
||||
const tsConfigContent = JSON.stringify({
|
||||
extends: getProjectFilePath('tsconfig.json'),
|
||||
extends: getProjectFilePath(buildOptions.tsConfig ?? 'tsconfig.json'),
|
||||
compilerOptions: {
|
||||
outDir: getProjectFilePath('out-tsc/cy'),
|
||||
allowSyntheticDefaultImports: true,
|
||||
@@ -215,14 +199,14 @@ export async function getAngularJson (projectRoot: string): Promise<AngularJson>
|
||||
return JSON.parse(angularJson)
|
||||
}
|
||||
|
||||
function createFakeContext (projectRoot: string, defaultProject: string, defaultProjectConfig: any) {
|
||||
function createFakeContext (projectRoot: string, defaultProjectConfig: Cypress.AngularDevServerProjectConfig) {
|
||||
const logger = {
|
||||
createChild: () => ({}),
|
||||
}
|
||||
|
||||
const context = {
|
||||
target: {
|
||||
project: defaultProject,
|
||||
project: 'angular',
|
||||
},
|
||||
workspaceRoot: projectRoot,
|
||||
getProjectMetadata: () => {
|
||||
@@ -239,3 +223,38 @@ function createFakeContext (projectRoot: string, defaultProject: string, default
|
||||
}
|
||||
|
||||
export const toPosix = (filePath: string) => filePath.split(path.sep).join(path.posix.sep)
|
||||
|
||||
async function getAngularCliWebpackConfig (devServerConfig: AngularWebpackDevServerConfig) {
|
||||
const { projectRoot } = devServerConfig.cypressConfig
|
||||
|
||||
const {
|
||||
generateBrowserWebpackConfigFromContext,
|
||||
getCommonConfig,
|
||||
getStylesConfig,
|
||||
} = await getAngularCliModules(projectRoot)
|
||||
|
||||
// normalize
|
||||
const projectConfig = devServerConfig.options?.projectConfig || await getProjectConfig(projectRoot)
|
||||
|
||||
const tsConfig = await generateTsConfig(devServerConfig, projectConfig.buildOptions)
|
||||
|
||||
const buildOptions = getAngularBuildOptions(projectConfig.buildOptions, tsConfig)
|
||||
|
||||
const context = createFakeContext(projectRoot, projectConfig)
|
||||
|
||||
const { config } = await generateBrowserWebpackConfigFromContext(
|
||||
buildOptions,
|
||||
context,
|
||||
(wco: any) => [getCommonConfig(wco), getStylesConfig(wco)],
|
||||
)
|
||||
|
||||
delete config.entry.main
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
export async function angularHandler (devServerConfig: AngularWebpackDevServerConfig): Promise<PresetHandlerResult> {
|
||||
const webpackConfig = await getAngularCliWebpackConfig(devServerConfig)
|
||||
|
||||
return { frameworkConfig: webpackConfig, sourceWebpackModulesResult: sourceDefaultWebpackDependencies(devServerConfig) }
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Module from 'module'
|
||||
import path from 'path'
|
||||
import type { WebpackDevServerConfig, ALL_FRAMEWORKS } from '../devServer'
|
||||
import type { Frameworks, WebpackDevServerConfig } from '../devServer'
|
||||
import debugFn from 'debug'
|
||||
|
||||
const debug = debugFn('cypress:webpack-dev-server:sourceRelativeWebpackModules')
|
||||
@@ -56,7 +56,7 @@ export const cypressWebpackPath = (config: WebpackDevServerConfig) => {
|
||||
})
|
||||
}
|
||||
|
||||
type FrameworkWebpackMapper = { [Property in typeof ALL_FRAMEWORKS[number]]: string | undefined }
|
||||
type FrameworkWebpackMapper = { [Property in Frameworks]: string | undefined }
|
||||
|
||||
const frameworkWebpackMapper: FrameworkWebpackMapper = {
|
||||
'create-react-app': 'react-scripts',
|
||||
@@ -66,6 +66,7 @@ const frameworkWebpackMapper: FrameworkWebpackMapper = {
|
||||
vue: undefined,
|
||||
next: 'next',
|
||||
'angular': '@angular-devkit/build-angular',
|
||||
'svelte': undefined,
|
||||
}
|
||||
|
||||
// Source the users framework from the provided projectRoot. The framework, if available, will serve
|
||||
|
||||
@@ -3,14 +3,15 @@ import chaiPromise from 'chai-as-promised'
|
||||
import * as fs from 'fs-extra'
|
||||
import cloneDeep from 'lodash/cloneDeep'
|
||||
import * as path from 'path'
|
||||
import { WebpackDevServerConfig } from '../../src/devServer'
|
||||
import {
|
||||
angularHandler,
|
||||
AngularJsonProjectConfig,
|
||||
AngularWebpackDevServerConfig,
|
||||
BuildOptions,
|
||||
generateTsConfig,
|
||||
getAngularBuildOptions,
|
||||
getAngularCliModules,
|
||||
getAngularJson,
|
||||
getProjectConfig,
|
||||
getTempDir,
|
||||
toPosix,
|
||||
} from '../../src/helpers/angularHandler'
|
||||
@@ -19,27 +20,6 @@ import { scaffoldMigrationProject } from '../test-helpers/scaffoldProject'
|
||||
|
||||
chai.use(chaiPromise)
|
||||
|
||||
const projectConfig: AngularJsonProjectConfig = {
|
||||
root: 'my-root',
|
||||
sourceRoot: 'my-root/src',
|
||||
projectType: 'application',
|
||||
architect: {
|
||||
build: {
|
||||
options: {
|
||||
aot: true,
|
||||
tsConfig: 'tsconfig.json',
|
||||
polyfills: 'src/polyfills.ts',
|
||||
optimization: true,
|
||||
},
|
||||
configurations: {
|
||||
development: {
|
||||
optimization: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
describe('angularHandler', function () {
|
||||
this.timeout(1000 * 60)
|
||||
|
||||
@@ -54,7 +34,7 @@ describe('angularHandler', function () {
|
||||
specPattern: 'src/**/*.cy.ts',
|
||||
} as Cypress.PluginConfigOptions,
|
||||
framework: 'angular',
|
||||
} as WebpackDevServerConfig
|
||||
} as AngularWebpackDevServerConfig
|
||||
|
||||
const { frameworkConfig: webpackConfig, sourceWebpackModulesResult } = await angularHandler(devServerConfig)
|
||||
|
||||
@@ -62,10 +42,12 @@ describe('angularHandler', function () {
|
||||
expect((webpackConfig?.entry as any).main).to.be.undefined
|
||||
expect(sourceWebpackModulesResult.framework?.importPath).to.include(path.join('@angular-devkit', 'build-angular'))
|
||||
|
||||
const { buildOptions } = await expectNormalizeProjectConfig(projectRoot)
|
||||
|
||||
await expectLoadsAngularJson(projectRoot)
|
||||
await expectLoadsAngularCLiModules(projectRoot)
|
||||
await expectGeneratesTsConfig(devServerConfig)
|
||||
expectLoadsAngularBuildOptions()
|
||||
await expectGeneratesTsConfig(devServerConfig, buildOptions)
|
||||
expectLoadsAngularBuildOptions(buildOptions)
|
||||
})
|
||||
|
||||
it('sources the config from angular-14', async () => {
|
||||
@@ -79,7 +61,58 @@ describe('angularHandler', function () {
|
||||
specPattern: 'src/**/*.cy.ts',
|
||||
} as Cypress.PluginConfigOptions,
|
||||
framework: 'angular',
|
||||
} as WebpackDevServerConfig
|
||||
} as AngularWebpackDevServerConfig
|
||||
|
||||
const { frameworkConfig: webpackConfig, sourceWebpackModulesResult } = await angularHandler(devServerConfig)
|
||||
|
||||
expect(webpackConfig).to.exist
|
||||
expect((webpackConfig?.entry as any).main).to.be.undefined
|
||||
expect(sourceWebpackModulesResult.framework?.importPath).to.include(path.join('@angular-devkit', 'build-angular'))
|
||||
|
||||
const { buildOptions } = await expectNormalizeProjectConfig(projectRoot)
|
||||
|
||||
await expectLoadsAngularJson(projectRoot)
|
||||
await expectLoadsAngularCLiModules(projectRoot)
|
||||
await expectGeneratesTsConfig(devServerConfig, buildOptions)
|
||||
expectLoadsAngularBuildOptions(buildOptions)
|
||||
})
|
||||
|
||||
it('allows custom project config', async () => {
|
||||
const customProjectConfig = {
|
||||
root: '',
|
||||
sourceRoot: 'src',
|
||||
buildOptions: {
|
||||
outputPath: 'dist/angular',
|
||||
index: 'src/index.html',
|
||||
main: 'src/main.ts',
|
||||
polyfills: 'src/polyfills.ts',
|
||||
tsConfig: 'tsconfig.app.json',
|
||||
inlineStyleLanguage: 'scss',
|
||||
assets: ['src/favicon.ico', 'src/assets'],
|
||||
styles: ['src/styles.scss'],
|
||||
scripts: [],
|
||||
buildOptimizer: false,
|
||||
optimization: false,
|
||||
vendorChunk: true,
|
||||
extractLicenses: false,
|
||||
sourceMap: true,
|
||||
namedChunks: true,
|
||||
},
|
||||
}
|
||||
const projectRoot = await scaffoldMigrationProject('angular-custom-config')
|
||||
|
||||
process.chdir(projectRoot)
|
||||
|
||||
const devServerConfig = {
|
||||
framework: 'angular',
|
||||
cypressConfig: {
|
||||
projectRoot,
|
||||
specPattern: 'src/**/*.cy.ts',
|
||||
} as Cypress.PluginConfigOptions,
|
||||
options: {
|
||||
projectConfig: customProjectConfig,
|
||||
},
|
||||
} as unknown as AngularWebpackDevServerConfig
|
||||
|
||||
const { frameworkConfig: webpackConfig, sourceWebpackModulesResult } = await angularHandler(devServerConfig)
|
||||
|
||||
@@ -89,11 +122,39 @@ describe('angularHandler', function () {
|
||||
|
||||
await expectLoadsAngularJson(projectRoot)
|
||||
await expectLoadsAngularCLiModules(projectRoot)
|
||||
await expectGeneratesTsConfig(devServerConfig)
|
||||
expectLoadsAngularBuildOptions()
|
||||
await expectGeneratesTsConfig(devServerConfig, customProjectConfig.buildOptions)
|
||||
expectLoadsAngularBuildOptions(customProjectConfig.buildOptions)
|
||||
})
|
||||
})
|
||||
|
||||
const expectNormalizeProjectConfig = async (projectRoot: string) => {
|
||||
const projectConfig = await getProjectConfig(projectRoot)
|
||||
|
||||
expect(projectConfig).to.deep.eq({
|
||||
root: '',
|
||||
sourceRoot: 'src',
|
||||
buildOptions: {
|
||||
outputPath: 'dist/angular',
|
||||
index: 'src/index.html',
|
||||
main: 'src/main.ts',
|
||||
polyfills: 'src/polyfills.ts',
|
||||
tsConfig: 'tsconfig.app.json',
|
||||
inlineStyleLanguage: 'scss',
|
||||
assets: ['src/favicon.ico', 'src/assets'],
|
||||
styles: ['src/styles.scss'],
|
||||
scripts: [],
|
||||
buildOptimizer: false,
|
||||
optimization: false,
|
||||
vendorChunk: true,
|
||||
extractLicenses: false,
|
||||
sourceMap: true,
|
||||
namedChunks: true,
|
||||
},
|
||||
})
|
||||
|
||||
return projectConfig
|
||||
}
|
||||
|
||||
const expectLoadsAngularJson = async (projectRoot: string) => {
|
||||
const angularJson = await getAngularJson(projectRoot)
|
||||
|
||||
@@ -112,29 +173,21 @@ const expectLoadsAngularCLiModules = async (projectRoot: string) => {
|
||||
await expect(getAngularCliModules(path.join('..', projectRoot))).to.be.rejected
|
||||
}
|
||||
|
||||
const expectLoadsAngularBuildOptions = () => {
|
||||
const expectLoadsAngularBuildOptions = (buildOptions: BuildOptions) => {
|
||||
const tsConfig = 'tsconfig.cypress.json'
|
||||
|
||||
let buildOptions = getAngularBuildOptions(projectConfig, tsConfig)
|
||||
let finalBuildOptions = getAngularBuildOptions(buildOptions, tsConfig)
|
||||
|
||||
expect(buildOptions.aot).to.be.false
|
||||
expect(buildOptions.optimization).to.be.false
|
||||
expect(buildOptions.tsConfig).to.equal(tsConfig)
|
||||
expect(buildOptions.outputHashing).to.equal('none')
|
||||
expect(buildOptions.budgets).to.be.undefined
|
||||
|
||||
const modifiedProjectConfig = cloneDeep(projectConfig)
|
||||
|
||||
delete modifiedProjectConfig.architect.build.configurations
|
||||
|
||||
buildOptions = getAngularBuildOptions(modifiedProjectConfig, tsConfig)
|
||||
|
||||
expect(buildOptions.optimization).to.be.true
|
||||
expect(finalBuildOptions.aot).to.be.false
|
||||
expect(finalBuildOptions.optimization).to.be.false
|
||||
expect(finalBuildOptions.tsConfig).to.equal(tsConfig)
|
||||
expect(finalBuildOptions.outputHashing).to.equal('none')
|
||||
expect(finalBuildOptions.budgets).to.be.undefined
|
||||
}
|
||||
|
||||
const expectGeneratesTsConfig = async (devServerConfig: WebpackDevServerConfig) => {
|
||||
const expectGeneratesTsConfig = async (devServerConfig: AngularWebpackDevServerConfig, buildOptions: any) => {
|
||||
const { projectRoot } = devServerConfig.cypressConfig
|
||||
let tsConfigPath = await generateTsConfig(devServerConfig, projectConfig)
|
||||
let tsConfigPath = await generateTsConfig(devServerConfig, buildOptions)
|
||||
const tempDir = await getTempDir()
|
||||
|
||||
expect(tsConfigPath).to.eq(path.join(tempDir, 'tsconfig.json'))
|
||||
@@ -142,7 +195,8 @@ const expectGeneratesTsConfig = async (devServerConfig: WebpackDevServerConfig)
|
||||
let tsConfig = JSON.parse(await fs.readFile(tsConfigPath, 'utf8'))
|
||||
|
||||
expect(tsConfig).to.deep.eq({
|
||||
extends: toPosix(path.join(projectRoot, 'tsconfig.json')),
|
||||
// verifies the default `tsconfig.app.json` is extended
|
||||
extends: toPosix(path.join(projectRoot, 'tsconfig.app.json')),
|
||||
compilerOptions: {
|
||||
outDir: toPosix(path.join(projectRoot, 'out-tsc/cy')),
|
||||
allowSyntheticDefaultImports: true,
|
||||
@@ -155,21 +209,31 @@ const expectGeneratesTsConfig = async (devServerConfig: WebpackDevServerConfig)
|
||||
],
|
||||
})
|
||||
|
||||
const modifiedProjectConfig = cloneDeep(projectConfig)
|
||||
const modifiedBuildOptions = cloneDeep(buildOptions)
|
||||
|
||||
delete modifiedProjectConfig.architect.build.options.polyfills
|
||||
delete modifiedBuildOptions.polyfills
|
||||
modifiedBuildOptions.tsConfig = 'tsconfig.cy.json'
|
||||
|
||||
const modifiedDevServerConfig = cloneDeep(devServerConfig)
|
||||
const supportFile = path.join(projectRoot, 'cypress', 'support', 'component.ts')
|
||||
|
||||
modifiedDevServerConfig.cypressConfig.supportFile = supportFile
|
||||
|
||||
tsConfigPath = await generateTsConfig(modifiedDevServerConfig, modifiedProjectConfig)
|
||||
tsConfigPath = await generateTsConfig(modifiedDevServerConfig, modifiedBuildOptions)
|
||||
tsConfig = JSON.parse(await fs.readFile(tsConfigPath, 'utf8'))
|
||||
|
||||
expect(tsConfig.include).to.deep.equal([
|
||||
toPosix(path.join(projectRoot, 'src/**/*.cy.ts')),
|
||||
toPosix(supportFile),
|
||||
toPosix(path.join(projectRoot, 'node_modules/cypress/types/index.d.ts')),
|
||||
])
|
||||
expect(tsConfig).to.deep.eq({
|
||||
// verifies the custom `tsconfig.cy.json` is extended
|
||||
extends: toPosix(path.join(projectRoot, 'tsconfig.cy.json')),
|
||||
compilerOptions: {
|
||||
outDir: toPosix(path.join(projectRoot, 'out-tsc/cy')),
|
||||
allowSyntheticDefaultImports: true,
|
||||
skipLibCheck: true,
|
||||
},
|
||||
include: [
|
||||
toPosix(path.join(projectRoot, 'src/**/*.cy.ts')),
|
||||
toPosix(supportFile),
|
||||
toPosix(path.join(projectRoot, 'node_modules/cypress/types/index.d.ts')),
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Fixtures, { ProjectFixtureDir } from '@tooling/system-tests'
|
||||
import * as FixturesScaffold from '@tooling/system-tests/lib/dep-installer'
|
||||
|
||||
export async function scaffoldMigrationProject (project: ProjectFixtureDir) {
|
||||
export async function scaffoldMigrationProject (project: ProjectFixtureDir): Promise<string> {
|
||||
Fixtures.removeProject(project)
|
||||
|
||||
await Fixtures.scaffoldProject(project)
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
"types": "tsc --noEmit --lib es2015,dom --types cypress cypress/e2e/*.ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"ts-loader": "7.0.4",
|
||||
"typescript": "^4.2.3"
|
||||
"ts-loader": "8.4.0",
|
||||
"typescript": "^4.7.4"
|
||||
},
|
||||
"license": "ISC",
|
||||
"keywords": []
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user