test: update tests, add github actions reporter (#7238)

* make jest tests silent

* fix path warning in tests

* don't hide build/ or node_modules/

* fix createUploadHandler test

* fix conversation handler test

* use github actions reporter for test results

* include test location in output

* move jest dep into test-utils and use correct reporter name

* ignore tests in dist

* fix up botProject open handle

* bump browserslist and update caniuse-lite

* use node env for node tests

supposedly this should help speed up tests that do not require jsdom

* perpare server for @swc-node/jest

* prepare client for @swc-node/jest

* switch to swc for transpiling ts in tests

* fix compiler errors

* removed unused var

* special case the fallback recognizer (#7634)

* chore: bump wait-on to v5.3.0 (#7648)

* fix: update left nav and top bar strings (#7609)

* start fixing header

* adjust header chrome and left nav order

* Update en-US.json

* fix typecheck errors

* fix unit tests

* Update en-US.json

* change Design to Create in e2e tests

* Update LuisDeploy.spec.ts

* update flaky check in visitPage

* add 'checked' flag to visitPage to minimize surface area

* fix punctuation

* fix e2e test!

* fix security issue

* Revert "fix security issue"

This reverts commit 3aa6e3e4e36a951bb37244e01dd411fa4eca10dd.

Co-authored-by: Chris Whitten <christopher.whitten@microsoft.com>

* fix: rephrase Enable Orchestrator dialog (#7639)

* "Add a skill" page changes

* Update EnableOrchestrator.tsx

* Update skill.test.tsx

* Update package.json

* Update yarn.lock

* Update en-US.json

* fix: Update provision & publishing copy (#7583) (#7642)

* Issue-7583: update copy for provision and publishing

* en-us json files

* Issue-7583: fix formatDialogTitle

* Issue-7583: refactor formatDialogTitle

* Issue-7583: reformat azure provision dialog block

* PR fixes

* Fixed spacing

Co-authored-by: GeoffCoxMSFT <gcox@microsoft.com>

* fix: Added cleanup logic to app on quit (#7645)

* Added cleanup logic to app on quit

* Added missing electron ipc event listener

Co-authored-by: TJ Durnford <tjdford@gmail.com>
Co-authored-by: Soroush <hatpick@gmail.com>

* feat: show tunneling information notification when starting bot with remote skills (#7611)

* add util to get device OS

* add port to local publish result

* add port to botEndpointsState

* show ngrok notification when starting a bot with remote skills

* only show the remote skills notification onces per session per bot

* update l10n file

* address feedback

* update locale file after merge

* fix failing test

Co-authored-by: Soroush <hatpick@gmail.com>

* feat: detect "old" bots and migrate them to new runtime (#6526)

* introduce a migration system for updating legacy projcts to the 2.0 runtime

* updated

* ensure root dialog gets updated appropriately

* adjust method for updating root dialog

* remove comments

* fire migration warning when loading bot

* use original project name for new project

* close modal on submit

* address feedback

* add confirm modal

* Rename azureFunctionsPublish publish targets to azurePublish (new shared id)

* Delete a.en-us.lu

* Delete a.en-us.qna

* Delete b.en-us.lu

* Delete b.en-us.qna

* Delete bot1.en-us.lu

* Delete bot1.en-us.qna

* do not require pva to migrate

* Add missing parameters

* clarify types

* Fix tests
reduce redundant code

* Fixes #6844: migrate appinsights key to new location

* fix migrate lint issue

* plumb through the yeomanOptions parameter, required to set the location of the settings folder when migrating

* feat: new validation pipeline - schema existence validation (#7001)

* add placeholder for schema validator

* add schema validator pipeline with mocked fn

* add schema visitor

* display diagnostics data in debug panel

* revert sdk.ts

* decrease schema diagnostic severity to 'Warning'

* optmize path join logic

* impl a unified walker covers SwitchCondition

* fix lint error: use BaseSchema

* feat: disable actions without schema

* wrap in useEffect

* optimization: avoid frequent recoil submission

* optimization: aggregate paths rather than updatedDialog to reduce time complexity

* chore: comments & var name

* lint

* add comments

* defense undefined skip-level 'actions'

* defense potential exceptions

* get sdk.schema content correctly

* fix lint

* fix folder name case problem

* Do not specify the luis endpoint key as a parameter to the runtime if no vlaue is present (#7240)

(leaving this paramter blank causes issues on windows)

* disable telemetry calls in the provision dialog while we investigate why telemetryclient is null (#7256)

Co-authored-by: Geoff Cox (Microsoft) <gcox@microsoft.com>

* prefer the botName field instead of the name field when managing connections (#7262)

* fix: Empty Webchat inspector text and Disabling items in PVA context (#7241)

* show floating notifications over eveything (#7269)

Co-authored-by: Soroush <sorgh@microsoft.com>

* Region for Microsoft Bot Channels Registration is now global (#7270)

Co-authored-by: Ben Brown <benbro@microsoft.com>
Co-authored-by: Soroush <hatpick@gmail.com>

* fix: adjust package manager feeds (#7243)

* Fix #7092: set default page size to 100 items

* Fixes #6854: merge community feeds into main feed, sort by downloads

* Fixes #7043: include any component tagged msbot-component

* improve error handling

* restore different checks for declarative only vs code driven components

* refactor to use includes instead of indexOf

Co-authored-by: Ben Yackley <61990921+beyackle@users.noreply.github.com>

* fix: remodel About page (#7191)

* remodel About page

* add SHA to version

* fixes from suggestion

* get info from Electron and use it for Release field

Co-authored-by: Chris Whitten <christopher.whitten@microsoft.com>

Co-authored-by: Chris Whitten <christopher.whitten@microsoft.com>
Co-authored-by: Ben Brown <benbro@microsoft.com>
Co-authored-by: Geoff Cox (Microsoft) <gcox@microsoft.com>
Co-authored-by: Srinaath Ravichandran <srinaath27@gmail.com>
Co-authored-by: Soroush <hatpick@gmail.com>
Co-authored-by: Soroush <sorgh@microsoft.com>
Co-authored-by: Vamsi Modem <12182973+VamsiModem@users.noreply.github.com>
Co-authored-by: Ben Yackley <61990921+beyackle@users.noreply.github.com>

* fix mapping of schema files during migration

* fix lint

* fix default name of migrated project

* run schema merge as part of the migration

* do we need to migrate teh schema? or not?

* do not migrate schema

* - Adding field for runtime language
- Fixing field population for runtime type

* Passing runtime lang and type down to yeoman calls

* Fixing adaptive runtime name

* Hardcoding runtime version and fixing settings page generation on migrate

* Fixing check for 'inBotMigration' to be based on CreationStatusState > path substring

* Fix default naming

* revert preload.js

* Resolving PR comments and fixing errors from merge

* Change schema diagnostics severity to 'Error'

* Fetch @latest version from npm before migration

Co-authored-by: Dong Lei <donglei@microsoft.com>
Co-authored-by: leilzh <leilzh@microsoft.com>
Co-authored-by: zhixzhan <zhixzhan@microsoft.com>
Co-authored-by: zeye <zeye@microsoft.com>
Co-authored-by: Chris Whitten <christopher.whitten@microsoft.com>
Co-authored-by: Geoff Cox (Microsoft) <gcox@microsoft.com>
Co-authored-by: Srinaath Ravichandran <srinaath27@gmail.com>
Co-authored-by: Soroush <hatpick@gmail.com>
Co-authored-by: Soroush <sorgh@microsoft.com>
Co-authored-by: Vamsi Modem <12182973+VamsiModem@users.noreply.github.com>
Co-authored-by: Ben Yackley <61990921+beyackle@users.noreply.github.com>
Co-authored-by: Patrick Volum <pavolum@outlook.com>
Co-authored-by: Chris Whitten <christopherwhitten@gmail.com>

* fix: data race writing on setting file (#7475)

* fix data race writing on setting file

* remove isSkill in skillConfiguration

* remove default allowedCallers value

Co-authored-by: TJ Durnford <tjdford@gmail.com>
Co-authored-by: Chris Whitten <christopher.whitten@microsoft.com>
Co-authored-by: Ben Yackley <61990921+beyackle@users.noreply.github.com>
Co-authored-by: Soroush <hatpick@gmail.com>

* remove appsettings file from default gitignore (#7679)

* fix: restore line beneath page header (#7676)

* Update Page.tsx

* Update BotProjectSettings.tsx

* chore: Rebase main with 1.4.1 release (#7678)

* exclude items from the package manager list that do not have names or versions -- such as locally implemented custom actions (#7683)

* Add components field to migrated settings. Guard against missing field during component merge (#7674)

Co-authored-by: Dong Lei <donglei@microsoft.com>
Co-authored-by: Chris Whitten <christopher.whitten@microsoft.com>

* fix: (Un) Installing a package now stops the currently running bot beforehand (#7689)

* Feed string, not int, to Switch per schema (#7707)

Co-authored-by: Chris Whitten <christopher.whitten@microsoft.com>

* fix: generate manifest file when creating new publish target (#7666)

* generate manifest file when creating new profile

* test

Co-authored-by: Lu Han <32191031+luhan2017@users.noreply.github.com>
Co-authored-by: TJ Durnford <tjdford@gmail.com>

* fix: converting bot with custom actions (ui update) (#7672)

* Hides the PVA publish profile from the profle creation dropdown (#7725)

* fix: set func-related settings during build step (#7723)

* set func-related settings during build step

* slight refactor to reduce duplicated code

* add type

* add typings in runtime plugin definition

* set type of port to number

* fix types, remove _ on variables that are used

* feat: Add preparatory work before connecting remote skill (#7519)

* draft dialog

* ux css

* css

* jump to create profile & set microsoftAppId to publish target

* comments & lint

* refactor dialog wrapper

* fix publish types missing in provision dialog (#7697)

* test case

* refactor

* fix type define

* fix title and json parse

* fix app Id not sync when create new profile

* Adds AppID and Password sections

* test case fixed

* Adds AppID/Password

* Update copy

Co-authored-by: Soroush <hatpick@gmail.com>
Co-authored-by: Chris Whitten <christopher.whitten@microsoft.com>
Co-authored-by: Ben Yackley <61990921+beyackle@users.noreply.github.com>
Co-authored-by: VanyLaw <wenyluo@microsoft.com>
Co-authored-by: Lu Han <32191031+luhan2017@users.noreply.github.com>
Co-authored-by: Chris Whitten <christopherwhitten@gmail.com>

* fix: Revert changes to adaptive card templates to support PVA (#7808)

* fix: Revert changes to adaptive card templates to support PVA

* requested changes

* fetch publish types for each project

* fix: Throttle restart conversation (#7824)

* Throttle restart

Signed-off-by: Srinaath Ravichandran <srravich@microsoft.com>

* Remove await

Signed-off-by: Srinaath Ravichandran <srravich@microsoft.com>

* Updated throttle time

Signed-off-by: Srinaath Ravichandran <srravich@microsoft.com>

* Unit test update

Signed-off-by: Srinaath Ravichandran <srravich@microsoft.com>

* Enable only on connected

Signed-off-by: Srinaath Ravichandran <srravich@microsoft.com>

* removed if

Signed-off-by: Srinaath Ravichandran <srravich@microsoft.com>

Co-authored-by: Srinaath Ravichandran <srravich@microsoft.com>

* chore: automated localization updates (#7759)

* Localized resource files from OneLocBuild

* fix typos and stray apostrophes

* fix apostrophes

* typo fix

* Update en-US.json

* fix more apostrophes

* revert reversions

Co-authored-by: Composer Localization <botframework-composer-eng@service.microsoft.com>
Co-authored-by: Ben Yackley <61990921+beyackle@users.noreply.github.com>
Co-authored-by: Chris Whitten <christopher.whitten@microsoft.com>

* Deploy exception returns real error (#7837)

* fix: Fix missing data collection settings on server (#7814)

* fix: Fix missing data collection settings on server

* fix tests

* fix test again

* minor change

Co-authored-by: Chris Whitten <christopher.whitten@microsoft.com>

* fix: add inner scrollbar to selection area (#7782)

* scroll

* css

* test

Co-authored-by: Lu Han <32191031+luhan2017@users.noreply.github.com>
Co-authored-by: Chris Whitten <christopher.whitten@microsoft.com>

* Migration: adjust target blob transcript field names (#7848)

* fix: Always use intermediate lg template for text and speak modalities (#7842)

* allow click outside blocking modals (#7727)

Co-authored-by: Soroush <sorgh@microsoft.com>
Co-authored-by: TJ Durnford <tjdford@gmail.com>

* fix: Remove depth and transparentBorders from FieldProps, remove ObjectArrayField, and refactor OpenObjectField (#6633)

* Remove depth and transparentBorder

* Update OneOfField dropdown styles

* Remove ObjectArrayField

* fix tests

* Refactor OpenObjectField

* updated test

* minor

* fix test

* minor

Co-authored-by: Ben Yackley <61990921+beyackle@users.noreply.github.com>

* fix: Teaching bubble missing content (#7764)

* fix: Teaching bubble missing content

* Fix item ref

Co-authored-by: Ben Yackley <61990921+beyackle@users.noreply.github.com>
Co-authored-by: Soroush <hatpick@gmail.com>

* fix: Reset allowed callers cache when switching between project settings (#7874)

* fix: Reset allowed callers cache when switching between project settings

* comment

Co-authored-by: Soroush <hatpick@gmail.com>

* fix: breadcrumb & show code button collision (#7720)

* fix breadcrumb & show code button collision

* use flex

* update styles

* observe button width, auto layout ... breadcrumb

* ignore typescript ResizeObserver declarations missing

* fix type in test

* useResizeObserver

* fix mock

Co-authored-by: Srinaath Ravichandran <srinaath27@gmail.com>
Co-authored-by: Soroush <sorgh@microsoft.com>
Co-authored-by: Soroush <hatpick@gmail.com>

* Update home feed for Build 2021 (#7912)

* fix (#7440)

Co-authored-by: Soroush <sorgh@microsoft.com>
Co-authored-by: TJ Durnford <tjdford@gmail.com>

* fix: Custom function declaration (#7775)

* Custom function declaration

Signed-off-by: Srinaath Ravichandran <srravich@microsoft.com>

* Typo in custom functions

Signed-off-by: Srinaath Ravichandran <srravich@microsoft.com>

Co-authored-by: Srinaath Ravichandran <srravich@microsoft.com>

* add new caller when caller still have empty list, just focus on the empty one (#7767)

Co-authored-by: Lu Han <32191031+luhan2017@users.noreply.github.com>

* Resolve dns-packet to 1.3.4 to fix CVE-2021-23386 (#7942)

Co-authored-by: Soroush <hatpick@fmail.com>

* fix: invalid profile case (#7784)

* invalid profile case

* comments

* remove unused component

* refactor publish profile wrapper dialog

* toekn page jump logic err

Co-authored-by: Lu Han <32191031+luhan2017@users.noreply.github.com>
Co-authored-by: Soroush <hatpick@gmail.com>

* fix: Need to wait for zip deployment to be done processing (#7858)

* Handled 202

* Added error case for processing

* Improvements

Co-authored-by: Soroush <hatpick@gmail.com>

* Update readme screenshot (#7918)

Co-authored-by: Soroush <sorgh@microsoft.com>

* upgrade json-ptr (#7988)

* update test-utils dependencies

* remove console overrides

* fix tests for jest 27

* use fake timers for jest

* fix more tests for jest 27

* fix merge conflict

* clean up

* replace HookResult with RenderResult

* fix orchestrator test

* fix test warning

* fix jest-haste-map warning

* fix testid error

* attempt to resolve NetworkError in tests that use jsdom

* rename test file

* globally mock http client

* fix unhandled rejection error

* clear mocks after each test

* fix type errors

* rename test file

* move integration tests into own workspace

this is required due to a dependency resolution conflict within @testing-library/dom. See https://github.com/testing-library/dom-testing-library/issues/963

* remove unused dependency

* fix integration tests linting errors

* use correct directory for running e2e script

* re-enable parallel cypress tests

* pass --record option to cypress runner

* actually set up cypress to run in parallel

* add @testing-library/user-event to test-utils exports

* fix fixture path

* use fs-extra to ensure test bot directory

* update cypress comands

* remove pr trigger from pipeline

2 runs were happening. Trying to see if this fixes that.

* re-enable cleanup after all tests

* update lockfile

* fix mocks and disabled button checks

* add axios mock

* remove cypress examples

* add hasOrchestrator to useEffect dependencies

* add description to integration-tests package.json

* fix e2e script for windows

* remove console.log

Co-authored-by: taicchoumsft <61705609+taicchoumsft@users.noreply.github.com>
Co-authored-by: Ben Yackley <61990921+beyackle@users.noreply.github.com>
Co-authored-by: Chris Whitten <christopher.whitten@microsoft.com>
Co-authored-by: natalgar <82851479+natalgar@users.noreply.github.com>
Co-authored-by: GeoffCoxMSFT <gcox@microsoft.com>
Co-authored-by: Tony Anziano <tonyanziano5@gmail.com>
Co-authored-by: TJ Durnford <tjdford@gmail.com>
Co-authored-by: Soroush <hatpick@gmail.com>
Co-authored-by: Ben Brown <benbro@microsoft.com>
Co-authored-by: Dong Lei <donglei@microsoft.com>
Co-authored-by: leilzh <leilzh@microsoft.com>
Co-authored-by: zhixzhan <zhixzhan@microsoft.com>
Co-authored-by: zeye <zeye@microsoft.com>
Co-authored-by: Srinaath Ravichandran <srinaath27@gmail.com>
Co-authored-by: Soroush <sorgh@microsoft.com>
Co-authored-by: Vamsi Modem <12182973+VamsiModem@users.noreply.github.com>
Co-authored-by: Patrick Volum <pavolum@outlook.com>
Co-authored-by: Chris Whitten <christopherwhitten@gmail.com>
Co-authored-by: Long Alan <alanlong9278@126.com>
Co-authored-by: Lu Han <32191031+luhan2017@users.noreply.github.com>
Co-authored-by: Long Alan <julong@microsoft.com>
Co-authored-by: VanyLaw <wenyluo@microsoft.com>
Co-authored-by: Srinaath Ravichandran <srravich@microsoft.com>
Co-authored-by: Composer Localization <botframework-composer-eng@service.microsoft.com>
Co-authored-by: Carlos Castro <carlosscastro@users.noreply.github.com>
Co-authored-by: Gary Pretty <gary@garypretty.co.uk>
Co-authored-by: Soroush <hatpick@fmail.com>
This commit is contained in:
Andy Brown 2021-06-18 16:00:48 -07:00 коммит произвёл GitHub
Родитель 971e45e37e
Коммит 5cfac6ac6c
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
128 изменённых файлов: 3373 добавлений и 3360 удалений

4
.github/workflows/main.yml поставляемый
Просмотреть файл

@ -41,8 +41,8 @@ jobs:
run: yarn build:dev
- name: yarn lint
run: yarn lint:ci && yarn lint:extensions
- name: yarn test:coverage
run: yarn test:coverage
- name: yarn test:ci
run: yarn test:ci
- name: Coveralls
uses: coverallsapp/github-action@v1.1.1
continue-on-error: true

4
.vscode/settings.json поставляемый
Просмотреть файл

@ -7,9 +7,7 @@
},
"files.exclude": {
"**/.git": true,
"**/.DS_Store": true,
"**/node_modules": true,
"**/build": true
"**/.DS_Store": true
},
"eslint.packageManager": "yarn",
"eslint.validate": [

Просмотреть файл

@ -1,3 +0,0 @@
module.exports = {
extends: ['../.eslintrc.js', 'plugin:cypress/recommended'],
};

Просмотреть файл

@ -1 +0,0 @@
{"error":"Access denied due to invalid subscription key. Make sure you are subscribed to an API you are trying to call and provide the right key."}

Просмотреть файл

@ -1,20 +0,0 @@
{
"status": {
"MyProject_main_en-us_lu": {
"version": "0000000001",
"checksum": "",
"status": 1
}
},
"luFiles": [
{
"content": "#Dummy\r\n--I am Dummy",
"diagnostics": [],
"id": "Main",
"intents": [],
"relativePath": "Main/Main.lu",
"lastPublishTime": 2,
"lastUpdateTime": 1
}
]
}

Просмотреть файл

@ -1,272 +0,0 @@
/// <reference types="Cypress" />
context('Actions', () => {
beforeEach(() => {
cy.visit('https://example.cypress.io/commands/actions')
})
// https://on.cypress.io/interacting-with-elements
it('.type() - type into a DOM element', () => {
// https://on.cypress.io/type
cy.get('.action-email')
.type('fake@email.com').should('have.value', 'fake@email.com')
// .type() with special character sequences
.type('{leftarrow}{rightarrow}{uparrow}{downarrow}')
.type('{del}{selectall}{backspace}')
// .type() with key modifiers
.type('{alt}{option}') //these are equivalent
.type('{ctrl}{control}') //these are equivalent
.type('{meta}{command}{cmd}') //these are equivalent
.type('{shift}')
// Delay each keypress by 0.1 sec
.type('slow.typing@email.com', { delay: 100 })
.should('have.value', 'slow.typing@email.com')
cy.get('.action-disabled')
// Ignore error checking prior to type
// like whether the input is visible or disabled
.type('disabled error checking', { force: true })
.should('have.value', 'disabled error checking')
})
it('.focus() - focus on a DOM element', () => {
// https://on.cypress.io/focus
cy.get('.action-focus').focus()
.should('have.class', 'focus')
.prev().should('have.attr', 'style', 'color: orange;')
})
it('.blur() - blur off a DOM element', () => {
// https://on.cypress.io/blur
cy.get('.action-blur').type('About to blur').blur()
.should('have.class', 'error')
.prev().should('have.attr', 'style', 'color: red;')
})
it('.clear() - clears an input or textarea element', () => {
// https://on.cypress.io/clear
cy.get('.action-clear').type('Clear this text')
.should('have.value', 'Clear this text')
.clear()
.should('have.value', '')
})
it('.submit() - submit a form', () => {
// https://on.cypress.io/submit
cy.get('.action-form')
.find('[type="text"]').type('HALFOFF')
cy.get('.action-form').submit()
.next().should('contain', 'Your form has been submitted!')
})
it('.click() - click on a DOM element', () => {
// https://on.cypress.io/click
cy.get('.action-btn').click()
// You can click on 9 specific positions of an element:
// -----------------------------------
// | topLeft top topRight |
// | |
// | |
// | |
// | left center right |
// | |
// | |
// | |
// | bottomLeft bottom bottomRight |
// -----------------------------------
// clicking in the center of the element is the default
cy.get('#action-canvas').click()
cy.get('#action-canvas').click('topLeft')
cy.get('#action-canvas').click('top')
cy.get('#action-canvas').click('topRight')
cy.get('#action-canvas').click('left')
cy.get('#action-canvas').click('right')
cy.get('#action-canvas').click('bottomLeft')
cy.get('#action-canvas').click('bottom')
cy.get('#action-canvas').click('bottomRight')
// .click() accepts an x and y coordinate
// that controls where the click occurs :)
cy.get('#action-canvas')
.click(80, 75) // click 80px on x coord and 75px on y coord
.click(170, 75)
.click(80, 165)
.click(100, 185)
.click(125, 190)
.click(150, 185)
.click(170, 165)
// click multiple elements by passing multiple: true
cy.get('.action-labels>.label').click({ multiple: true })
// Ignore error checking prior to clicking
cy.get('.action-opacity>.btn').click({ force: true })
})
it('.dblclick() - double click on a DOM element', () => {
// https://on.cypress.io/dblclick
// Our app has a listener on 'dblclick' event in our 'scripts.js'
// that hides the div and shows an input on double click
cy.get('.action-div').dblclick().should('not.be.visible')
cy.get('.action-input-hidden').should('be.visible')
})
it('.check() - check a checkbox or radio element', () => {
// https://on.cypress.io/check
// By default, .check() will check all
// matching checkbox or radio elements in succession, one after another
cy.get('.action-checkboxes [type="checkbox"]').not('[disabled]')
.check().should('be.checked')
cy.get('.action-radios [type="radio"]').not('[disabled]')
.check().should('be.checked')
// .check() accepts a value argument
cy.get('.action-radios [type="radio"]')
.check('radio1').should('be.checked')
// .check() accepts an array of values
cy.get('.action-multiple-checkboxes [type="checkbox"]')
.check(['checkbox1', 'checkbox2']).should('be.checked')
// Ignore error checking prior to checking
cy.get('.action-checkboxes [disabled]')
.check({ force: true }).should('be.checked')
cy.get('.action-radios [type="radio"]')
.check('radio3', { force: true }).should('be.checked')
})
it('.uncheck() - uncheck a checkbox element', () => {
// https://on.cypress.io/uncheck
// By default, .uncheck() will uncheck all matching
// checkbox elements in succession, one after another
cy.get('.action-check [type="checkbox"]')
.not('[disabled]')
.uncheck().should('not.be.checked')
// .uncheck() accepts a value argument
cy.get('.action-check [type="checkbox"]')
.check('checkbox1')
.uncheck('checkbox1').should('not.be.checked')
// .uncheck() accepts an array of values
cy.get('.action-check [type="checkbox"]')
.check(['checkbox1', 'checkbox3'])
.uncheck(['checkbox1', 'checkbox3']).should('not.be.checked')
// Ignore error checking prior to unchecking
cy.get('.action-check [disabled]')
.uncheck({ force: true }).should('not.be.checked')
})
it('.select() - select an option in a <select> element', () => {
// https://on.cypress.io/select
// Select option(s) with matching text content
cy.get('.action-select').select('apples')
cy.get('.action-select-multiple')
.select(['apples', 'oranges', 'bananas'])
// Select option(s) with matching value
cy.get('.action-select').select('fr-bananas')
cy.get('.action-select-multiple')
.select(['fr-apples', 'fr-oranges', 'fr-bananas'])
})
it('.scrollIntoView() - scroll an element into view', () => {
// https://on.cypress.io/scrollintoview
// normally all of these buttons are hidden,
// because they're not within
// the viewable area of their parent
// (we need to scroll to see them)
cy.get('#scroll-horizontal button')
.should('not.be.visible')
// scroll the button into view, as if the user had scrolled
cy.get('#scroll-horizontal button').scrollIntoView()
.should('be.visible')
cy.get('#scroll-vertical button')
.should('not.be.visible')
// Cypress handles the scroll direction needed
cy.get('#scroll-vertical button').scrollIntoView()
.should('be.visible')
cy.get('#scroll-both button')
.should('not.be.visible')
// Cypress knows to scroll to the right and down
cy.get('#scroll-both button').scrollIntoView()
.should('be.visible')
})
it('.trigger() - trigger an event on a DOM element', () => {
// https://on.cypress.io/trigger
// To interact with a range input (slider)
// we need to set its value & trigger the
// event to signal it changed
// Here, we invoke jQuery's val() method to set
// the value and trigger the 'change' event
cy.get('.trigger-input-range')
.invoke('val', 25)
.trigger('change')
.get('input[type=range]').siblings('p')
.should('have.text', '25')
})
it('cy.scrollTo() - scroll the window or element to a position', () => {
// https://on.cypress.io/scrollTo
// You can scroll to 9 specific positions of an element:
// -----------------------------------
// | topLeft top topRight |
// | |
// | |
// | |
// | left center right |
// | |
// | |
// | |
// | bottomLeft bottom bottomRight |
// -----------------------------------
// if you chain .scrollTo() off of cy, we will
// scroll the entire window
cy.scrollTo('bottom')
cy.get('#scrollable-horizontal').scrollTo('right')
// or you can scroll to a specific coordinate:
// (x axis, y axis) in pixels
cy.get('#scrollable-vertical').scrollTo(250, 250)
// or you can scroll to a specific percentage
// of the (width, height) of the element
cy.get('#scrollable-both').scrollTo('75%', '25%')
// control the easing of the scroll (default is 'swing')
cy.get('#scrollable-vertical').scrollTo('center', { easing: 'linear' })
// control the duration of the scroll (in ms)
cy.get('#scrollable-both').scrollTo('center', { duration: 2000 })
})
})

Просмотреть файл

@ -1,42 +0,0 @@
/// <reference types="Cypress" />
context('Aliasing', () => {
beforeEach(() => {
cy.visit('https://example.cypress.io/commands/aliasing')
})
it('.as() - alias a DOM element for later use', () => {
// https://on.cypress.io/as
// Alias a DOM element for use later
// We don't have to traverse to the element
// later in our code, we reference it with @
cy.get('.as-table').find('tbody>tr')
.first().find('td').first()
.find('button').as('firstBtn')
// when we reference the alias, we place an
// @ in front of its name
cy.get('@firstBtn').click()
cy.get('@firstBtn')
.should('have.class', 'btn-success')
.and('contain', 'Changed')
})
it('.as() - alias a route for later use', () => {
// Alias the route to wait for its response
cy.server()
cy.route('GET', 'comments/*').as('getComment')
// we have code that gets a comment when
// the button is clicked in scripts.js
cy.get('.network-btn').click()
// https://on.cypress.io/wait
cy.wait('@getComment').its('status').should('eq', 200)
})
})

Просмотреть файл

@ -1,168 +0,0 @@
/// <reference types="Cypress" />
context('Assertions', () => {
beforeEach(() => {
cy.visit('https://example.cypress.io/commands/assertions')
})
describe('Implicit Assertions', () => {
it('.should() - make an assertion about the current subject', () => {
// https://on.cypress.io/should
cy.get('.assertion-table')
.find('tbody tr:last')
.should('have.class', 'success')
.find('td')
.first()
// checking the text of the <td> element in various ways
.should('have.text', 'Column content')
.should('contain', 'Column content')
.should('have.html', 'Column content')
// chai-jquery uses "is()" to check if element matches selector
.should('match', 'td')
// to match text content against a regular expression
// first need to invoke jQuery method text()
// and then match using regular expression
.invoke('text')
.should('match', /column content/i)
// a better way to check element's text content against a regular expression
// is to use "cy.contains"
// https://on.cypress.io/contains
cy.get('.assertion-table')
.find('tbody tr:last')
// finds first <td> element with text content matching regular expression
.contains('td', /column content/i)
.should('be.visible')
// for more information about asserting element's text
// see https://on.cypress.io/using-cypress-faq#How-do-I-get-an-elements-text-contents
})
it('.and() - chain multiple assertions together', () => {
// https://on.cypress.io/and
cy.get('.assertions-link')
.should('have.class', 'active')
.and('have.attr', 'href')
.and('include', 'cypress.io')
})
})
describe('Explicit Assertions', () => {
// https://on.cypress.io/assertions
it('expect - make an assertion about a specified subject', () => {
// We can use Chai's BDD style assertions
expect(true).to.be.true
const o = { foo: 'bar' }
expect(o).to.equal(o)
expect(o).to.deep.equal({ foo: 'bar' })
// matching text using regular expression
expect('FooBar').to.match(/bar$/i)
})
it('pass your own callback function to should()', () => {
// Pass a function to should that can have any number
// of explicit assertions within it.
// The ".should(cb)" function will be retried
// automatically until it passes all your explicit assertions or times out.
cy.get('.assertions-p')
.find('p')
.should(($p) => {
// https://on.cypress.io/$
// return an array of texts from all of the p's
// @ts-ignore TS6133 unused variable
const texts = $p.map((i, el) => Cypress.$(el).text())
// jquery map returns jquery object
// and .get() convert this to simple array
const paragraphs = texts.get()
// array should have length of 3
expect(paragraphs, 'has 3 paragraphs').to.have.length(3)
// use second argument to expect(...) to provide clear
// message with each assertion
expect(paragraphs, 'has expected text in each paragraph').to.deep.eq([
'Some text from first p',
'More text from second p',
'And even more text from third p',
])
})
})
it('finds element by class name regex', () => {
cy.get('.docs-header')
.find('div')
// .should(cb) callback function will be retried
.should(($div) => {
expect($div).to.have.length(1)
const className = $div[0].className
expect(className).to.match(/heading-/)
})
// .then(cb) callback is not retried,
// it either passes or fails
.then(($div) => {
expect($div, 'text content').to.have.text('Introduction')
})
})
it('can throw any error', () => {
cy.get('.docs-header')
.find('div')
.should(($div) => {
if ($div.length !== 1) {
// you can throw your own errors
throw new Error('Did not find 1 element')
}
const className = $div[0].className
if (!className.match(/heading-/)) {
throw new Error(`Could not find class "heading-" in ${className}`)
}
})
})
it('matches unknown text between two elements', () => {
/**
* Text from the first element.
* @type {string}
*/
let text
/**
* Normalizes passed text,
* useful before comparing text with spaces and different capitalization.
* @param {string} s Text to normalize
*/
const normalizeText = (s) => s.replace(/\s/g, '').toLowerCase()
cy.get('.two-elements')
.find('.first')
.then(($first) => {
// save text from the first element
text = normalizeText($first.text())
})
cy.get('.two-elements')
.find('.second')
.should(($div) => {
// we can massage text before comparing
const secondText = normalizeText($div.text())
expect(secondText, 'second text').to.equal(text)
})
})
it('assert - assert shape of an object', () => {
const person = {
name: 'Joe',
age: 20,
}
assert.isObject(person, 'value is object')
})
})
})

Просмотреть файл

@ -1,56 +0,0 @@
/// <reference types="Cypress" />
context('Connectors', () => {
beforeEach(() => {
cy.visit('https://example.cypress.io/commands/connectors')
})
it('.each() - iterate over an array of elements', () => {
// https://on.cypress.io/each
cy.get('.connectors-each-ul>li')
.each(($el, index, $list) => {
console.log($el, index, $list)
})
})
it('.its() - get properties on the current subject', () => {
// https://on.cypress.io/its
cy.get('.connectors-its-ul>li')
// calls the 'length' property yielding that value
.its('length')
.should('be.gt', 2)
})
it('.invoke() - invoke a function on the current subject', () => {
// our div is hidden in our script.js
// $('.connectors-div').hide()
// https://on.cypress.io/invoke
cy.get('.connectors-div').should('be.hidden')
// call the jquery method 'show' on the 'div.container'
.invoke('show')
.should('be.visible')
})
it('.spread() - spread an array as individual args to callback function', () => {
// https://on.cypress.io/spread
const arr = ['foo', 'bar', 'baz']
cy.wrap(arr).spread((foo, bar, baz) => {
expect(foo).to.eq('foo')
expect(bar).to.eq('bar')
expect(baz).to.eq('baz')
})
})
it('.then() - invoke a callback function with the current subject', () => {
// https://on.cypress.io/then
cy.get('.connectors-list > li')
.then(($lis) => {
expect($lis, '3 items').to.have.length(3)
expect($lis.eq(0), 'first item').to.contain('Walk the dog')
expect($lis.eq(1), 'second item').to.contain('Feed the cat')
expect($lis.eq(2), 'third item').to.contain('Write JavaScript')
})
})
})

Просмотреть файл

@ -1,78 +0,0 @@
/// <reference types="Cypress" />
context('Cookies', () => {
beforeEach(() => {
Cypress.Cookies.debug(true)
cy.visit('https://example.cypress.io/commands/cookies')
// clear cookies again after visiting to remove
// any 3rd party cookies picked up such as cloudflare
cy.clearCookies()
})
it('cy.getCookie() - get a browser cookie', () => {
// https://on.cypress.io/getcookie
cy.get('#getCookie .set-a-cookie').click()
// cy.getCookie() yields a cookie object
cy.getCookie('token').should('have.property', 'value', '123ABC')
})
it('cy.getCookies() - get browser cookies', () => {
// https://on.cypress.io/getcookies
cy.getCookies().should('be.empty')
cy.get('#getCookies .set-a-cookie').click()
// cy.getCookies() yields an array of cookies
cy.getCookies().should('have.length', 1).should((cookies) => {
// each cookie has these properties
expect(cookies[0]).to.have.property('name', 'token')
expect(cookies[0]).to.have.property('value', '123ABC')
expect(cookies[0]).to.have.property('httpOnly', false)
expect(cookies[0]).to.have.property('secure', false)
expect(cookies[0]).to.have.property('domain')
expect(cookies[0]).to.have.property('path')
})
})
it('cy.setCookie() - set a browser cookie', () => {
// https://on.cypress.io/setcookie
cy.getCookies().should('be.empty')
cy.setCookie('foo', 'bar')
// cy.getCookie() yields a cookie object
cy.getCookie('foo').should('have.property', 'value', 'bar')
})
it('cy.clearCookie() - clear a browser cookie', () => {
// https://on.cypress.io/clearcookie
cy.getCookie('token').should('be.null')
cy.get('#clearCookie .set-a-cookie').click()
cy.getCookie('token').should('have.property', 'value', '123ABC')
// cy.clearCookies() yields null
cy.clearCookie('token').should('be.null')
cy.getCookie('token').should('be.null')
})
it('cy.clearCookies() - clear browser cookies', () => {
// https://on.cypress.io/clearcookies
cy.getCookies().should('be.empty')
cy.get('#clearCookies .set-a-cookie').click()
cy.getCookies().should('have.length', 1)
// cy.clearCookies() yields null
cy.clearCookies()
cy.getCookies().should('be.empty')
})
})

Просмотреть файл

@ -1,222 +0,0 @@
/// <reference types="Cypress" />
context('Cypress.Commands', () => {
beforeEach(() => {
cy.visit('https://example.cypress.io/cypress-api')
})
// https://on.cypress.io/custom-commands
it('.add() - create a custom command', () => {
Cypress.Commands.add('console', {
prevSubject: true,
}, (subject, method) => {
// the previous subject is automatically received
// and the commands arguments are shifted
// allow us to change the console method used
method = method || 'log'
// log the subject to the console
// @ts-ignore TS7017
console[method]('The subject is', subject)
// whatever we return becomes the new subject
// we don't want to change the subject so
// we return whatever was passed in
return subject
})
// @ts-ignore TS2339
cy.get('button').console('info').then(($button) => {
// subject is still $button
})
})
})
context('Cypress.Cookies', () => {
beforeEach(() => {
cy.visit('https://example.cypress.io/cypress-api')
})
// https://on.cypress.io/cookies
it('.debug() - enable or disable debugging', () => {
Cypress.Cookies.debug(true)
// Cypress will now log in the console when
// cookies are set or cleared
cy.setCookie('fakeCookie', '123ABC')
cy.clearCookie('fakeCookie')
cy.setCookie('fakeCookie', '123ABC')
cy.clearCookie('fakeCookie')
cy.setCookie('fakeCookie', '123ABC')
})
it('.preserveOnce() - preserve cookies by key', () => {
// normally cookies are reset after each test
cy.getCookie('fakeCookie').should('not.be.ok')
// preserving a cookie will not clear it when
// the next test starts
cy.setCookie('lastCookie', '789XYZ')
Cypress.Cookies.preserveOnce('lastCookie')
})
it('.defaults() - set defaults for all cookies', () => {
// now any cookie with the name 'session_id' will
// not be cleared before each new test runs
Cypress.Cookies.defaults({
whitelist: 'session_id',
})
})
})
context('Cypress.Server', () => {
beforeEach(() => {
cy.visit('https://example.cypress.io/cypress-api')
})
// Permanently override server options for
// all instances of cy.server()
// https://on.cypress.io/cypress-server
it('.defaults() - change default config of server', () => {
Cypress.Server.defaults({
delay: 0,
force404: false,
})
})
})
context('Cypress.arch', () => {
beforeEach(() => {
cy.visit('https://example.cypress.io/cypress-api')
})
it('Get CPU architecture name of underlying OS', () => {
// https://on.cypress.io/arch
expect(Cypress.arch).to.exist
})
})
context('Cypress.config()', () => {
beforeEach(() => {
cy.visit('https://example.cypress.io/cypress-api')
})
it('Get and set configuration options', () => {
// https://on.cypress.io/config
let myConfig = Cypress.config()
expect(myConfig).to.have.property('animationDistanceThreshold', 5)
expect(myConfig).to.have.property('baseUrl', null)
expect(myConfig).to.have.property('defaultCommandTimeout', 4000)
expect(myConfig).to.have.property('requestTimeout', 5000)
expect(myConfig).to.have.property('responseTimeout', 30000)
expect(myConfig).to.have.property('viewportHeight', 660)
expect(myConfig).to.have.property('viewportWidth', 1000)
expect(myConfig).to.have.property('pageLoadTimeout', 60000)
expect(myConfig).to.have.property('waitForAnimations', true)
expect(Cypress.config('pageLoadTimeout')).to.eq(60000)
// this will change the config for the rest of your tests!
Cypress.config('pageLoadTimeout', 20000)
expect(Cypress.config('pageLoadTimeout')).to.eq(20000)
Cypress.config('pageLoadTimeout', 60000)
})
})
context('Cypress.dom', () => {
beforeEach(() => {
cy.visit('https://example.cypress.io/cypress-api')
})
// https://on.cypress.io/dom
it('.isHidden() - determine if a DOM element is hidden', () => {
let hiddenP = Cypress.$('.dom-p p.hidden').get(0)
let visibleP = Cypress.$('.dom-p p.visible').get(0)
// our first paragraph has css class 'hidden'
expect(Cypress.dom.isHidden(hiddenP)).to.be.true
expect(Cypress.dom.isHidden(visibleP)).to.be.false
})
})
context('Cypress.env()', () => {
beforeEach(() => {
cy.visit('https://example.cypress.io/cypress-api')
})
// We can set environment variables for highly dynamic values
// https://on.cypress.io/environment-variables
it('Get environment variables', () => {
// https://on.cypress.io/env
// set multiple environment variables
Cypress.env({
host: 'veronica.dev.local',
api_server: 'http://localhost:8888/v1/',
})
// get environment variable
expect(Cypress.env('host')).to.eq('veronica.dev.local')
// set environment variable
Cypress.env('api_server', 'http://localhost:8888/v2/')
expect(Cypress.env('api_server')).to.eq('http://localhost:8888/v2/')
// get all environment variable
expect(Cypress.env()).to.have.property('host', 'veronica.dev.local')
expect(Cypress.env()).to.have.property('api_server', 'http://localhost:8888/v2/')
})
})
context('Cypress.log', () => {
beforeEach(() => {
cy.visit('https://example.cypress.io/cypress-api')
})
it('Control what is printed to the Command Log', () => {
// https://on.cypress.io/cypress-log
})
})
context('Cypress.platform', () => {
beforeEach(() => {
cy.visit('https://example.cypress.io/cypress-api')
})
it('Get underlying OS name', () => {
// https://on.cypress.io/platform
expect(Cypress.platform).to.be.exist
})
})
context('Cypress.version', () => {
beforeEach(() => {
cy.visit('https://example.cypress.io/cypress-api')
})
it('Get current version of Cypress being run', () => {
// https://on.cypress.io/version
expect(Cypress.version).to.be.exist
})
})
context('Cypress.spec', () => {
beforeEach(() => {
cy.visit('https://example.cypress.io/cypress-api')
})
it('Get current spec information', () => {
// https://on.cypress.io/spec
// wrap the object so we can inspect it easily by clicking in the command log
cy.wrap(Cypress.spec).should('have.keys', ['name', 'relative', 'absolute'])
})
})

Просмотреть файл

@ -1,86 +0,0 @@
/// <reference types="Cypress" />
context('Files', () => {
beforeEach(() => {
cy.visit('https://example.cypress.io/commands/files')
})
it('cy.fixture() - load a fixture', () => {
// https://on.cypress.io/fixture
// Instead of writing a response inline you can
// use a fixture file's content.
cy.server()
cy.fixture('example.json').as('comment')
cy.route('GET', 'comments/*', '@comment').as('getComment')
// we have code that gets a comment when
// the button is clicked in scripts.js
cy.get('.fixture-btn').click()
cy.wait('@getComment').its('responseBody')
.should('have.property', 'name')
.and('include', 'Using fixtures to represent data')
// you can also just write the fixture in the route
cy.route('GET', 'comments/*', 'fixture:example.json').as('getComment')
// we have code that gets a comment when
// the button is clicked in scripts.js
cy.get('.fixture-btn').click()
cy.wait('@getComment').its('responseBody')
.should('have.property', 'name')
.and('include', 'Using fixtures to represent data')
// or write fx to represent fixture
// by default it assumes it's .json
cy.route('GET', 'comments/*', 'fx:example').as('getComment')
// we have code that gets a comment when
// the button is clicked in scripts.js
cy.get('.fixture-btn').click()
cy.wait('@getComment').its('responseBody')
.should('have.property', 'name')
.and('include', 'Using fixtures to represent data')
})
it('cy.readFile() - read a files contents', () => {
// https://on.cypress.io/readfile
// You can read a file and yield its contents
// The filePath is relative to your project's root.
cy.readFile('cypress.json').then((json) => {
expect(json).to.be.an('object')
})
})
it('cy.writeFile() - write to a file', () => {
// https://on.cypress.io/writefile
// You can write to a file
// Use a response from a request to automatically
// generate a fixture file for use later
cy.request('https://jsonplaceholder.cypress.io/users')
.then((response) => {
cy.writeFile('cypress/fixtures/users.json', response.body)
})
cy.fixture('users').should((users) => {
expect(users[0].name).to.exist
})
// JavaScript arrays and objects are stringified
// and formatted into text.
cy.writeFile('cypress/fixtures/profile.json', {
id: 8739,
name: 'Jane',
email: 'jane@example.com',
})
cy.fixture('profile').should((profile) => {
expect(profile.name).to.eq('Jane')
})
})
})

Просмотреть файл

@ -1,52 +0,0 @@
/// <reference types="Cypress" />
context('Local Storage', () => {
beforeEach(() => {
cy.visit('https://example.cypress.io/commands/local-storage')
})
// Although local storage is automatically cleared
// in between tests to maintain a clean state
// sometimes we need to clear the local storage manually
it('cy.clearLocalStorage() - clear all data in local storage', () => {
// https://on.cypress.io/clearlocalstorage
cy.get('.ls-btn').click().should(() => {
expect(localStorage.getItem('prop1')).to.eq('red')
expect(localStorage.getItem('prop2')).to.eq('blue')
expect(localStorage.getItem('prop3')).to.eq('magenta')
})
// clearLocalStorage() yields the localStorage object
cy.clearLocalStorage().should((ls) => {
expect(ls.getItem('prop1')).to.be.null
expect(ls.getItem('prop2')).to.be.null
expect(ls.getItem('prop3')).to.be.null
})
// Clear key matching string in Local Storage
cy.get('.ls-btn').click().should(() => {
expect(localStorage.getItem('prop1')).to.eq('red')
expect(localStorage.getItem('prop2')).to.eq('blue')
expect(localStorage.getItem('prop3')).to.eq('magenta')
})
cy.clearLocalStorage('prop1').should((ls) => {
expect(ls.getItem('prop1')).to.be.null
expect(ls.getItem('prop2')).to.eq('blue')
expect(ls.getItem('prop3')).to.eq('magenta')
})
// Clear keys matching regex in Local Storage
cy.get('.ls-btn').click().should(() => {
expect(localStorage.getItem('prop1')).to.eq('red')
expect(localStorage.getItem('prop2')).to.eq('blue')
expect(localStorage.getItem('prop3')).to.eq('magenta')
})
cy.clearLocalStorage(/prop1|2/).should((ls) => {
expect(ls.getItem('prop1')).to.be.null
expect(ls.getItem('prop2')).to.be.null
expect(ls.getItem('prop3')).to.eq('magenta')
})
})
})

Просмотреть файл

@ -1,32 +0,0 @@
/// <reference types="Cypress" />
context('Location', () => {
beforeEach(() => {
cy.visit('https://example.cypress.io/commands/location')
})
it('cy.hash() - get the current URL hash', () => {
// https://on.cypress.io/hash
cy.hash().should('be.empty')
})
it('cy.location() - get window.location', () => {
// https://on.cypress.io/location
cy.location().should((location) => {
expect(location.hash).to.be.empty
expect(location.href).to.eq('https://example.cypress.io/commands/location')
expect(location.host).to.eq('example.cypress.io')
expect(location.hostname).to.eq('example.cypress.io')
expect(location.origin).to.eq('https://example.cypress.io')
expect(location.pathname).to.eq('/commands/location')
expect(location.port).to.eq('')
expect(location.protocol).to.eq('https:')
expect(location.search).to.be.empty
})
})
it('cy.url() - get the current URL', () => {
// https://on.cypress.io/url
cy.url().should('eq', 'https://example.cypress.io/commands/location')
})
})

Просмотреть файл

@ -1,83 +0,0 @@
/// <reference types="Cypress" />
context('Misc', () => {
beforeEach(() => {
cy.visit('https://example.cypress.io/commands/misc')
})
it('.end() - end the command chain', () => {
// https://on.cypress.io/end
// cy.end is useful when you want to end a chain of commands
// and force Cypress to re-query from the root element
cy.get('.misc-table').within(() => {
// ends the current chain and yields null
cy.contains('Cheryl').click().end()
// queries the entire table again
cy.contains('Charles').click()
})
})
it('cy.exec() - execute a system command', () => {
// https://on.cypress.io/exec
// execute a system command.
// so you can take actions necessary for
// your test outside the scope of Cypress.
cy.exec('echo Jane Lane')
.its('stdout').should('contain', 'Jane Lane')
// we can use Cypress.platform string to
// select appropriate command
// https://on.cypress/io/platform
cy.log(`Platform ${Cypress.platform} architecture ${Cypress.arch}`)
if (Cypress.platform === 'win32') {
cy.exec('print cypress.json')
.its('stderr').should('be.empty')
} else {
cy.exec('cat cypress.json')
.its('stderr').should('be.empty')
cy.exec('pwd')
.its('code').should('eq', 0)
}
})
it('cy.focused() - get the DOM element that has focus', () => {
// https://on.cypress.io/focused
cy.get('.misc-form').find('#name').click()
cy.focused().should('have.id', 'name')
cy.get('.misc-form').find('#description').click()
cy.focused().should('have.id', 'description')
})
context('Cypress.Screenshot', function () {
it('cy.screenshot() - take a screenshot', () => {
// https://on.cypress.io/screenshot
cy.screenshot('my-image')
})
it('Cypress.Screenshot.defaults() - change default config of screenshots', function () {
Cypress.Screenshot.defaults({
blackout: ['.foo'],
capture: 'viewport',
clip: { x: 0, y: 0, width: 200, height: 200 },
scale: false,
disableTimersAndAnimations: true,
screenshotOnRunFailure: true,
beforeScreenshot () { },
afterScreenshot () { },
})
})
})
it('cy.wrap() - wrap an object', () => {
// https://on.cypress.io/wrap
cy.wrap({ foo: 'bar' })
.should('have.property', 'foo')
.and('include', 'bar')
})
})

Просмотреть файл

@ -1,56 +0,0 @@
/// <reference types="Cypress" />
context('Navigation', () => {
beforeEach(() => {
cy.visit('https://example.cypress.io')
cy.get('.navbar-nav').contains('Commands').click()
cy.get('.dropdown-menu').contains('Navigation').click()
})
it('cy.go() - go back or forward in the browser\'s history', () => {
// https://on.cypress.io/go
cy.location('pathname').should('include', 'navigation')
cy.go('back')
cy.location('pathname').should('not.include', 'navigation')
cy.go('forward')
cy.location('pathname').should('include', 'navigation')
// clicking back
cy.go(-1)
cy.location('pathname').should('not.include', 'navigation')
// clicking forward
cy.go(1)
cy.location('pathname').should('include', 'navigation')
})
it('cy.reload() - reload the page', () => {
// https://on.cypress.io/reload
cy.reload()
// reload the page without using the cache
cy.reload(true)
})
it('cy.visit() - visit a remote url', () => {
// https://on.cypress.io/visit
// Visit any sub-domain of your current domain
// Pass options to the visit
cy.visit('https://example.cypress.io/commands/navigation', {
timeout: 50000, // increase total time for the visit to resolve
onBeforeLoad (contentWindow) {
// contentWindow is the remote page's window object
expect(typeof contentWindow === 'object').to.be.true
},
onLoad (contentWindow) {
// contentWindow is the remote page's window object
expect(typeof contentWindow === 'object').to.be.true
},
})
})
})

Просмотреть файл

@ -1,140 +0,0 @@
/// <reference types="Cypress" />
context('Network Requests', () => {
beforeEach(() => {
cy.visit('https://example.cypress.io/commands/network-requests')
})
// Manage AJAX / XHR requests in your app
it('cy.server() - control behavior of network requests and responses', () => {
// https://on.cypress.io/server
cy.server().should((server) => {
// the default options on server
// you can override any of these options
expect(server.delay).to.eq(0)
expect(server.method).to.eq('GET')
expect(server.status).to.eq(200)
expect(server.headers).to.be.null
expect(server.response).to.be.null
expect(server.onRequest).to.be.undefined
expect(server.onResponse).to.be.undefined
expect(server.onAbort).to.be.undefined
// These options control the server behavior
// affecting all requests
// pass false to disable existing route stubs
expect(server.enable).to.be.true
// forces requests that don't match your routes to 404
expect(server.force404).to.be.false
// whitelists requests from ever being logged or stubbed
expect(server.whitelist).to.be.a('function')
})
cy.server({
method: 'POST',
delay: 1000,
status: 422,
response: {},
})
// any route commands will now inherit the above options
// from the server. anything we pass specifically
// to route will override the defaults though.
})
it('cy.request() - make an XHR request', () => {
// https://on.cypress.io/request
cy.request('https://jsonplaceholder.cypress.io/comments')
.should((response) => {
expect(response.status).to.eq(200)
expect(response.body).to.have.length(500)
expect(response).to.have.property('headers')
expect(response).to.have.property('duration')
})
})
it('cy.request() - verify response using BDD syntax', () => {
cy.request('https://jsonplaceholder.cypress.io/comments')
.then((response) => {
// https://on.cypress.io/assertions
expect(response).property('status').to.equal(200)
expect(response).property('body').to.have.length(500)
expect(response).to.include.keys('headers', 'duration')
})
})
it('cy.request() with query parameters', () => {
// will execute request
// https://jsonplaceholder.cypress.io/comments?postId=1&id=3
cy.request({
url: 'https://jsonplaceholder.cypress.io/comments',
qs: {
postId: 1,
id: 3,
},
})
.its('body')
.should('be.an', 'array')
.and('have.length', 1)
.its('0') // yields first element of the array
.should('contain', {
postId: 1,
id: 3,
})
})
it('cy.route() - route responses to matching requests', () => {
// https://on.cypress.io/route
let message = 'whoa, this comment does not exist'
cy.server()
// Listen to GET to comments/1
cy.route('GET', 'comments/*').as('getComment')
// we have code that gets a comment when
// the button is clicked in scripts.js
cy.get('.network-btn').click()
// https://on.cypress.io/wait
cy.wait('@getComment').its('status').should('eq', 200)
// Listen to POST to comments
cy.route('POST', '/comments').as('postComment')
// we have code that posts a comment when
// the button is clicked in scripts.js
cy.get('.network-post').click()
cy.wait('@postComment')
// get the route
cy.get('@postComment').should((xhr) => {
expect(xhr.requestBody).to.include('email')
expect(xhr.requestHeaders).to.have.property('Content-Type')
expect(xhr.responseBody).to.have.property('name', 'Using POST in cy.route()')
})
// Stub a response to PUT comments/ ****
cy.route({
method: 'PUT',
url: 'comments/*',
status: 404,
response: { error: message },
delay: 500,
}).as('putComment')
// we have code that puts a comment when
// the button is clicked in scripts.js
cy.get('.network-put').click()
cy.wait('@putComment')
// our 404 statusCode logic in scripts.js executed
cy.get('.network-put-comment').should('contain', message)
})
})

Просмотреть файл

@ -1,87 +0,0 @@
/// <reference types="Cypress" />
context('Querying', () => {
beforeEach(() => {
cy.visit('https://example.cypress.io/commands/querying')
})
// The most commonly used query is 'cy.get()', you can
// think of this like the '$' in jQuery
it('cy.get() - query DOM elements', () => {
// https://on.cypress.io/get
cy.get('#query-btn').should('contain', 'Button')
cy.get('.query-btn').should('contain', 'Button')
cy.get('#querying .well>button:first').should('contain', 'Button')
// ↲
// Use CSS selectors just like jQuery
cy.get('[data-test-id="test-example"]').should('have.class', 'example')
// 'cy.get()' yields jQuery object, you can get its attribute
// by invoking `.attr()` method
cy.get('[data-test-id="test-example"]')
.invoke('attr', 'data-test-id')
.should('equal', 'test-example')
// or you can get element's CSS property
cy.get('[data-test-id="test-example"]')
.invoke('css', 'position')
.should('equal', 'static')
// or use assertions directly during 'cy.get()'
// https://on.cypress.io/assertions
cy.get('[data-test-id="test-example"]')
.should('have.attr', 'data-test-id', 'test-example')
.and('have.css', 'position', 'static')
})
it('cy.contains() - query DOM elements with matching content', () => {
// https://on.cypress.io/contains
cy.get('.query-list')
.contains('bananas')
.should('have.class', 'third')
// we can pass a regexp to `.contains()`
cy.get('.query-list')
.contains(/^b\w+/)
.should('have.class', 'third')
cy.get('.query-list')
.contains('apples')
.should('have.class', 'first')
// passing a selector to contains will
// yield the selector containing the text
cy.get('#querying')
.contains('ul', 'oranges')
.should('have.class', 'query-list')
cy.get('.query-button')
.contains('Save Form')
.should('have.class', 'btn')
})
it('.within() - query DOM elements within a specific element', () => {
// https://on.cypress.io/within
cy.get('.query-form').within(() => {
cy.get('input:first').should('have.attr', 'placeholder', 'Email')
cy.get('input:last').should('have.attr', 'placeholder', 'Password')
})
})
it('cy.root() - query the root DOM element', () => {
// https://on.cypress.io/root
// By default, root is the document
cy.root().should('match', 'html')
cy.get('.query-ul').within(() => {
// In this within, the root is now the ul DOM element
cy.root().should('have.class', 'query-ul')
})
})
})

Просмотреть файл

@ -1,69 +0,0 @@
/// <reference types="Cypress" />
context('Spies, Stubs, and Clock', () => {
it('cy.spy() - wrap a method in a spy', () => {
// https://on.cypress.io/spy
cy.visit('https://example.cypress.io/commands/spies-stubs-clocks')
let obj = {
foo () {},
}
let spy = cy.spy(obj, 'foo').as('anyArgs')
obj.foo()
expect(spy).to.be.called
})
it('cy.stub() - create a stub and/or replace a function with stub', () => {
// https://on.cypress.io/stub
cy.visit('https://example.cypress.io/commands/spies-stubs-clocks')
let obj = {
/**
* prints both arguments to the console
* @param a {string}
* @param b {string}
*/
foo (a, b) {
console.log('a', a, 'b', b)
},
}
let stub = cy.stub(obj, 'foo').as('foo')
obj.foo('foo', 'bar')
expect(stub).to.be.called
})
it('cy.clock() - control time in the browser', () => {
// https://on.cypress.io/clock
// create the date in UTC so its always the same
// no matter what local timezone the browser is running in
let now = new Date(Date.UTC(2017, 2, 14)).getTime()
cy.clock(now)
cy.visit('https://example.cypress.io/commands/spies-stubs-clocks')
cy.get('#clock-div').click()
.should('have.text', '1489449600')
})
it('cy.tick() - move time in the browser', () => {
// https://on.cypress.io/tick
// create the date in UTC so its always the same
// no matter what local timezone the browser is running in
let now = new Date(Date.UTC(2017, 2, 14)).getTime()
cy.clock(now)
cy.visit('https://example.cypress.io/commands/spies-stubs-clocks')
cy.get('#tick-div').click()
.should('have.text', '1489449600')
cy.tick(10000) // 10 seconds passed
cy.get('#tick-div').click()
.should('have.text', '1489449610')
})
})

Просмотреть файл

@ -1,121 +0,0 @@
/// <reference types="Cypress" />
context('Traversal', () => {
beforeEach(() => {
cy.visit('https://example.cypress.io/commands/traversal')
})
it('.children() - get child DOM elements', () => {
// https://on.cypress.io/children
cy.get('.traversal-breadcrumb')
.children('.active')
.should('contain', 'Data')
})
it('.closest() - get closest ancestor DOM element', () => {
// https://on.cypress.io/closest
cy.get('.traversal-badge')
.closest('ul')
.should('have.class', 'list-group')
})
it('.eq() - get a DOM element at a specific index', () => {
// https://on.cypress.io/eq
cy.get('.traversal-list>li')
.eq(1).should('contain', 'siamese')
})
it('.filter() - get DOM elements that match the selector', () => {
// https://on.cypress.io/filter
cy.get('.traversal-nav>li')
.filter('.active').should('contain', 'About')
})
it('.find() - get descendant DOM elements of the selector', () => {
// https://on.cypress.io/find
cy.get('.traversal-pagination')
.find('li').find('a')
.should('have.length', 7)
})
it('.first() - get first DOM element', () => {
// https://on.cypress.io/first
cy.get('.traversal-table td')
.first().should('contain', '1')
})
it('.last() - get last DOM element', () => {
// https://on.cypress.io/last
cy.get('.traversal-buttons .btn')
.last().should('contain', 'Submit')
})
it('.next() - get next sibling DOM element', () => {
// https://on.cypress.io/next
cy.get('.traversal-ul')
.contains('apples').next().should('contain', 'oranges')
})
it('.nextAll() - get all next sibling DOM elements', () => {
// https://on.cypress.io/nextall
cy.get('.traversal-next-all')
.contains('oranges')
.nextAll().should('have.length', 3)
})
it('.nextUntil() - get next sibling DOM elements until next el', () => {
// https://on.cypress.io/nextuntil
cy.get('#veggies')
.nextUntil('#nuts').should('have.length', 3)
})
it('.not() - remove DOM elements from set of DOM elements', () => {
// https://on.cypress.io/not
cy.get('.traversal-disabled .btn')
.not('[disabled]').should('not.contain', 'Disabled')
})
it('.parent() - get parent DOM element from DOM elements', () => {
// https://on.cypress.io/parent
cy.get('.traversal-mark')
.parent().should('contain', 'Morbi leo risus')
})
it('.parents() - get parent DOM elements from DOM elements', () => {
// https://on.cypress.io/parents
cy.get('.traversal-cite')
.parents().should('match', 'blockquote')
})
it('.parentsUntil() - get parent DOM elements from DOM elements until el', () => {
// https://on.cypress.io/parentsuntil
cy.get('.clothes-nav')
.find('.active')
.parentsUntil('.clothes-nav')
.should('have.length', 2)
})
it('.prev() - get previous sibling DOM element', () => {
// https://on.cypress.io/prev
cy.get('.birds').find('.active')
.prev().should('contain', 'Lorikeets')
})
it('.prevAll() - get all previous sibling DOM elements', () => {
// https://on.cypress.io/prevAll
cy.get('.fruits-list').find('.third')
.prevAll().should('have.length', 2)
})
it('.prevUntil() - get all previous sibling DOM elements until el', () => {
// https://on.cypress.io/prevUntil
cy.get('.foods-list').find('#nuts')
.prevUntil('#veggies').should('have.length', 3)
})
it('.siblings() - get all sibling DOM elements', () => {
// https://on.cypress.io/siblings
cy.get('.traversal-pills .active')
.siblings().should('have.length', 2)
})
})

Просмотреть файл

@ -1,117 +0,0 @@
/// <reference types="Cypress" />
context('Utilities', () => {
beforeEach(() => {
cy.visit('https://example.cypress.io/utilities')
})
it('Cypress._ - call a lodash method', () => {
// https://on.cypress.io/_
cy.request('https://jsonplaceholder.cypress.io/users')
.then((response) => {
let ids = Cypress._.chain(response.body).map('id').take(3).value()
expect(ids).to.deep.eq([1, 2, 3])
})
})
it('Cypress.$ - call a jQuery method', () => {
// https://on.cypress.io/$
let $li = Cypress.$('.utility-jquery li:first')
cy.wrap($li)
.should('not.have.class', 'active')
.click()
.should('have.class', 'active')
})
it('Cypress.Blob - blob utilities and base64 string conversion', () => {
// https://on.cypress.io/blob
cy.get('.utility-blob').then(($div) =>
// https://github.com/nolanlawson/blob-util#imgSrcToDataURL
// get the dataUrl string for the javascript-logo
Cypress.Blob.imgSrcToDataURL('https://example.cypress.io/assets/img/javascript-logo.png', undefined, 'anonymous')
.then((dataUrl) => {
// create an <img> element and set its src to the dataUrl
let img = Cypress.$('<img />', { src: dataUrl })
// need to explicitly return cy here since we are initially returning
// the Cypress.Blob.imgSrcToDataURL promise to our test
// append the image
$div.append(img)
cy.get('.utility-blob img').click()
.should('have.attr', 'src', dataUrl)
}))
})
it('Cypress.minimatch - test out glob patterns against strings', () => {
// https://on.cypress.io/minimatch
let matching = Cypress.minimatch('/users/1/comments', '/users/*/comments', {
matchBase: true,
})
expect(matching, 'matching wildcard').to.be.true
matching = Cypress.minimatch('/users/1/comments/2', '/users/*/comments', {
matchBase: true,
})
expect(matching, 'comments').to.be.false
// ** matches against all downstream path segments
matching = Cypress.minimatch('/foo/bar/baz/123/quux?a=b&c=2', '/foo/**', {
matchBase: true,
})
expect(matching, 'comments').to.be.true
// whereas * matches only the next path segment
matching = Cypress.minimatch('/foo/bar/baz/123/quux?a=b&c=2', '/foo/*', {
matchBase: false,
})
expect(matching, 'comments').to.be.false
})
it('Cypress.moment() - format or parse dates using a moment method', () => {
// https://on.cypress.io/moment
const time = Cypress.moment().utc('2014-04-25T19:38:53.196Z').format('h:mm A')
expect(time).to.be.a('string')
cy.get('.utility-moment').contains('3:38 PM')
.should('have.class', 'badge')
})
it('Cypress.Promise - instantiate a bluebird promise', () => {
// https://on.cypress.io/promise
let waited = false
/**
* @return Bluebird<string>
*/
function waitOneSecond () {
// return a promise that resolves after 1 second
// @ts-ignore TS2351 (new Cypress.Promise)
return new Cypress.Promise((resolve, reject) => {
setTimeout(() => {
// set waited to true
waited = true
// resolve with 'foo' string
resolve('foo')
}, 1000)
})
}
cy.then(() =>
// return a promise to cy.then() that
// is awaited until it resolves
// @ts-ignore TS7006
waitOneSecond().then((str) => {
expect(str).to.eq('foo')
expect(waited).to.be.true
}))
})
})

Просмотреть файл

@ -1,59 +0,0 @@
/// <reference types="Cypress" />
context('Viewport', () => {
beforeEach(() => {
cy.visit('https://example.cypress.io/commands/viewport')
})
it('cy.viewport() - set the viewport size and dimension', () => {
// https://on.cypress.io/viewport
cy.get('#navbar').should('be.visible')
cy.viewport(320, 480)
// the navbar should have collapse since our screen is smaller
cy.get('#navbar').should('not.be.visible')
cy.get('.navbar-toggle').should('be.visible').click()
cy.get('.nav').find('a').should('be.visible')
// lets see what our app looks like on a super large screen
cy.viewport(2999, 2999)
// cy.viewport() accepts a set of preset sizes
// to easily set the screen to a device's width and height
// We added a cy.wait() between each viewport change so you can see
// the change otherwise it is a little too fast to see :)
cy.viewport('macbook-15')
cy.wait(200)
cy.viewport('macbook-13')
cy.wait(200)
cy.viewport('macbook-11')
cy.wait(200)
cy.viewport('ipad-2')
cy.wait(200)
cy.viewport('ipad-mini')
cy.wait(200)
cy.viewport('iphone-6+')
cy.wait(200)
cy.viewport('iphone-6')
cy.wait(200)
cy.viewport('iphone-5')
cy.wait(200)
cy.viewport('iphone-4')
cy.wait(200)
cy.viewport('iphone-3')
cy.wait(200)
// cy.viewport() accepts an orientation for all presets
// the default orientation is 'portrait'
cy.viewport('ipad-2', 'portrait')
cy.wait(200)
cy.viewport('iphone-4', 'landscape')
cy.wait(200)
// The viewport will be reset back to the default dimensions
// in between tests (the default can be set in cypress.json)
})
})

Просмотреть файл

@ -1,34 +0,0 @@
/// <reference types="Cypress" />
context('Waiting', () => {
beforeEach(() => {
cy.visit('https://example.cypress.io/commands/waiting')
})
// BE CAREFUL of adding unnecessary wait times.
// https://on.cypress.io/best-practices#Unnecessary-Waiting
// https://on.cypress.io/wait
it('cy.wait() - wait for a specific amount of time', () => {
cy.get('.wait-input1').type('Wait 1000ms after typing')
cy.wait(1000)
cy.get('.wait-input2').type('Wait 1000ms after typing')
cy.wait(1000)
cy.get('.wait-input3').type('Wait 1000ms after typing')
cy.wait(1000)
})
it('cy.wait() - wait for a specific route', () => {
cy.server()
// Listen to GET to comments/1
cy.route('GET', 'comments/*').as('getComment')
// we have code that gets a comment when
// the button is clicked in scripts.js
cy.get('.network-btn').click()
// wait for GET comments/1
cy.wait('@getComment').its('status').should('eq', 200)
})
})

Просмотреть файл

@ -1,22 +0,0 @@
/// <reference types="Cypress" />
context('Window', () => {
beforeEach(() => {
cy.visit('https://example.cypress.io/commands/window')
})
it('cy.window() - get the global window object', () => {
// https://on.cypress.io/window
cy.window().should('have.property', 'top')
})
it('cy.document() - get the document object', () => {
// https://on.cypress.io/document
cy.document().should('have.property', 'charset').and('eq', 'UTF-8')
})
it('cy.title() - get the title', () => {
// https://on.cypress.io/title
cy.title().should('include', 'Kitchen Sink')
})
})

Просмотреть файл

@ -6,6 +6,7 @@
"@babel/parser": "^7.11.3",
"@types/react": "16.9.23",
"bl": "^4.0.3",
"browserslist": "^4.16.5",
"elliptic": "^6.5.3",
"kind-of": "^6.0.3",
"lodash": "^4.17.21",
@ -34,13 +35,14 @@
"packages/adaptive-form",
"packages/client",
"packages/electron-server",
"packages/extension",
"packages/extension-client",
"packages/extension",
"packages/form-dialogs",
"packages/integration-tests",
"packages/intellisense",
"packages/lib/*",
"packages/server",
"packages/server-workers",
"packages/server",
"packages/test-utils",
"packages/tools/built-in-functions",
"packages/tools/language-servers/*",
@ -48,7 +50,8 @@
"packages/ui-plugins/*"
],
"nohoist": [
"**/server-workers/**"
"**/server-workers/**",
"**/integration-tests/**"
]
},
"scripts": {
@ -71,14 +74,12 @@
"start:server": "yarn workspace @bfc/server start",
"start:server:dev": "yarn workspace @bfc/server start:dev",
"runtime": "cd ../runtime/dotnet/azurewebapp && dotnet build && dotnet run",
"test": "cross-env NODE_OPTIONS=--max-old-space-size=4096 yarn typecheck && jest",
"test": "cross-env NODE_OPTIONS=--max-old-space-size=4096 yarn typecheck && jest --silent",
"test:watch": "yarn typecheck && jest --watch",
"test:coverage": "yarn test --coverage --no-cache --forceExit --reporters=default --silent",
"test:integration": "cypress run --browser edge",
"test:integration:start-server": "node scripts/e2e.js",
"test:integration:open": "cypress open",
"test:integration:clean": "node scripts/clean-e2e.js",
"test:integration:clean-all": "node scripts/clean-e2e.js --all",
"test:ci": "cross-env CI=true yarn test --coverage --no-cache --forceExit --reporters=default --reporters=jest-github-actions-reporter --testLocationInResults",
"test:integration": "yarn workspace @bfc/integration-tests start",
"test:integration:start-server": "yarn workspace @bfc/integration-tests start:server",
"test:integration:open": "yarn workspace @bfc/integration-tests open",
"lint": "wsrun --exclude-missing --collect-logs --report lint",
"lint:ci": "wsrun --exclude-missing --collect-logs --report --no-prefix lint --format github-actions",
"lint:fix": "wsrun --exclude-missing --collect-logs --report lint:fix",
@ -119,20 +120,14 @@
"@babel/preset-typescript": "^7.9.0",
"@bfc/eslint-plugin-bfcomposer": "*",
"@emotion/babel-preset-css-prop": "^10.0.27",
"@testing-library/cypress": "7.0.0-beta.1",
"@testing-library/user-event": "12.6.3",
"@typescript-eslint/eslint-plugin": "2.34.0",
"@typescript-eslint/parser": "2.34.0",
"chalk": "^4.0.0",
"concurrently": "^4.1.0",
"coveralls": "^3.1.0",
"cross-env": "^6.0.3",
"cypress": "^5.0.0",
"cypress-plugin-tab": "^1.0.5",
"eslint": "7.0.0",
"eslint-config-prettier": "6.11.0",
"eslint-formatter-github-actions": "^1.0.0",
"eslint-plugin-cypress": "2.11.1",
"eslint-plugin-emotion": "10.0.27",
"eslint-plugin-format-message": "6.2.3",
"eslint-plugin-import": "2.20.2",

Просмотреть файл

@ -24,8 +24,8 @@ const recognizers = [
{
id: 'TestRecognizer',
displayName: 'TestRecognizer',
intentEditor: ({ id, onChange }) => (
<div id={id}>
intentEditor: ({ id, onChange, label }) => (
<div aria-label={label} id={id}>
Test Recognizer <button onClick={onChange}>Update</button>
</div>
),

Просмотреть файл

@ -1,6 +1,6 @@
let mock = jest.createMockFromModule('@azure/arm-resources');
mock.ResourceManagementClient = () => {
function ResourceManagementClient() {
return {
resourceGroups: {
list: async () => {
@ -14,6 +14,8 @@ mock.ResourceManagementClient = () => {
},
},
};
};
}
mock.ResourceManagementClient = ResourceManagementClient;
module.exports = mock;

Просмотреть файл

@ -2,7 +2,7 @@
// Licensed under the MIT License.
let mock = jest.createMockFromModule('@azure/arm-subscriptions');
mock.SubscriptionClient = () => {
function SubscriptionClient() {
return {
subscriptions: {
list: async () => {
@ -22,6 +22,7 @@ mock.SubscriptionClient = () => {
},
},
};
};
}
mock.SubscriptionClient = SubscriptionClient;
module.exports = mock;

Просмотреть файл

@ -0,0 +1,4 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
module.exports = jest.createMockFromModule('axios');

Просмотреть файл

@ -8,12 +8,11 @@ import { OpenEmulatorButton } from '../../../src/components/BotRuntimeController
import { botEndpointsState, botStatusState, settingsState } from '../../../src/recoilModel';
import { BotStatus } from '../../../src/constants';
import { renderWithRecoil } from '../../testUtils';
const mockCallEmulator = jest.fn();
import { openInEmulator } from '../../../src/utils/navigation';
jest.mock('../../../src/utils/navigation', () => {
return {
openInEmulator: mockCallEmulator,
openInEmulator: jest.fn(),
};
});
@ -44,7 +43,7 @@ const initialState = ({ currentStatus = BotStatus.connected } = {}) => ({ set })
describe('<OpenEmulatorButton />', () => {
it('should show the button to open emulator', async () => {
mockCallEmulator.mockImplementationOnce((url) => {
(openInEmulator as jest.Mock).mockImplementationOnce((url) => {
expect(url).toBeDefined();
});
const { findByTestId } = renderWithRecoil(<OpenEmulatorButton isRootBot projectId={projectId} />, initialState());

Просмотреть файл

@ -90,10 +90,12 @@ describe('<FileSelector/>', () => {
it('should create a new folder', async () => {
const component = renderComponent();
const createFolderBtn = await component.findByText('Create new folder');
fireEvent.click(createFolderBtn);
const textField = await component.findByTestId('newFolderTextField');
fireEvent.change(textField, { target: { value: 'newFolder' } });
fireEvent.keyDown(textField, { key: 'Enter' });
await act(async () => {
fireEvent.click(createFolderBtn);
const textField = await component.findByTestId('newFolderTextField');
fireEvent.change(textField, { target: { value: 'newFolder' } });
fireEvent.keyDown(textField, { key: 'Enter' });
});
//locally this should be 'C:\\test-folder\\Desktop', but online it should be 'C:/test-folder/Desktop'
expect(
createFolder.mock.calls[0][0] === 'C:/test-folder/Desktop' ||

Просмотреть файл

@ -11,7 +11,7 @@ const serviceName = 'Language Understanding';
const DOWN_ARROW = { keyCode: 40 };
jest.mock('@azure/arm-cognitiveservices', () => ({
CognitiveServicesManagementClient: () => {
CognitiveServicesManagementClient: function CognitiveServicesManagementClient() {
return {
accounts: {
create: async () => {},
@ -103,7 +103,7 @@ describe('<ManageLuis />', () => {
const onNext = jest.fn();
const onToggleVisibility = jest.fn();
const { baseElement, findByText, findByTestId } = renderWithRecoil(
const { baseElement, findByText, findByTestId, findByRole } = renderWithRecoil(
<ManageLuis
hidden={false}
onDismiss={onDismiss}
@ -115,7 +115,7 @@ describe('<ManageLuis />', () => {
// test the default option (choose existing)
// click the next button, ensure the title changes
const nextButton = await findByText('Next');
const nextButton = await findByRole('button', { name: 'Next' });
expect(nextButton).toBeDefined();
await act(async () => {
await fireEvent.click(nextButton);
@ -136,7 +136,7 @@ describe('<ManageLuis />', () => {
// ensure that since a subscription hasn't been selected
// this button is disabled
const nextButton2 = await findByText('Next');
const nextButton2 = await findByRole('button', { name: 'Next' });
expect(nextButton2).toBeDefined();
expect(nextButton2).toBeDisabled();
@ -188,7 +188,7 @@ describe('<ManageLuis />', () => {
const onNext = jest.fn();
const onToggleVisibility = jest.fn();
const { baseElement, findByText, findByTestId } = renderWithRecoil(
const { baseElement, findByText, findByTestId, findByRole } = renderWithRecoil(
<ManageLuis
hidden={false}
onDismiss={onDismiss}
@ -204,7 +204,7 @@ describe('<ManageLuis />', () => {
fireEvent.click(createOption);
// click the next button, ensure the title changes
const nextButton = await findByText('Next');
const nextButton = await findByRole('button', { name: 'Next' });
expect(nextButton).toBeDefined();
await act(async () => {
await fireEvent.click(nextButton);
@ -213,7 +213,7 @@ describe('<ManageLuis />', () => {
// ensure that since a subscription hasn't been selected
// this button is disabled
const nextButton2 = await findByText('Next');
const nextButton2 = await findByRole('button', { name: 'Next' });
expect(nextButton2).toBeDefined();
expect(nextButton2).toBeDisabled();
@ -244,7 +244,7 @@ describe('<ManageLuis />', () => {
await fireEvent.click(nextButton2);
});
const nextButton3 = await findByText('Next');
const nextButton3 = await findByRole('button', { name: 'Next' });
expect(nextButton3).toBeDefined();
expect(nextButton3).toBeDisabled();
@ -303,7 +303,7 @@ describe('<ManageLuis />', () => {
const onNext = jest.fn();
const onToggleVisibility = jest.fn();
const { baseElement, findByText } = renderWithRecoil(
const { baseElement, findByText, findByRole } = renderWithRecoil(
<ManageLuis
hidden={false}
onDismiss={onDismiss}
@ -319,7 +319,7 @@ describe('<ManageLuis />', () => {
fireEvent.click(generateOption);
// click the next button, ensure the title changes
const nextButton = await findByText('Next');
const nextButton = await findByRole('button', { name: 'Next' });
expect(nextButton).toBeDefined();
await act(async () => {
await fireEvent.click(nextButton);

Просмотреть файл

@ -11,7 +11,7 @@ const serviceName = 'QnA Maker';
const DOWN_ARROW = { keyCode: 40 };
jest.mock('@azure/arm-appservice', () => ({
WebSiteManagementClient: () => {
WebSiteManagementClient: function WebSiteManagementClient() {
return {
appServicePlans: {
createOrUpdate: jest.fn(),
@ -30,7 +30,7 @@ jest.mock('@azure/arm-appservice', () => ({
},
}));
jest.mock('@azure/arm-search', () => ({
SearchManagementClient: () => {
SearchManagementClient: function SearchManagementClient() {
return {
services: {
createOrUpdate: jest.fn(),
@ -44,7 +44,7 @@ jest.mock('@azure/arm-search', () => ({
},
}));
jest.mock('@azure/arm-cognitiveservices', () => ({
CognitiveServicesManagementClient: () => {
CognitiveServicesManagementClient: function CognitiveServicesManagementClient() {
return {
accounts: {
create: async () => {},
@ -136,7 +136,7 @@ describe('<ManageQNA />', () => {
const onNext = jest.fn();
const onToggleVisibility = jest.fn();
const { baseElement, findByText, findByTestId } = renderWithRecoil(
const { baseElement, findByText, findByTestId, findByRole } = renderWithRecoil(
<ManageQNA
hidden={false}
onDismiss={onDismiss}
@ -148,7 +148,7 @@ describe('<ManageQNA />', () => {
// test the default option (choose existing)
// click the next button, ensure the title changes
const nextButton = await findByText('Next');
const nextButton = await findByRole('button', { name: 'Next' });
expect(nextButton).toBeDefined();
await act(async () => {
await fireEvent.click(nextButton);
@ -169,7 +169,7 @@ describe('<ManageQNA />', () => {
// ensure that since a subscription hasn't been selected
// this button is disabled
const nextButton2 = await findByText('Next');
const nextButton2 = await findByRole('button', { name: 'Next' });
expect(nextButton2).toBeDefined();
expect(nextButton2).toBeDisabled();
@ -221,7 +221,7 @@ describe('<ManageQNA />', () => {
const onNext = jest.fn();
const onToggleVisibility = jest.fn();
const { baseElement, findByText, findByTestId } = renderWithRecoil(
const { baseElement, findByText, findByTestId, findByRole } = renderWithRecoil(
<ManageQNA
hidden={false}
onDismiss={onDismiss}
@ -237,7 +237,7 @@ describe('<ManageQNA />', () => {
fireEvent.click(createOption);
// click the next button, ensure the title changes
const nextButton = await findByText('Next');
const nextButton = await findByRole('button', { name: 'Next' });
expect(nextButton).toBeDefined();
await act(async () => {
await fireEvent.click(nextButton);
@ -246,7 +246,7 @@ describe('<ManageQNA />', () => {
// ensure that since a subscription hasn't been selected
// this button is disabled
const nextButton2 = await findByText('Next');
const nextButton2 = await findByRole('button', { name: 'Next' });
expect(nextButton2).toBeDefined();
expect(nextButton2).toBeDisabled();
@ -277,7 +277,7 @@ describe('<ManageQNA />', () => {
await fireEvent.click(nextButton2);
});
const nextButton3 = await findByText('Next');
const nextButton3 = await findByRole('button', { name: 'Next' });
expect(nextButton3).toBeDefined();
expect(nextButton3).toBeDisabled();
@ -358,7 +358,7 @@ describe('<ManageQNA />', () => {
const onNext = jest.fn();
const onToggleVisibility = jest.fn();
const { baseElement, findByText } = renderWithRecoil(
const { baseElement, findByText, findByRole } = renderWithRecoil(
<ManageQNA
hidden={false}
onDismiss={onDismiss}
@ -374,7 +374,7 @@ describe('<ManageQNA />', () => {
fireEvent.click(generateOption);
// click the next button, ensure the title changes
const nextButton = await findByText('Next');
const nextButton = await findByRole('button', { name: 'Next' });
expect(nextButton).toBeDefined();
await act(async () => {
await fireEvent.click(nextButton);

Просмотреть файл

@ -17,7 +17,7 @@ const handoffInstructions = 'handoffInstructions';
const DOWN_ARROW = { keyCode: 40 };
jest.mock('@azure/arm-cognitiveservices', () => ({
CognitiveServicesManagementClient: () => {
CognitiveServicesManagementClient: function CognitiveServicesManagementClient() {
return {
accounts: {
list: async () => {
@ -129,7 +129,7 @@ describe('<ManageService />', () => {
const onNext = jest.fn();
const onToggleVisibility = jest.fn();
const { baseElement, findByText, findByTestId } = renderWithRecoil(
const { baseElement, findByText, findByTestId, findByRole } = renderWithRecoil(
<ManageService
createService={createService}
handoffInstructions={handoffInstructions}
@ -148,10 +148,10 @@ describe('<ManageService />', () => {
// test the default option (choose existing)
// click the next button, ensure the title changes
const nextButton = await findByText('Next');
const nextButton = await findByRole('button', { name: 'Next' });
expect(nextButton).toBeDefined();
await act(async () => {
await fireEvent.click(nextButton);
act(() => {
fireEvent.click(nextButton);
});
const tenantOption = await findByTestId('service-useexisting-tenant-selection');
@ -169,45 +169,48 @@ describe('<ManageService />', () => {
// ensure that since a subscription hasn't been selected
// this button is disabled
const nextButton2 = await findByText('Next');
const nextButton2 = await findByRole('button', { name: 'Next' });
expect(nextButton2).toBeDefined();
expect(nextButton2).toBeDisabled();
// select a subscription
await act(async () => {
await fireEvent.keyDown(subscriptionOption, DOWN_ARROW);
act(() => {
fireEvent.keyDown(subscriptionOption, DOWN_ARROW);
});
const mySub = await findByText('mockSubscription');
expect(mySub).toBeDefined();
await act(async () => {
await fireEvent.click(mySub);
act(() => {
fireEvent.click(mySub);
});
// select a resource group
const resourceOption = await findByTestId('service-useexisting-key-selection');
expect(resourceOption).toBeDefined();
expect(resourceOption).toBeEnabled();
await act(async () => {
await fireEvent.keyDown(resourceOption, DOWN_ARROW);
act(() => {
fireEvent.keyDown(resourceOption, DOWN_ARROW);
});
// select the key
const myKey = await findByText('mockedAccount');
expect(myKey).toBeDefined();
await act(async () => {
await fireEvent.click(myKey);
act(() => {
fireEvent.click(myKey);
});
// make sure the next button is appropriately enabled
expect(nextButton2).toBeEnabled();
// click next
await act(async () => {
await fireEvent.click(nextButton2);
act(() => {
fireEvent.click(nextButton2);
});
// let promises flush
await Promise.resolve();
// ensure that the final callback was called
expect(onGetKey).toBeCalledWith({
region: 'westus',
@ -222,7 +225,7 @@ describe('<ManageService />', () => {
const onNext = jest.fn();
const onToggleVisibility = jest.fn();
const { baseElement, findByText, findByTestId } = renderWithRecoil(
const { baseElement, findByText, findByTestId, findByRole } = renderWithRecoil(
<ManageService
createService={createService}
handoffInstructions={handoffInstructions}
@ -245,16 +248,16 @@ describe('<ManageService />', () => {
fireEvent.click(createOption);
// click the next button, ensure the title changes
const nextButton = await findByText('Next');
const nextButton = await findByRole('button', { name: 'Next' });
expect(nextButton).toBeDefined();
await act(async () => {
await fireEvent.click(nextButton);
act(() => {
fireEvent.click(nextButton);
});
expect(baseElement).toHaveTextContent(`Create ${serviceName} resources`);
// ensure that since a subscription hasn't been selected
// this button is disabled
const nextButton2 = await findByText('Next');
const nextButton2 = await findByRole('button', { name: 'Next' });
expect(nextButton2).toBeDefined();
expect(nextButton2).toBeDisabled();
@ -267,25 +270,25 @@ describe('<ManageService />', () => {
expect(subscriptionOption).toBeEnabled();
// choose subscription
await act(async () => {
await fireEvent.keyDown(subscriptionOption, DOWN_ARROW);
act(() => {
fireEvent.keyDown(subscriptionOption, DOWN_ARROW);
});
const mySub = await findByText('mockSubscription');
expect(mySub).toBeDefined();
await act(async () => {
await fireEvent.click(mySub);
act(() => {
fireEvent.click(mySub);
});
// next button should now be enabled
expect(nextButton2).toBeEnabled();
await act(async () => {
await fireEvent.click(nextButton2);
act(() => {
fireEvent.click(nextButton2);
});
const nextButton3 = await findByText('Next');
const nextButton3 = await findByRole('button', { name: 'Next' });
expect(nextButton3).toBeDefined();
expect(nextButton3).toBeDisabled();
@ -298,16 +301,16 @@ describe('<ManageService />', () => {
expect(resourceName).toBeEnabled();
// choose subscription
await act(async () => {
await fireEvent.click(resourceOption);
act(() => {
fireEvent.click(resourceOption);
});
const myGroup = await findByText('mockedGroup');
expect(myGroup).toBeDefined();
await act(async () => {
await fireEvent.click(myGroup);
await fireEvent.change(resourceName, { target: { value: 'mockedResource' } });
act(() => {
fireEvent.click(myGroup);
fireEvent.change(resourceName, { target: { value: 'mockedResource' } });
});
// select region
@ -315,22 +318,25 @@ describe('<ManageService />', () => {
expect(regionOption).toBeDefined();
expect(regionOption).toBeEnabled();
// choose subscription
await act(async () => {
await fireEvent.keyDown(regionOption, DOWN_ARROW);
act(() => {
fireEvent.keyDown(regionOption, DOWN_ARROW);
});
const myRegion = await findByText('West US');
expect(myRegion).toBeDefined();
await act(async () => {
await fireEvent.click(myRegion);
act(() => {
fireEvent.click(myRegion);
});
expect(nextButton3).toBeEnabled();
await act(async () => {
await fireEvent.click(nextButton3);
act(() => {
fireEvent.click(nextButton3);
});
// let promises flush
await Promise.resolve();
expect(createService).toBeCalledWith(
expect.anything(),
'mockSubscription',
@ -354,7 +360,7 @@ describe('<ManageService />', () => {
const onNext = jest.fn();
const onToggleVisibility = jest.fn();
const { baseElement, findByText, findByTestId } = renderWithRecoil(
const { baseElement, findByText, findByTestId, findByRole } = renderWithRecoil(
<ManageService
createService={createService}
handoffInstructions={handoffInstructions}
@ -378,16 +384,16 @@ describe('<ManageService />', () => {
fireEvent.click(createOption);
// click the next button, ensure the title changes
const nextButton = await findByText('Next');
const nextButton = await findByRole('button', { name: 'Next' });
expect(nextButton).toBeDefined();
await act(async () => {
await fireEvent.click(nextButton);
act(() => {
fireEvent.click(nextButton);
});
expect(baseElement).toHaveTextContent(`Create ${serviceName} resources`);
// ensure that since a subscription hasn't been selected
// this button is disabled
const nextButton2 = await findByText('Next');
const nextButton2 = await findByRole('button', { name: 'Next' });
expect(nextButton2).toBeDefined();
expect(nextButton2).toBeDisabled();
@ -400,25 +406,25 @@ describe('<ManageService />', () => {
expect(subscriptionOption).toBeEnabled();
// choose subscription
await act(async () => {
await fireEvent.keyDown(subscriptionOption, DOWN_ARROW);
act(() => {
fireEvent.keyDown(subscriptionOption, DOWN_ARROW);
});
const mySub = await findByText('mockSubscription');
expect(mySub).toBeDefined();
await act(async () => {
await fireEvent.click(mySub);
act(() => {
fireEvent.click(mySub);
});
// next button should now be enabled
expect(nextButton2).toBeEnabled();
await act(async () => {
await fireEvent.click(nextButton2);
act(() => {
fireEvent.click(nextButton2);
});
const nextButton3 = await findByText('Next');
const nextButton3 = await findByRole('button', { name: 'Next' });
expect(nextButton3).toBeDefined();
expect(nextButton3).toBeDisabled();
@ -431,16 +437,16 @@ describe('<ManageService />', () => {
expect(resourceName).toBeEnabled();
// choose subscription
await act(async () => {
await fireEvent.click(resourceOption);
act(() => {
fireEvent.click(resourceOption);
});
const myGroup = await findByText('mockedGroup');
expect(myGroup).toBeDefined();
await act(async () => {
await fireEvent.click(myGroup);
await fireEvent.change(resourceName, { target: { value: 'mockedResource' } });
act(() => {
fireEvent.click(myGroup);
fireEvent.change(resourceName, { target: { value: 'mockedResource' } });
});
// select region
@ -448,15 +454,15 @@ describe('<ManageService />', () => {
expect(regionOption).toBeDefined();
expect(regionOption).toBeEnabled();
// choose subscription
await act(async () => {
await fireEvent.keyDown(regionOption, DOWN_ARROW);
act(() => {
fireEvent.keyDown(regionOption, DOWN_ARROW);
});
const myRegion = await findByText('West US');
expect(myRegion).toBeDefined();
await act(async () => {
await fireEvent.click(myRegion);
act(() => {
fireEvent.click(myRegion);
});
// NEXT BUTTON SHOULD STILL BE DISABLED! need to do tier selection!
@ -466,24 +472,27 @@ describe('<ManageService />', () => {
expect(tierOption).toBeDefined();
expect(tierOption).toBeEnabled();
// choose subscription
await act(async () => {
await fireEvent.keyDown(tierOption, DOWN_ARROW);
act(() => {
fireEvent.keyDown(tierOption, DOWN_ARROW);
});
const myTier = await findByText('mockedTier');
expect(myTier).toBeDefined();
await act(async () => {
await fireEvent.click(myTier);
act(() => {
fireEvent.click(myTier);
});
// finally the button should now be enabled
expect(nextButton3).toBeEnabled();
await act(async () => {
await fireEvent.click(nextButton3);
act(() => {
fireEvent.click(nextButton3);
});
// let promises flush
await Promise.resolve();
expect(createService).toBeCalledWith(
expect.anything(),
'mockSubscription',
@ -507,7 +516,7 @@ describe('<ManageService />', () => {
const onNext = jest.fn();
const onToggleVisibility = jest.fn();
const { baseElement, findByText, findByTestId } = renderWithRecoil(
const { baseElement, findByText, findByTestId, findByRole } = renderWithRecoil(
<ManageService
createService={createService}
handoffInstructions={handoffInstructions}
@ -530,16 +539,16 @@ describe('<ManageService />', () => {
fireEvent.click(createOption);
// click the next button, ensure the title changes
const nextButton = await findByText('Next');
const nextButton = await findByRole('button', { name: 'Next' });
expect(nextButton).toBeDefined();
await act(async () => {
await fireEvent.click(nextButton);
act(() => {
fireEvent.click(nextButton);
});
expect(baseElement).toHaveTextContent(`Create ${serviceName} resources`);
// ensure that since a subscription hasn't been selected
// this button is disabled
const nextButton2 = await findByText('Next');
const nextButton2 = await findByRole('button', { name: 'Next' });
expect(nextButton2).toBeDefined();
expect(nextButton2).toBeDisabled();
@ -552,25 +561,25 @@ describe('<ManageService />', () => {
expect(subscriptionOption).toBeEnabled();
// choose subscription
await act(async () => {
await fireEvent.keyDown(subscriptionOption, DOWN_ARROW);
act(() => {
fireEvent.keyDown(subscriptionOption, DOWN_ARROW);
});
const mySub = await findByText('mockSubscription');
expect(mySub).toBeDefined();
await act(async () => {
await fireEvent.click(mySub);
act(() => {
fireEvent.click(mySub);
});
// next button should now be enabled
expect(nextButton2).toBeEnabled();
await act(async () => {
await fireEvent.click(nextButton2);
act(() => {
fireEvent.click(nextButton2);
});
const nextButton3 = await findByText('Next');
const nextButton3 = await findByRole('button', { name: 'Next' });
expect(nextButton3).toBeDefined();
expect(nextButton3).toBeDisabled();
@ -583,16 +592,16 @@ describe('<ManageService />', () => {
expect(resourceName).toBeEnabled();
// choose subscription
await act(async () => {
await fireEvent.click(resourceOption);
act(() => {
fireEvent.click(resourceOption);
});
const myGroup = await findByText('mockedGroup');
expect(myGroup).toBeDefined();
await act(async () => {
await fireEvent.click(myGroup);
await fireEvent.change(resourceName, { target: { value: 'mockedResource' } });
act(() => {
fireEvent.click(myGroup);
fireEvent.change(resourceName, { target: { value: 'mockedResource' } });
});
// select region
@ -600,15 +609,15 @@ describe('<ManageService />', () => {
expect(regionOption).toBeDefined();
expect(regionOption).toBeEnabled();
// choose subscription
await act(async () => {
await fireEvent.keyDown(regionOption, DOWN_ARROW);
act(() => {
fireEvent.keyDown(regionOption, DOWN_ARROW);
});
const myRegion = await findByText('West US');
expect(myRegion).toBeDefined();
await act(async () => {
await fireEvent.click(myRegion);
act(() => {
fireEvent.click(myRegion);
});
// NEXT BUTTON SHOULD STILL BE DISABLED! need to do tier selection!
@ -618,24 +627,27 @@ describe('<ManageService />', () => {
expect(tierOption).toBeDefined();
expect(tierOption).toBeEnabled();
// choose subscription
await act(async () => {
await fireEvent.keyDown(tierOption, DOWN_ARROW);
act(() => {
fireEvent.keyDown(tierOption, DOWN_ARROW);
});
const myTier = await findByText('mockedTier');
expect(myTier).toBeDefined();
await act(async () => {
await fireEvent.click(myTier);
act(() => {
fireEvent.click(myTier);
});
// finally the button should now be enabled
expect(nextButton3).toBeEnabled();
await act(async () => {
await fireEvent.click(nextButton3);
act(() => {
fireEvent.click(nextButton3);
});
// let promises flush
await Promise.resolve();
expect(createService).toBeCalledWith(
expect.anything(),
'mockSubscription',
@ -659,7 +671,7 @@ describe('<ManageService />', () => {
const onNext = jest.fn();
const onToggleVisibility = jest.fn();
const { baseElement, findByText } = renderWithRecoil(
const { baseElement, findByText, findByRole } = renderWithRecoil(
<ManageService
createService={createService}
handoffInstructions={handoffInstructions}
@ -682,10 +694,10 @@ describe('<ManageService />', () => {
fireEvent.click(generateOption);
// click the next button, ensure the title changes
const nextButton = await findByText('Next');
const nextButton = await findByRole('button', { name: 'Next' });
expect(nextButton).toBeDefined();
await act(async () => {
await fireEvent.click(nextButton);
act(() => {
fireEvent.click(nextButton);
});
expect(baseElement).toHaveTextContent(

Просмотреть файл

@ -11,7 +11,7 @@ const serviceName = 'Speech';
const DOWN_ARROW = { keyCode: 40 };
jest.mock('@azure/arm-cognitiveservices', () => ({
CognitiveServicesManagementClient: () => {
CognitiveServicesManagementClient: function CognitiveServicesManagementClient() {
return {
accounts: {
create: async () => {},
@ -103,7 +103,7 @@ describe('<ManageSpeech />', () => {
const onNext = jest.fn();
const onToggleVisibility = jest.fn();
const { baseElement, findByText, findByTestId } = renderWithRecoil(
const { baseElement, findByText, findByTestId, findByRole } = renderWithRecoil(
<ManageSpeech
hidden={false}
onDismiss={onDismiss}
@ -115,10 +115,10 @@ describe('<ManageSpeech />', () => {
// test the default option (choose existing)
// click the next button, ensure the title changes
const nextButton = await findByText('Next');
const nextButton = await findByRole('button', { name: 'Next' });
expect(nextButton).toBeDefined();
await act(async () => {
await fireEvent.click(nextButton);
act(() => {
fireEvent.click(nextButton);
});
const tenantOption = await findByTestId('service-useexisting-tenant-selection');
@ -136,7 +136,7 @@ describe('<ManageSpeech />', () => {
// ensure that since a subscription hasn't been selected
// this button is disabled
const nextButton2 = await findByText('Next');
const nextButton2 = await findByRole('button', { name: 'Next' });
expect(nextButton2).toBeDefined();
expect(nextButton2).toBeDisabled();
@ -188,7 +188,7 @@ describe('<ManageSpeech />', () => {
const onNext = jest.fn();
const onToggleVisibility = jest.fn();
const { baseElement, findByText, findByTestId } = renderWithRecoil(
const { baseElement, findByText, findByTestId, findByRole } = renderWithRecoil(
<ManageSpeech
hidden={false}
onDismiss={onDismiss}
@ -204,7 +204,7 @@ describe('<ManageSpeech />', () => {
fireEvent.click(createOption);
// click the next button, ensure the title changes
const nextButton = await findByText('Next');
const nextButton = await findByRole('button', { name: 'Next' });
expect(nextButton).toBeDefined();
await act(async () => {
await fireEvent.click(nextButton);
@ -213,7 +213,7 @@ describe('<ManageSpeech />', () => {
// ensure that since a subscription hasn't been selected
// this button is disabled
const nextButton2 = await findByText('Next');
const nextButton2 = await findByRole('button', { name: 'Next' });
expect(nextButton2).toBeDefined();
expect(nextButton2).toBeDisabled();
@ -244,7 +244,7 @@ describe('<ManageSpeech />', () => {
await fireEvent.click(nextButton2);
});
const nextButton3 = await findByText('Next');
const nextButton3 = await findByRole('button', { name: 'Next' });
expect(nextButton3).toBeDefined();
expect(nextButton3).toBeDisabled();
@ -304,7 +304,7 @@ describe('<ManageSpeech />', () => {
const onNext = jest.fn();
const onToggleVisibility = jest.fn();
const { baseElement, findByText } = renderWithRecoil(
const { baseElement, findByText, findByRole } = renderWithRecoil(
<ManageSpeech
hidden={false}
onDismiss={onDismiss}
@ -320,7 +320,7 @@ describe('<ManageSpeech />', () => {
fireEvent.click(generateOption);
// click the next button, ensure the title changes
const nextButton = await findByText('Next');
const nextButton = await findByRole('button', { name: 'Next' });
expect(nextButton).toBeDefined();
await act(async () => {
await fireEvent.click(nextButton);

Просмотреть файл

@ -12,8 +12,6 @@ import CreateSkillModal, {
} from '../../src/components/AddRemoteSkillModal/CreateSkillModal';
import { botProjectFileState, currentProjectIdState, settingsState } from '../../src/recoilModel';
jest.mock('../../src//utils/httpUtil');
jest.mock('../../src/components/Modal/dialogStyle', () => ({}));
const projectId = '123a.234';
@ -105,6 +103,8 @@ describe('<SkillForm />', () => {
value: 'https://onenote-dev.azurewebsites.net/manifests/OneNoteSync-2-1-preview-1-manifest.json',
},
});
// allow validatation debounce to execute
jest.runAllTimers();
});
expect(urlInput.getAttribute('value')).toBe(

Просмотреть файл

@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import * as React from 'react';
import React from 'react';
import { render } from '@botframework-composer/test-utils';
import { BASEPATH } from '../src/constants';

Просмотреть файл

@ -2,8 +2,7 @@
// Licensed under the MIT License.
import React from 'react';
import { act, fireEvent, within } from '@botframework-composer/test-utils';
import userEvent from '@testing-library/user-event';
import { act, fireEvent, within, userEvent } from '@botframework-composer/test-utils';
import ExternalAdapterSettings from '../../../src/pages/botProject/adapters/ExternalAdapterSettings';
import { renderWithRecoilAndCustomDispatchers } from '../../testUtils';
@ -140,7 +139,7 @@ describe('ExternalAdapterSettings', () => {
});
});
it('does not proceed if required settings are missing', async () => {
it('does not proceed if required settings are missing', () => {
const { getByTestId } = renderWithRecoilAndCustomDispatchers(
<ExternalAdapterSettings projectId={PROJECT_ID} />,
initRecoilState
@ -152,10 +151,10 @@ describe('ExternalAdapterSettings', () => {
});
const modal = getByTestId('adapterModal');
expect(within(modal).getByText('Configure')).toBeDisabled();
expect(within(modal).getByRole('button', { name: 'Configure' })).toBeDisabled();
});
it('disables an adapter', async () => {
it('disables an adapter', () => {
const initStateWithAdapter = {
runtimeSettings: {
adapters: [{ name: 'Adapter.Mock', enabled: true, route: 'mock', type: 'Adapter.Full.Type.Mock' }],
@ -176,7 +175,7 @@ describe('ExternalAdapterSettings', () => {
const toggle = queryByTestId('toggle_Adapter.Mock');
expect(toggle).not.toBeNull();
await act(async () => {
act(() => {
fireEvent.click(toggle!);
});
@ -190,7 +189,7 @@ describe('ExternalAdapterSettings', () => {
);
});
it('enables an adapter', async () => {
it('enables an adapter', () => {
const initStateWithAdapter = {
runtimeSettings: {
adapters: [{ name: 'Adapter.Mock', enabled: false, route: 'mock', type: 'Adapter.Full.Type.Mock' }],
@ -210,7 +209,7 @@ describe('ExternalAdapterSettings', () => {
const toggle = queryByTestId('toggle_Adapter.Mock');
expect(toggle).not.toBeNull();
await act(async () => {
act(() => {
fireEvent.click(toggle!);
});

Просмотреть файл

@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { renderHook, HookResult } from '@botframework-composer/test-utils/lib/hooks';
import { renderHook, RenderResult } from '@botframework-composer/test-utils/lib/hooks';
import * as React from 'react';
import { RecoilRoot } from 'recoil';
@ -31,7 +31,7 @@ const state = {
describe('use lgApi hooks', () => {
let removeLgTemplatesMock, initRecoilState, copyLgTemplateMock, updateLgTemplateMock;
let result: HookResult<any>;
let result: RenderResult<any>;
beforeEach(() => {
updateLgTemplateMock = jest.fn();

Просмотреть файл

@ -4,9 +4,7 @@
import { getExtension, getBaseName, upperCaseName, loadLocale, getUniqueName } from '../../src/utils/fileUtil';
import httpClient from '../../src/utils/httpUtil';
jest.mock('../../src/utils/httpUtil');
const files = ['a.text', 'a.b.text', 1];
const files = ['a.text', 'a.b.text', 1] as string[];
describe('getExtension', () => {
it('returns extension', () => {
@ -48,19 +46,19 @@ describe('loadLocale', () => {
const LOCALE = 'en-test';
it("does not set locale if it can't find one", async () => {
jest.spyOn(httpClient, 'get').mockImplementation(() => ({ data: null }));
(httpClient.get as jest.Mock).mockImplementation(() => ({ data: null }));
expect(await loadLocale(LOCALE)).toBeNull();
});
it('does not set locale if the server returns an error page', async () => {
jest.spyOn(httpClient, 'get').mockImplementation(() => ({ data: 'error page' }));
(httpClient.get as jest.Mock).mockImplementation(() => ({ data: 'error page' }));
expect(await loadLocale(LOCALE)).toBeNull();
});
it('sets locale if it does find one', async () => {
const RESPONSE = { data: { abc: 'def' } };
jest.spyOn(httpClient, 'get').mockImplementation(() => RESPONSE);
(httpClient.get as jest.Mock).mockImplementation(() => RESPONSE);
expect(await loadLocale(LOCALE)).toMatchObject({
locale: LOCALE,

Просмотреть файл

@ -1,5 +1,9 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
/// <reference types="jest" />
// for tests using Electron IPC to talk to main process
(window as any).ipcRenderer = { on: jest.fn() };
jest.mock('./src/utils/httpUtil');

Просмотреть файл

@ -54,7 +54,7 @@ export interface CreateSkillModalProps {
onDismiss: () => void;
}
export const validateManifestUrl = async ({ formData, formDataErrors, setFormDataErrors }, skills: string[] = []) => {
export const validateManifestUrl = ({ formData, formDataErrors, setFormDataErrors }, skills: string[] = []) => {
const { manifestUrl } = formData;
const { manifestUrl: _, ...errors } = formDataErrors;
@ -71,6 +71,7 @@ export const validateManifestUrl = async ({ formData, formDataErrors, setFormDat
setFormDataErrors({});
}
};
export const getSkillManifest = async (projectId: string, manifestUrl: string, setSkillManifest, setFormDataErrors) => {
try {
const { data } = await httpClient.get(`/projects/${projectId}/skill/retrieveSkillManifest`, {

Просмотреть файл

@ -3,8 +3,7 @@
/** @jsx jsx */
import { jsx } from '@emotion/core';
import { useRecoilValue } from 'recoil';
import { Suspense, Fragment } from 'react';
import React from 'react';
import React, { Suspense, Fragment } from 'react';
import { onboardingDisabled } from '../../constants';
import { useLocation } from '../../utils/hooks';

Просмотреть файл

@ -13,12 +13,6 @@ const container = css`
position: relative;
`;
const top = css`
width: 100%;
height: 10px;
background-color: #efeaf5;
`;
// -------------------- Conversation -------------------- //
const Conversation = (props) => {

Просмотреть файл

@ -4,7 +4,7 @@
import { DialogTypes, DialogWrapper } from '@bfc/ui-shared/lib/components/DialogWrapper';
import { SDKKinds } from '@botframework-composer/types';
import { Button } from 'office-ui-fabric-react/lib/components/Button/Button';
import React, { useMemo } from 'react';
import React, { useMemo, useEffect } from 'react';
import { useRecoilState, useRecoilValue } from 'recoil';
import { enableOrchestratorDialog } from '../../constants';
@ -32,6 +32,12 @@ export const OrchestratorForSkillsDialog = () => {
return curRecognizers.some((f) => f.id === fileName && f.content.$kind === SDKKinds.OrchestratorRecognizer);
}, [curRecognizers, dialogId, locale]);
useEffect(() => {
if (showOrchestratorDialog && hasOrchestrator) {
setShowOrchestratorDialog(false);
}
}, [hasOrchestrator, showOrchestratorDialog]);
const handleOrchestratorSubmit = async (event: React.MouseEvent<HTMLElement | Button>, enable?: boolean) => {
event.preventDefault();
if (enable) {
@ -41,25 +47,14 @@ export const OrchestratorForSkillsDialog = () => {
setShowOrchestratorDialog(false);
};
const setVisibility = () => {
if (showOrchestratorDialog) {
if (hasOrchestrator) {
setShowOrchestratorDialog(false);
return false;
}
return true;
}
return false;
};
const onDismissHandler = (event: React.MouseEvent<HTMLButtonElement> | undefined) => {
const onDismissHandler = () => {
setShowOrchestratorDialog(false);
};
return (
<DialogWrapper
dialogType={DialogTypes.CreateFlow}
isOpen={setVisibility()}
isOpen={showOrchestratorDialog}
title={enableOrchestratorDialog.title}
onDismiss={onDismissHandler}
>

Просмотреть файл

@ -1,10 +1,9 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { act, getQueriesForElement, within } from '@botframework-composer/test-utils';
import { act, screen, userEvent } from '@botframework-composer/test-utils';
import { SDKKinds } from '@botframework-composer/types';
import * as React from 'react';
import userEvent from '@testing-library/user-event';
import { renderWithRecoil } from '../../../../__tests__/testUtils/renderWithRecoil';
import {
@ -42,55 +41,55 @@ describe('<OrchestratorForSkillsDialog />', () => {
});
it('should not open OrchestratorForSkillsDialog if orchestratorForSkillsDialogState is false', () => {
const { baseElement } = renderWithRecoil(<OrchestratorForSkillsDialog />, ({ set }) => {
renderWithRecoil(<OrchestratorForSkillsDialog />, ({ set }) => {
makeInitialState(set);
set(orchestratorForSkillsDialogState, false);
});
const dialog = getQueriesForElement(baseElement).queryByTestId(orchestratorTestId);
const dialog = screen.queryByTestId(orchestratorTestId);
expect(dialog).toBeNull();
});
it('should not open OrchestratorForSkillsDialog if orchestrator already being used in root', () => {
const { baseElement } = renderWithRecoil(<OrchestratorForSkillsDialog />, ({ set }) => {
renderWithRecoil(<OrchestratorForSkillsDialog />, ({ set }) => {
makeInitialState(set);
set(recognizersSelectorFamily('rootBotId'), [
{ id: 'rootBotRootDialogId.en-us.lu.dialog', content: { $kind: SDKKinds.OrchestratorRecognizer } },
]);
});
const dialog = getQueriesForElement(baseElement).queryByTestId(orchestratorTestId);
const dialog = screen.queryByTestId(orchestratorTestId);
expect(dialog).toBeNull();
});
it('open OrchestratorForSkillsDialog if orchestratorForSkillsDialogState and Orchestrator not used in Root Bot Root Dialog', () => {
const { baseElement } = renderWithRecoil(<OrchestratorForSkillsDialog />, ({ set }) => {
renderWithRecoil(<OrchestratorForSkillsDialog />, ({ set }) => {
makeInitialState(set);
});
const dialog = getQueriesForElement(baseElement).queryByTestId(orchestratorTestId);
const dialog = screen.queryByTestId(orchestratorTestId);
expect(dialog).toBeTruthy();
});
it('should install Orchestrator package when user clicks Continue', async () => {
const { baseElement } = renderWithRecoil(<OrchestratorForSkillsDialog />, ({ set }) => {
renderWithRecoil(<OrchestratorForSkillsDialog />, ({ set }) => {
makeInitialState(set);
});
await act(async () => {
userEvent.click(within(baseElement).getByTestId('import-orchestrator'));
userEvent.click(screen.getByTestId('import-orchestrator'));
});
expect(importOrchestrator).toBeCalledWith('rootBotId', expect.anything(), expect.anything());
});
it('should not install Orchestrator package when user clicks skip', async () => {
const { baseElement } = renderWithRecoil(<OrchestratorForSkillsDialog />, ({ set }) => {
renderWithRecoil(<OrchestratorForSkillsDialog />, ({ set }) => {
makeInitialState(set);
});
await act(async () => {
userEvent.click(await within(baseElement).findByText('Skip'));
userEvent.click(await screen.findByText('Skip'));
});
const dialog = getQueriesForElement(baseElement).queryByTestId(orchestratorTestId);
const dialog = screen.queryByTestId(orchestratorTestId);
expect(dialog).toBeNull();
expect(importOrchestrator).toBeCalledTimes(0);

Просмотреть файл

@ -78,7 +78,7 @@ export const ImportQnAFromUrlModal: React.FC<ImportQnAFromUrlModalProps> = (prop
<div css={dialogWindow}>
<Stack>
<TextField
data-testId={'ImportNewUrlToOverwriteQnAFile'}
data-testid="ImportNewUrlToOverwriteQnAFile"
errorMessage={formErrors.url}
label={formatMessage('FAQ website (source)')}
placeholder={formatMessage('Enter a URL to Import QnA resource')}

Просмотреть файл

@ -61,8 +61,12 @@ describe('<WebchatPanel />', () => {
mockstartNewConversation.mockResolvedValue({
directline: {
activity$: jest.fn(),
activity$: {
subscribe: jest.fn(),
},
subscribe: jest.fn(),
connectionStatus$: { subscribe: jest.fn() },
postActivity: jest.fn(),
},
chatMode: 'conversation',
projectId: '123-12',

Просмотреть файл

@ -9,8 +9,6 @@ import { botBuildTimeErrorState, botStatusState } from '../../../recoilModel';
import { BotStatus } from '../../../constants';
import { BotRuntimeStatus } from '../../BotRuntimeController/BotRuntimeStatus';
jest.mock('../../../utils/httpUtil');
const mockStart = jest.fn();
const mockStop = jest.fn();
const pollingInterval = 1500;

Просмотреть файл

@ -8,8 +8,6 @@ import { botBuildTimeErrorState, botStatusState } from '../../../recoilModel';
import { BotStatus, BotStatusesCopy } from '../../../constants';
import { BotStatusIndicator } from '../../BotRuntimeController/BotStatusIndicator';
jest.mock('../../../utils/httpUtil');
const mockStart = jest.fn();
const mockStop = jest.fn();

Просмотреть файл

@ -16,7 +16,6 @@ import {
import { OutputsTabContent } from '../DebugPanel/TabExtensions/RuntimeOutputLog/OutputTabContent';
const projectId = '123-avw';
jest.mock('../../../utils/httpUtil');
const standardOut = `/Users/tester/Desktop/conversational_core_3/conversational_core_3/conversational_core_3.csproj : warning NU1701: Package 'Microsoft.Azure.KeyVault.Core 1.0.0' was restored using '.NETFramework,Version=v4.6.1, .NETFramework,Version=v4.6.2, .NETFramework,Version=v4.7, .NETFramework,Version=v4.7.1, .NETFramework,Version=v4.7.2, .NETFramework,Version=v4.8' instead of the project target framework '.NETCoreApp,Version=v3.1'. This package may not be fully compatible with your project.
/Users/tester/Desktop/conversational_core_3/conversational_core_3/conversational_core_3.csproj : warning NU1701: Package 'Microsoft.Azure.KeyVault.Core 1.0.0' was restored using '.NETFramework,Version=v4.6.1, .NETFramework,Version=v4.6.2, .NETFramework,Version=v4.7, .NETFramework,Version=v4.7.1, .NETFramework,Version=v4.7.2, .NETFramework,Version=v4.8' instead of the project target framework '.NETCoreApp,Version=v3.1'. This package may not be fully compatible with your project.
info: Microsoft.Hosting.Lifetime[0]

Просмотреть файл

@ -352,7 +352,7 @@ const TableView: React.FC<TableViewProps> = (props) => {
return (
<IconButton
hidden
data-testId={'knowledgeBaseMore'}
data-testid="knowledgeBaseMore"
menuIconProps={{ iconName: 'More' }}
menuProps={{ items: overflowItems || [] }}
role="menuitem"

Просмотреть файл

@ -1,11 +1,10 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { useRef, useState, Fragment, useLayoutEffect, MutableRefObject } from 'react';
import React, { useRef, useState, Fragment, useLayoutEffect, MutableRefObject } from 'react';
// eslint-disable-next-line @typescript-eslint/camelcase
import { useRecoilTransactionObserver_UNSTABLE, Snapshot, useRecoilState } from 'recoil';
import once from 'lodash/once';
import React from 'react';
import { BotAssets } from '@bfc/shared';
import { useRecoilValue } from 'recoil';
import isEmpty from 'lodash/isEmpty';

Просмотреть файл

@ -2,7 +2,7 @@
// Licensed under the MIT License.
import { useRecoilState, useRecoilValue } from 'recoil';
import { act, RenderHookResult, HookResult } from '@botframework-composer/test-utils/lib/hooks';
import { act, RenderHookResult, RenderResult } from '@botframework-composer/test-utils/lib/hooks';
// eslint-disable-next-line lodash/import-scope
import debounce from 'lodash/debounce';
@ -23,7 +23,7 @@ import { StateError } from '../../types';
jest.mock('lodash/debounce');
(debounce as any).mockImplementation((fn) => fn);
describe('<Editor />', () => {
describe('application dispatchers', () => {
const useRecoilTestHook = () => {
const [appUpdater, setAppUpdater] = useRecoilState(appUpdateState);
const dispatcher = useRecoilValue(dispatcherState);
@ -42,9 +42,7 @@ describe('<Editor />', () => {
onboarding,
};
};
let renderedComponent: HookResult<ReturnType<typeof useRecoilTestHook>>, dispatcher: Dispatcher;
beforeAll(() => {});
let renderedComponent: RenderResult<ReturnType<typeof useRecoilTestHook>>, dispatcher: Dispatcher;
beforeEach(() => {
const rendered: RenderHookResult<unknown, ReturnType<typeof useRecoilTestHook>> = renderRecoilHook(

Просмотреть файл

@ -2,7 +2,7 @@
// Licensed under the MIT License.
import { selector, useRecoilValue, selectorFamily, useRecoilState } from 'recoil';
import { act, RenderHookResult, HookResult } from '@botframework-composer/test-utils/lib/hooks';
import { act, RenderHookResult, RenderResult } from '@botframework-composer/test-utils/lib/hooks';
import noop from 'lodash/noop';
import { BotProjectFile, Skill } from '@bfc/shared';
@ -23,7 +23,6 @@ import {
} from '../../atoms';
import { Dispatcher } from '..';
jest.mock('../../../utils/httpUtil');
const rootBotProjectId = '2345.32324';
const testSkillId = '123.1sd23';
@ -83,7 +82,7 @@ describe('Bot Project File dispatcher', () => {
};
};
let renderedComponent: HookResult<ReturnType<typeof useRecoilTestHook>>, dispatcher: Dispatcher;
let renderedComponent: RenderResult<ReturnType<typeof useRecoilTestHook>>, dispatcher: Dispatcher;
beforeEach(() => {
const rendered: RenderHookResult<unknown, ReturnType<typeof useRecoilTestHook>> = renderRecoilHook(
useRecoilTestHook,

Просмотреть файл

@ -3,7 +3,7 @@
import { useRecoilValue } from 'recoil';
import test from '@bfc/indexers';
import { act, HookResult } from '@botframework-composer/test-utils/lib/hooks';
import { act, RenderResult } from '@botframework-composer/test-utils/lib/hooks';
import { dialogsDispatcher } from '../dialogs';
import { renderRecoilHook } from '../../../../__tests__/testUtils';
@ -117,7 +117,7 @@ describe('dialog dispatcher', () => {
};
};
let renderedComponent: HookResult<ReturnType<typeof useRecoilTestHook>>, dispatcher: Dispatcher;
let renderedComponent: RenderResult<ReturnType<typeof useRecoilTestHook>>, dispatcher: Dispatcher;
beforeEach(() => {
const { result } = renderRecoilHook(useRecoilTestHook, {

Просмотреть файл

@ -2,7 +2,7 @@
// Licensed under the MIT License.
import { useRecoilValue } from 'recoil';
import { act, HookResult } from '@botframework-composer/test-utils/lib/hooks';
import { act, RenderResult } from '@botframework-composer/test-utils/lib/hooks';
import httpClient from '../../../utils/httpUtil';
import { exportDispatcher } from '../export';
@ -10,7 +10,6 @@ import { renderRecoilHook } from '../../../../__tests__/testUtils';
import { botDisplayNameState, currentProjectIdState, dispatcherState } from '../../atoms';
import { Dispatcher } from '../../../recoilModel/dispatchers';
jest.mock('../../../utils/httpUtil');
const projectId = '2345.32324';
describe('Export dispatcher', () => {
@ -23,7 +22,7 @@ describe('Export dispatcher', () => {
};
};
let renderedComponent: HookResult<ReturnType<typeof useRecoilTestHook>>,
let renderedComponent: RenderResult<ReturnType<typeof useRecoilTestHook>>,
dispatcher: Dispatcher,
prevDocumentCreateElement,
prevCreateObjectURL,

Просмотреть файл

@ -4,7 +4,7 @@
import { useRecoilState } from 'recoil';
import { LgFile, LgTemplate } from '@bfc/shared';
import { useRecoilValue } from 'recoil';
import { act, HookResult } from '@botframework-composer/test-utils/lib/hooks';
import { act, RenderResult } from '@botframework-composer/test-utils/lib/hooks';
import { lgDispatcher } from '../lg';
import { renderRecoilHook } from '../../../../__tests__/testUtils';
@ -70,7 +70,7 @@ describe('Lg dispatcher', () => {
};
};
let renderedComponent: HookResult<ReturnType<typeof useRecoilTestHook>>, dispatcher: Dispatcher;
let renderedComponent: RenderResult<ReturnType<typeof useRecoilTestHook>>, dispatcher: Dispatcher;
beforeEach(() => {
const { result } = renderRecoilHook(useRecoilTestHook, {

Просмотреть файл

@ -4,7 +4,7 @@
import { useRecoilState } from 'recoil';
import { LuIntentSection, LuFile } from '@bfc/shared';
import { useRecoilValue } from 'recoil';
import { act, HookResult } from '@botframework-composer/test-utils/lib/hooks';
import { act, RenderResult } from '@botframework-composer/test-utils/lib/hooks';
import { luUtil } from '@bfc/indexers';
import { renderRecoilHook } from '../../../../__tests__/testUtils';
@ -51,7 +51,7 @@ describe('Lu dispatcher', () => {
};
};
let renderedComponent: HookResult<ReturnType<typeof useRecoilTestHook>>, dispatcher: Dispatcher;
let renderedComponent: RenderResult<ReturnType<typeof useRecoilTestHook>>, dispatcher: Dispatcher;
beforeEach(() => {
const { result } = renderRecoilHook(useRecoilTestHook, {

Просмотреть файл

@ -2,7 +2,7 @@
// Licensed under the MIT License.
import { useRecoilValue } from 'recoil';
import { act, HookResult } from '@botframework-composer/test-utils/lib/hooks';
import { act, RenderResult } from '@botframework-composer/test-utils/lib/hooks';
import { renderRecoilHook } from '../../../../__tests__/testUtils';
import {
@ -62,7 +62,7 @@ describe('Multilang dispatcher', () => {
};
};
let renderedComponent: HookResult<ReturnType<typeof useRecoilTestHook>>, dispatcher: Dispatcher;
let renderedComponent: RenderResult<ReturnType<typeof useRecoilTestHook>>, dispatcher: Dispatcher;
beforeEach(() => {
const { result } = renderRecoilHook(useRecoilTestHook, {

Просмотреть файл

@ -2,7 +2,7 @@
// Licensed under the MIT License.
import { useRecoilValue } from 'recoil';
import { act, HookResult } from '@botframework-composer/test-utils/lib/hooks';
import { act, RenderResult } from '@botframework-composer/test-utils/lib/hooks';
import { SDKKinds } from '@bfc/shared';
import { navigationDispatcher } from '../navigation';
@ -53,7 +53,7 @@ describe('navigation dispatcher', () => {
};
};
let renderedComponent: HookResult<ReturnType<typeof useRecoilTestHook>>, dispatcher: Dispatcher;
let renderedComponent: RenderResult<ReturnType<typeof useRecoilTestHook>>, dispatcher: Dispatcher;
beforeEach(() => {
mockCheckUrl.mockClear();

Просмотреть файл

@ -3,7 +3,7 @@
import { selector, useRecoilValue } from 'recoil';
import { v4 as uuid } from 'uuid';
import { act, RenderHookResult, HookResult } from '@botframework-composer/test-utils/lib/hooks';
import { act, RenderHookResult, RenderResult } from '@botframework-composer/test-utils/lib/hooks';
import { useRecoilState } from 'recoil';
import cloneDeep from 'lodash/cloneDeep';
import endsWith from 'lodash/endsWith';
@ -46,26 +46,20 @@ import {
import { dialogsSelectorFamily, lgFilesSelectorFamily, luFilesSelectorFamily } from '../../selectors';
import { Dispatcher } from '../../dispatchers';
import { BotStatus } from '../../../constants';
import { navigateTo } from '../../../utils/navigation';
import mockProjectData from './mocks/mockProjectResponse.json';
import mockManifestData from './mocks/mockManifest.json';
import mockBotProjectFileData from './mocks/mockBotProjectFile.json';
// let httpMocks;
let navigateTo;
const projectId = '30876.502871204648';
jest.mock('../../../utils/navigation', () => {
const navigateMock = jest.fn();
navigateTo = navigateMock;
return {
navigateTo: navigateMock,
navigateTo: jest.fn(),
};
});
jest.mock('../../../utils/httpUtil');
jest.mock('../../parsers/lgWorker', () => {
return {
flush: () => new Promise((resolve) => resolve(null)),
@ -193,10 +187,10 @@ describe('Project dispatcher', () => {
};
};
let renderedComponent: HookResult<ReturnType<typeof useRecoilTestHook>>, dispatcher: Dispatcher;
let renderedComponent: RenderResult<ReturnType<typeof useRecoilTestHook>>, dispatcher: Dispatcher;
beforeEach(async () => {
navigateTo.mockReset();
(navigateTo as jest.Mock).mockReset();
mockProjectResponse = cloneDeep(mockProjectData);
mockManifestResponse = cloneDeep(mockManifestData);
mockBotProjectResponse = cloneDeep(mockBotProjectFileData);
@ -480,7 +474,7 @@ describe('Project dispatcher', () => {
expect(renderedComponent.current.botStates.oneNoteSync).toBeUndefined();
});
it('should be able to open a project and its skills in Bot project file', async (done) => {
it('should be able to open a project and its skills in Bot project file', async () => {
let callIndex = 0;
(httpClient.put as jest.Mock).mockImplementation(() => {
let mockSkillData: any;
@ -513,12 +507,9 @@ describe('Project dispatcher', () => {
await dispatcher.openProject('../test/empty-bot', 'default');
});
setImmediate(() => {
expect(renderedComponent.current.botStates.todoSkill.botDisplayName).toBe('todo-skill');
expect(renderedComponent.current.botStates.googleKeepSync.botDisplayName).toBe('google-keep-sync');
expect(renderedComponent.current.botProjectSpaceLoaded).toBeTruthy();
done();
});
expect(renderedComponent.current.botStates.todoSkill.botDisplayName).toBe('todo-skill');
expect(renderedComponent.current.botStates.googleKeepSync.botDisplayName).toBe('google-keep-sync');
expect(renderedComponent.current.botProjectSpaceLoaded).toBeTruthy();
});
it('should migrate skills from existing bots and add them to botproject file', async () => {

Просмотреть файл

@ -5,7 +5,7 @@ import { useRecoilState } from 'recoil';
import { QnAFile } from '@bfc/shared';
import { qnaUtil } from '@bfc/indexers';
import { useRecoilValue } from 'recoil';
import { act, HookResult } from '@botframework-composer/test-utils/lib/hooks';
import { act, RenderResult } from '@botframework-composer/test-utils/lib/hooks';
import { qnaDispatcher } from '../qna';
import { renderRecoilHook } from '../../../../__tests__/testUtils';
@ -67,7 +67,7 @@ describe('QnA dispatcher', () => {
};
};
let renderedComponent: HookResult<ReturnType<typeof useRecoilTestHook>>, dispatcher: Dispatcher;
let renderedComponent: RenderResult<ReturnType<typeof useRecoilTestHook>>, dispatcher: Dispatcher;
beforeEach(() => {
const { result } = renderRecoilHook(useRecoilTestHook, {

Просмотреть файл

@ -2,15 +2,13 @@
// Licensed under the MIT License.
import { useRecoilValue } from 'recoil';
import { act, HookResult } from '@botframework-composer/test-utils/lib/hooks';
import { act, RenderResult } from '@botframework-composer/test-utils/lib/hooks';
import { renderRecoilHook } from '../../../../__tests__/testUtils';
import { settingsState, currentProjectIdState, dispatcherState } from '../../atoms';
import { Dispatcher } from '..';
import { settingsDispatcher } from '../setting';
jest.mock('../../../utils/httpUtil');
const projectId = '1235a.2341';
const settings = {
@ -87,7 +85,7 @@ describe('setting dispatcher', () => {
};
};
let renderedComponent: HookResult<ReturnType<typeof useRecoilTestHook>>, dispatcher: Dispatcher;
let renderedComponent: RenderResult<ReturnType<typeof useRecoilTestHook>>, dispatcher: Dispatcher;
beforeEach(() => {
const { result } = renderRecoilHook(useRecoilTestHook, {

Просмотреть файл

@ -2,7 +2,7 @@
// Licensed under the MIT License.
import { selectorFamily, useRecoilState, useRecoilValue } from 'recoil';
import { act, HookResult } from '@botframework-composer/test-utils/lib/hooks';
import { act, RenderResult } from '@botframework-composer/test-utils/lib/hooks';
import { skillDispatcher } from '../skill';
import { botProjectFileDispatcher } from '../botProjectFile';
@ -22,25 +22,16 @@ import {
import { botEndpointsState, botProjectIdsState, currentProjectIdState, displaySkillManifestState } from '../../atoms';
import { Dispatcher } from '..';
import { skillsStateSelector } from '../../selectors';
import httpClient from '../../../utils/httpUtil';
import mockBotProjectFileData from './mocks/mockBotProjectFile.json';
jest.mock('../../../utils/httpUtil', () => {
return {
__esModule: true,
default: {
post: (url, skillObject) => ({
url,
data: skillObject.skills,
}),
},
};
});
const mockDialogComplete = jest.fn();
const projectId = '42345.23432';
const skillIds = ['1234.123', '234.234'];
(httpClient.post as jest.Mock).mockImplementation((url, skillObject) => ({ url, data: skillObject.skills }));
describe('skill dispatcher', () => {
const skillsDataSelector = selectorFamily({
key: 'skillSelector-skill',
@ -93,7 +84,7 @@ describe('skill dispatcher', () => {
};
};
let renderedComponent: HookResult<ReturnType<typeof useRecoilTestHook>>, dispatcher: Dispatcher;
let renderedComponent: RenderResult<ReturnType<typeof useRecoilTestHook>>, dispatcher: Dispatcher;
beforeEach(() => {
mockDialogComplete.mockClear();

Просмотреть файл

@ -2,29 +2,23 @@
// Licensed under the MIT License.
import { useRecoilValue } from 'recoil';
import { act, RenderHookResult, HookResult } from '@botframework-composer/test-utils/lib/hooks';
import { act, RenderHookResult, RenderResult } from '@botframework-composer/test-utils/lib/hooks';
import httpClient from '../../../utils/httpUtil';
import { storageDispatcher } from '../storage';
import { renderRecoilHook } from '../../../../__tests__/testUtils';
import { runtimeTemplatesState, currentProjectIdState, dispatcherState } from '../../atoms';
import { Dispatcher } from '../../dispatchers';
// let httpMocks;
let navigateTo;
import { navigateTo } from '../../../utils/navigation';
const projectId = '30876.502871204648';
jest.mock('../../../utils/navigation', () => {
const navigateMock = jest.fn();
navigateTo = navigateMock;
return {
navigateTo: navigateMock,
navigateTo: jest.fn(),
};
});
jest.mock('../../../utils/httpUtil');
jest.mock('../../parsers/lgWorker', () => {
return {
flush: () => new Promise((resolve) => resolve()),
@ -55,10 +49,10 @@ describe('Storage dispatcher', () => {
};
};
let renderedComponent: HookResult<ReturnType<typeof useRecoilTestHook>>, dispatcher: Dispatcher;
let renderedComponent: RenderResult<ReturnType<typeof useRecoilTestHook>>, dispatcher: Dispatcher;
beforeEach(() => {
navigateTo.mockReset();
(navigateTo as jest.Mock).mockReset();
const rendered: RenderHookResult<unknown, ReturnType<typeof useRecoilTestHook>> = renderRecoilHook(
useRecoilTestHook,
{

Просмотреть файл

@ -2,7 +2,7 @@
// Licensed under the MIT License.
import { useRecoilValue } from 'recoil';
import { act, HookResult } from '@botframework-composer/test-utils/lib/hooks';
import { act, RenderResult } from '@botframework-composer/test-utils/lib/hooks';
import { dialogsDispatcher } from '../dialogs';
import { triggerDispatcher } from '../trigger';
@ -118,7 +118,7 @@ describe('trigger dispatcher', () => {
qnaFiles,
};
};
let renderedComponent: HookResult<ReturnType<typeof useRecoilTestHook>>, dispatcher: Dispatcher;
let renderedComponent: RenderResult<ReturnType<typeof useRecoilTestHook>>, dispatcher: Dispatcher;
beforeEach(() => {
const { result } = renderRecoilHook(useRecoilTestHook, {

Просмотреть файл

@ -2,7 +2,7 @@
// Licensed under the MIT License.
import { useRecoilValue } from 'recoil';
import { act, HookResult } from '@botframework-composer/test-utils/lib/hooks';
import { act, RenderResult } from '@botframework-composer/test-utils/lib/hooks';
import jwtDecode from 'jwt-decode';
import { userDispatcher } from '../user';
@ -53,7 +53,7 @@ describe('user dispatcher', () => {
};
};
let renderedComponent: HookResult<ReturnType<typeof useRecoilTestHook>>, dispatcher: Dispatcher;
let renderedComponent: RenderResult<ReturnType<typeof useRecoilTestHook>>, dispatcher: Dispatcher;
beforeEach(() => {
process.env.COMPOSER_REQUIRE_AUTH = 'true'; // needs to be a string
@ -118,6 +118,7 @@ describe('user dispatcher', () => {
});
it('sets values given a decodable token', async () => {
const setTimeoutSpy = jest.spyOn(global, 'setTimeout');
mockJwtDecode.mockImplementationOnce(() => {
return {
exp: 12345,
@ -138,7 +139,7 @@ describe('user dispatcher', () => {
// 12345 is the expiration time in seconds, *1000 = 12345000
// 10000000 is the mock time we set
// 2045000 = 12345000 - 10000000 - (1000 * 60 * 5)
expect(setTimeout).toHaveBeenCalledWith(expect.anything(), 2045000);
expect(setTimeoutSpy).toHaveBeenCalledWith(expect.anything(), 2045000);
});
});
});

Просмотреть файл

@ -2,7 +2,7 @@
// Licensed under the MIT License.
import { useRecoilValue } from 'recoil';
import { act, HookResult } from '@botframework-composer/test-utils/lib/hooks';
import { act, RenderResult } from '@botframework-composer/test-utils/lib/hooks';
import { Dispatcher } from '..';
import { renderRecoilHook } from '../../../../__tests__/testUtils';
@ -29,7 +29,7 @@ describe('web chat dispatcher', () => {
};
};
let renderedComponent: HookResult<ReturnType<typeof useRecoilTestHook>>, dispatcher: Dispatcher;
let renderedComponent: RenderResult<ReturnType<typeof useRecoilTestHook>>, dispatcher: Dispatcher;
beforeEach(() => {
const { result } = renderRecoilHook(useRecoilTestHook, {

Просмотреть файл

@ -5,18 +5,6 @@ import { DialogInfo, DialogSchemaFile, LgFile, LuFile, BotAssets } from '@bfc/sh
import FilePersistence from '../FilePersistence';
const projectId = '2123.2234as';
jest.mock('axios', () => {
return {
create: jest.fn(() => {
return {
put: new Promise((resolve) => setTimeout(() => resolve({ data: {} }), 10)),
post: new Promise((resolve) => setTimeout(() => resolve({ data: {} }), 10)),
delete: new Promise((resolve) => setTimeout(() => resolve({ data: {} }), 10)),
};
}),
};
});
jest.mock('../../parsers/fileDiffCalculator', () => {
return {
difference: require('../../parsers/workers/calculator.worker').getDifferenceItems,

Просмотреть файл

@ -2,7 +2,7 @@
// Licensed under the MIT License.
import { selectorFamily, useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
import { act, RenderHookResult, HookResult } from '@botframework-composer/test-utils/lib/hooks';
import { act, RenderHookResult, RenderResult } from '@botframework-composer/test-utils/lib/hooks';
import noop from 'lodash/noop';
import { renderRecoilHook } from '../../../../__tests__/testUtils';
@ -62,7 +62,7 @@ const useRecoilTestHook = () => {
};
};
let renderedComponent: HookResult<ReturnType<typeof useRecoilTestHook>>;
let renderedComponent: RenderResult<ReturnType<typeof useRecoilTestHook>>;
beforeEach(() => {
const rendered: RenderHookResult<unknown, ReturnType<typeof useRecoilTestHook>> = renderRecoilHook(

Просмотреть файл

@ -5,7 +5,7 @@
import { jsx } from '@emotion/core';
import { act } from 'react-test-renderer';
import { useRecoilValue, useSetRecoilState, useRecoilState } from 'recoil';
import { HookResult } from '@botframework-composer/test-utils/lib/hooks';
import { RenderResult } from '@botframework-composer/test-utils/lib/hooks';
import { UndoRoot, undoFunctionState, undoHistoryState } from '../history';
import {
@ -55,7 +55,7 @@ describe('<UndoRoot/>', () => {
};
};
let renderedComponent: HookResult<ReturnType<typeof useRecoilTestHook>>;
let renderedComponent: RenderResult<ReturnType<typeof useRecoilTestHook>>;
beforeEach(() => {
const { result } = renderRecoilHook(useRecoilTestHook, {

Просмотреть файл

@ -6,8 +6,6 @@ import { TelemetryEventTypes } from '@bfc/shared';
import httpClient from '../../utils/httpUtil';
import AppInsightsClient from '../AppInsightsClient';
jest.mock('../../utils/httpUtil');
describe('Application Insights Logger', () => {
beforeEach(() => {
jest.clearAllMocks();

Просмотреть файл

@ -2,5 +2,6 @@ const { createConfig } = require('@botframework-composer/test-utils');
module.exports = createConfig('electron-server', 'node', {
setupFilesAfterEnv: ['./__tests__/setupTests.js'],
testPathIgnorePatterns: ['/node_modules/', '/__tests__/setupTests.js'],
testPathIgnorePatterns: ['/node_modules/', '/__tests__/setupTests.js', 'dist'],
modulePathIgnorePatterns: ['dist'],
});

Просмотреть файл

@ -0,0 +1,9 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
module.exports = {
extends: ['../../.eslintrc.js', 'plugin:cypress/recommended'],
rules: {
'cypress/no-unnecessary-waiting': 'warn',
},
};

Просмотреть файл

@ -1,9 +1,6 @@
{
"projectId": "27ikaq",
"ignoreTestFiles": [
"**/examples/*",
"*.hot-update.js"
],
"ignoreTestFiles": ["**/examples/*", "*.hot-update.js"],
"supportFile": "cypress/support/index.ts",
"video": false,
"videoUploadOnPasses": false,

Просмотреть файл

Просмотреть файл

@ -2,4 +2,4 @@
"name": "Using fixtures to represent data",
"email": "hello@cypress.io",
"body": "Fixtures are a great way to mock data for responses to routes"
}
}

Просмотреть файл

@ -0,0 +1,3 @@
{
"error": "Access denied due to invalid subscription key. Make sure you are subscribed to an API you are trying to call and provide the right key."
}

Просмотреть файл

@ -0,0 +1,20 @@
{
"status": {
"MyProject_main_en-us_lu": {
"version": "0000000001",
"checksum": "",
"status": 1
}
},
"luFiles": [
{
"content": "#Dummy\r\n--I am Dummy",
"diagnostics": [],
"id": "Main",
"intents": [],
"relativePath": "Main/Main.lu",
"lastPublishTime": 2,
"lastUpdateTime": 1
}
]
}

Просмотреть файл

@ -2,4 +2,4 @@
"id": 8739,
"name": "Jane",
"email": "jane@example.com"
}
}

Просмотреть файл

@ -229,4 +229,4 @@
"bs": "target end-to-end models"
}
}
]
]

Просмотреть файл

@ -24,7 +24,7 @@ context('Luis Deploy', () => {
method: 'POST',
url: 'api/projects/*/build',
status: 400,
response: 'fixture:luPublish/failure',
response: 'fixture:luPublish/error',
});
cy.findByTestId('startBotButton').click();
cy.findByTestId('runtime-logs-sidebar');
@ -37,5 +37,7 @@ context('Luis Deploy', () => {
});
cy.findByTestId('startBotButton').click();
cy.findByTitle(/^Starting bot../);
cy.route('GET', '/api/publish/*/status/default', { endpointURL: 'anything', status: 200 });
cy.route('PUT', '/api/projects/*/files/appsettings.json', { status: 200 });
});
});

Просмотреть файл

@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
module.exports = on => {};
module.exports = (on) => {};

Просмотреть файл

@ -9,19 +9,19 @@ declare namespace Cypress {
* Creates a bot based on empty bot.
* @example cy.createBot('EmptySample', ()=> {})
*/
createBot(botName: string, createdCallback: (bot: any) => void): void;
createBot(botName: string, createdCallback?: (bot: any) => void): void;
/**
* Creates a bot based on empty bot.
* @example cy.createTemplateBot('EmptySample', ()=> {})
*/
createTemplateBot(botName: string, createdCallback: (bot: any) => void): void;
createTemplateBot(botName: string, createdCallback?: (bot: any) => void): void;
/**
* Creates a bot from above created template bot.
* @example cy.createTestBot('EmptySample', ()=> {})
*/
createTestBot(botName: string, createdCallback: (bot: any) => void): void;
createTestBot(botName: string, createdCallback?: (bot: any) => void): void;
/**
* Visits a page from the left nav bar using the page's testid

Просмотреть файл

@ -5,7 +5,7 @@ import '@testing-library/cypress/add-commands';
let TemplateBotProjectId = '';
Cypress.Commands.add('createBot', (botName: string, callback: (bot: any) => void) => {
Cypress.Commands.add('createBot', (botName: string, callback?: (bot: any) => void) => {
const params = {
description: '',
location: '',
@ -15,10 +15,10 @@ Cypress.Commands.add('createBot', (botName: string, callback: (bot: any) => void
schemaUrl: '',
storageId: 'default',
templateId: '@microsoft/generator-bot-empty',
templateVersion: '1.0.0-rc3',
templateVersion: '1.0.0',
};
const pollingRequestBotStatus = (jobId: string, callback: (result: any) => void) => {
const pollingRequestBotStatus = (jobId: string, callback?: (result: any) => void) => {
cy.wait(2000);
try {
cy.request('get', `/api/status/${jobId}`).then((res) => {
@ -26,10 +26,11 @@ Cypress.Commands.add('createBot', (botName: string, callback: (bot: any) => void
if (httpStatusCode !== 200) {
pollingRequestBotStatus(id, callback);
} else {
callback(result);
callback?.(result);
}
});
} catch (error) {
console.error(error);
pollingRequestBotStatus(jobId, callback);
}
};
@ -37,18 +38,19 @@ Cypress.Commands.add('createBot', (botName: string, callback: (bot: any) => void
cy.request('post', '/api/projects', params).then((res) => {
const { jobId } = res.body;
// install package can take a long time.
cy.wait(20000);
pollingRequestBotStatus(jobId, (result) => callback(result));
cy.wait(5000);
pollingRequestBotStatus(jobId, (result) => callback?.(result));
});
});
Cypress.Commands.add('createTemplateBot', (botName: string, callback: (bot: any) => void) => {
Cypress.Commands.add('createTemplateBot', (botName: string, callback?: (bot: any) => void) => {
cy.createBot(`TemplateBot_${botName}`, (bot) => {
TemplateBotProjectId = bot.id;
callback?.(bot);
});
});
Cypress.Commands.add('createTestBot', (botName: string, callback: (bot: any) => void) => {
Cypress.Commands.add('createTestBot', (botName: string, callback?: (bot: any) => void) => {
const name = `TestBot_${botName}`;
const params = {
@ -59,7 +61,7 @@ Cypress.Commands.add('createTestBot', (botName: string, callback: (bot: any) =>
};
cy.request('post', `/api/projects/${TemplateBotProjectId}/project/saveAs`, params).then((res) => {
callback(res.body);
callback?.(res.body);
});
});

Просмотреть файл

@ -4,14 +4,12 @@
import './commands';
before(() => {
cy.exec('yarn test:integration:clean-all');
cy.createTemplateBot('EmptySample', ({ id }) => {
cy.visit(`/bot/${id}`);
});
cy.exec('yarn clean-all');
cy.createTemplateBot('EmptySample');
});
beforeEach(() => {
cy.exec('yarn test:integration:clean');
cy.exec('yarn clean');
window.localStorage.setItem('composer:userSettings', JSON.stringify({ telemetry: { allowDataCollection: false } }));
window.localStorage.setItem('composer:OnboardingState', JSON.stringify({ complete: true }));
window.sessionStorage.setItem('composer:ProjectIdCache', '');
@ -20,5 +18,5 @@ beforeEach(() => {
after(() => {
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(500);
cy.exec('yarn test:integration:clean-all');
cy.exec('yarn clean-all');
});

Просмотреть файл

@ -3,7 +3,7 @@
"strict": true,
"baseUrl": "../node_modules",
"target": "es5",
"lib": ["es5", "dom"],
"lib": ["es2015", "dom"],
"types": ["cypress", "@testing-library/cypress", "./support/commands"]
},
"include": ["**/*.ts"]

Просмотреть файл

@ -0,0 +1,26 @@
{
"name": "@bfc/integration-tests",
"version": "1.0.0",
"description": "Composer end-to-end tests using the cypress testing framework.",
"scripts": {
"start": "cypress run --browser edge",
"start:server": "node scripts/e2e.js",
"open": "cypress open",
"clean": "node scripts/clean-e2e.js",
"clean-all": "node scripts/clean-e2e.js --all",
"lint": "eslint --quiet cypress"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"@testing-library/cypress": "7.0.6",
"chalk": "^4.0.0",
"cypress": "^7.5.0",
"eslint-plugin-cypress": "2.11.1"
},
"devDependencies": {
"@types/fs-extra": "^9.0.11",
"fs-extra": "^10.0.0"
}
}

Просмотреть файл

@ -4,10 +4,13 @@ function cleanup {
kill $SERVER_PID
}
# move up to composer root dir
cd ../..
yarn start >> e2e.log 2>&1 &
SERVER_PID=$!
npx cypress run
cd packages/integration-tests
npx cypress run "$@"
EXIT_CODE=$?
# kill server process

Просмотреть файл

@ -7,15 +7,14 @@
/* eslint-disable security/detect-child-process */
const path = require('path');
const fs = require('fs');
const { promisify } = require('util');
const { spawn, execSync } = require('child_process');
const fs = require('fs-extra');
const chalk = require('chalk');
const mkdir = promisify(fs.mkdir);
const rootDir = path.resolve(__dirname, '..');
const composerRootDir = path.resolve(rootDir, '..', '..');
process.env.COMPOSER_BOTS_FOLDER = path.resolve(rootDir, 'cypress/__test_bots__');
process.env.COMPOSER_APP_DATA = path.resolve(rootDir, 'cypress/__e2e_data.json');
@ -61,7 +60,7 @@ Wait for the server to come up and then start cypress.
async function setup() {
try {
await mkdir(process.env.COMPOSER_BOTS_FOLDER);
await fs.ensureDir(process.env.COMPOSER_BOTS_FOLDER);
} catch (err) {
process.stderr.write('There was a problem setting up.\n');
process.stderr.write(`Error:\n${err.message}\n`);
@ -71,7 +70,12 @@ async function setup() {
async function run() {
return new Promise((resolve) => {
const startCommand = isDev ? 'start:dev' : 'start';
const server = spawn('yarn', [startCommand], { cwd: path.resolve(rootDir), stdio: 'inherit' });
const spawnOptions = { cwd: composerRootDir, stdio: 'inherit' };
if (process.platform === 'win32') {
spawnOptions.shell = true;
}
const server = spawn('yarn', [startCommand], spawnOptions);
server.on('close', () => {
resolve();

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше