Enhance E2E Reporting + CI Reliability (#325)
* Yarn * Enhance E2E Reporting + CI Reliability * Nit - Formatting * Removing sleep call - originated from RNW. Will observe how this affects the pipeline
This commit is contained in:
Родитель
c5b508f7fa
Коммит
00aa319f77
|
@ -0,0 +1,46 @@
|
|||
parameters:
|
||||
platform:
|
||||
applicationType:
|
||||
buildArtifacts:
|
||||
|
||||
steps:
|
||||
# Generate reports. This task doesn't end until a ctrl+c is pressed. I'm not sure how to end it in this case, so
|
||||
# I added a 1 minute timer. It should always generate the report in this time, then the task timeouts and continues on the pipeline.
|
||||
# continueOnError is true because the pipeline should continue even on failure
|
||||
- script: |
|
||||
yarn report
|
||||
workingDirectory: apps\${{parameters.platform}}
|
||||
continueOnError: true
|
||||
timeoutInMinutes: 1
|
||||
displayName: "generate ${{parameters.platform}} report"
|
||||
condition: and (succeededOrFailed(), eq(${{parameters.buildArtifacts}}, 'success'))
|
||||
|
||||
# Copy errorShots to FluentTesterDump
|
||||
- task: CopyFiles@2
|
||||
inputs:
|
||||
sourceFolder: $(Build.SourcesDirectory)\apps\${{parameters.platform}}\errorShots
|
||||
targetFolder: $(Build.StagingDirectory)/E2E_${{parameters.applicationType}}_Dump
|
||||
displayName: "Copy tree dump screenshots"
|
||||
condition: and (succeededOrFailed(), eq(${{parameters.buildArtifacts}}, 'success'))
|
||||
|
||||
# Copy appium log to FluentTesterDump
|
||||
- task: CopyFiles@2
|
||||
inputs:
|
||||
sourceFolder: $(Build.SourcesDirectory)\apps\${{parameters.platform}}\reports\
|
||||
contents: "*"
|
||||
targetFolder: $(Build.StagingDirectory)/E2E_${{parameters.applicationType}}_Dump
|
||||
displayName: "Copy tree dump report logs"
|
||||
condition: and (succeededOrFailed(), eq(${{parameters.buildArtifacts}}, 'success'))
|
||||
|
||||
- powershell: |
|
||||
Move-Item -Path $(Build.SourcesDirectory)\apps\${{parameters.platform}}\allure-report -Destination $(Build.StagingDirectory)/E2E_${{parameters.applicationType}}_Dump
|
||||
displayName: "Copy allure report"
|
||||
condition: and (succeededOrFailed(), eq(${{parameters.buildArtifacts}}, 'success'))
|
||||
|
||||
# Publish FluentTesterDump
|
||||
- task: PublishBuildArtifacts@1
|
||||
inputs:
|
||||
artifactName: E2E_${{parameters.applicationType}}_Dump
|
||||
pathtoPublish: $(Build.StagingDirectory)/E2E_${{parameters.applicationType}}_Dump
|
||||
displayName: "Publish Artifact:E2E_${{parameters.applicationType}}_Dump"
|
||||
condition: and (succeededOrFailed(), eq(${{parameters.buildArtifacts}}, 'success'))
|
|
@ -38,18 +38,34 @@ steps:
|
|||
workingDirectory: apps\windows
|
||||
displayName: "run UWP app"
|
||||
|
||||
# Wait for app to launch. A workaround to avoid WinAppDriver error: Failed to locate opened application window with appId
|
||||
- powershell: |
|
||||
Start-Sleep -Seconds 60
|
||||
displayName: "Wait for app to launch"
|
||||
|
||||
# Kill FluentTester, leave server up and running. This was the only way I could get the server continuously running
|
||||
# as a separate process.
|
||||
- powershell: |
|
||||
Stop-Process -Name FluentTester
|
||||
displayName: "Kill FluentTester Process"
|
||||
|
||||
# Creates a variable that determines whether the previous build tasks succeeded.
|
||||
# Usage: We want the tasks that generate reports to run for both passing/failing E2E testing tasks. In order to do so, we need to make
|
||||
# those reporting tasks run even on when certain previous tasks fail. This variable allows us to differentiate build failures from
|
||||
# E2E testing failures. Thus, if this variable != "Success", we know the build failed, and to not run the reporting tasks.
|
||||
- task: PowerShell@2
|
||||
inputs:
|
||||
targetType: "inline"
|
||||
script: |
|
||||
Write-Host "##vso[task.setvariable variable=task.Build.status]Success"
|
||||
condition: succeeded()
|
||||
displayName: "Create success build variable"
|
||||
|
||||
- script: |
|
||||
yarn e2etest
|
||||
workingDirectory: apps\windows
|
||||
displayName: "run E2E UWP tests"
|
||||
condition: succeeded()
|
||||
|
||||
# The following condition (using task.Build.status variable) make it so the reports generate even if the E2E tasks fails,
|
||||
# but not if the initial repo build steps fail.
|
||||
- template: e2e-publish-artifacts.yml
|
||||
parameters:
|
||||
applicationType: UWP
|
||||
platform: windows
|
||||
buildArtifacts: variables['task.Build.status']
|
||||
|
|
|
@ -4,7 +4,26 @@ steps:
|
|||
workingDirectory: apps/win32
|
||||
displayName: "yarn bundle"
|
||||
|
||||
# Creates a variable that determines whether the previous build tasks succeeded.
|
||||
# Usage: We want the tasks that generate reports to run for both passing/failing E2E testing tasks. In order to do so, we need to make
|
||||
# those reporting tasks run even on when certain previous tasks fail. This variable allows us to differentiate build failures from
|
||||
# E2E testing failures. Thus, if this variable != "Success", we know the build failed, and to not run the reporting tasks.
|
||||
- task: PowerShell@2
|
||||
inputs:
|
||||
targetType: "inline"
|
||||
script: |
|
||||
Write-Host "##vso[task.setvariable variable=task.Build.status]Success"
|
||||
condition: succeeded()
|
||||
displayName: "Create success build variable"
|
||||
|
||||
- script: |
|
||||
yarn e2etest
|
||||
workingDirectory: apps/win32
|
||||
displayName: "run E2E Win32 tests"
|
||||
condition: succeeded()
|
||||
|
||||
- template: e2e-publish-artifacts.yml
|
||||
parameters:
|
||||
applicationType: win32
|
||||
platform: win32
|
||||
buildArtifacts: variables['task.Build.status']
|
||||
|
|
|
@ -6,6 +6,8 @@ yarn-debug.log*
|
|||
yarn-error.log*
|
||||
**/reports/
|
||||
**/errorShots/
|
||||
**/allure-results/
|
||||
**/allure-report/
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
|
|
|
@ -12,64 +12,87 @@ import SeparatorTestPage from '../../Separator/pages/SeparatorTestPage.win';
|
|||
import TextTestPage from '../../Text/pages/TextTestPage.win';
|
||||
import ThemeTestPage from '../../Theme/pages/ThemeTestPage.win';
|
||||
|
||||
describe('Click on each test page and check if it renders', function() {
|
||||
const BOOT_APP_TIMEOUT = 60000;
|
||||
const PAGE_TIMEOUT = 3000;
|
||||
|
||||
// Before testing begins, allow up to 60 seconds for app to open
|
||||
describe('Open the app', function () {
|
||||
it('Boot app', () => {
|
||||
BootTestPage.waitForPageDisplayed(BOOT_APP_TIMEOUT);
|
||||
expect(BootTestPage.isPageLoaded()).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Click on each test page and check if it renders', function () {
|
||||
it('Button Test Page', () => {
|
||||
BootTestPage.clickAndGoToButtonPage();
|
||||
ButtonTestPage.waitForPageDisplayed(PAGE_TIMEOUT);
|
||||
expect(ButtonTestPage.isPageLoaded()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('Callout Test Page', () => {
|
||||
BootTestPage.clickAndGoToCalloutPage();
|
||||
CalloutTestPage.waitForPageDisplayed(PAGE_TIMEOUT);
|
||||
expect(CalloutTestPage.isPageLoaded()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('Checkbox Test Page', () => {
|
||||
BootTestPage.clickAndGoToCheckboxPage();
|
||||
CheckboxTestPage.waitForPageDisplayed(PAGE_TIMEOUT);
|
||||
expect(CheckboxTestPage.isPageLoaded()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('FocusTrapZone Test Page', () => {
|
||||
BootTestPage.clickAndGoToFocusTrapZonePage();
|
||||
FocusTrapZoneTestPage.waitForPageDisplayed(PAGE_TIMEOUT);
|
||||
expect(FocusTrapZoneTestPage.isPageLoaded()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('Link Test Page', () => {
|
||||
BootTestPage.clickAndGoToLinkPage();
|
||||
LinkTestPage.waitForPageDisplayed(PAGE_TIMEOUT);
|
||||
expect(LinkTestPage.isPageLoaded()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('Persona Test Page', () => {
|
||||
BootTestPage.clickAndGoToPersonaPage();
|
||||
PersonaTestPage.waitForPageDisplayed(PAGE_TIMEOUT);
|
||||
expect(PersonaTestPage.isPageLoaded()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('PersonaCoin Test Page', () => {
|
||||
BootTestPage.clickAndGoToPersonaCoinPage();
|
||||
PersonaCoinTestPage.waitForPageDisplayed(PAGE_TIMEOUT);
|
||||
expect(PersonaCoinTestPage.isPageLoaded()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('Pressable Test Page', () => {
|
||||
BootTestPage.clickAndGoToPressablePage();
|
||||
PressableTestPage.waitForPageDisplayed(PAGE_TIMEOUT);
|
||||
expect(PressableTestPage.isPageLoaded()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('RadioGroup Test Page', () => {
|
||||
BootTestPage.clickAndGoToRadioGroupPage();
|
||||
RadioGroupTestPage.waitForPageDisplayed(PAGE_TIMEOUT);
|
||||
expect(RadioGroupTestPage.isPageLoaded()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('Separator Test Page', () => {
|
||||
BootTestPage.clickAndGoToSeparatorPage();
|
||||
SeparatorTestPage.waitForPageDisplayed(PAGE_TIMEOUT);
|
||||
expect(SeparatorTestPage.isPageLoaded()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('Text Test Page', () => {
|
||||
BootTestPage.clickAndGoToTextPage();
|
||||
TextTestPage.waitForPageDisplayed(PAGE_TIMEOUT);
|
||||
expect(TextTestPage.isPageLoaded()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('Theme Test Page', () => {
|
||||
BootTestPage.clickAndGoToThemePage();
|
||||
ThemeTestPage.waitForPageDisplayed(PAGE_TIMEOUT);
|
||||
expect(ThemeTestPage.isPageLoaded()).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -10,61 +10,75 @@ import SeparatorTestPage from '../../Separator/pages/SeparatorTestPage.win';
|
|||
import TextTestPage from '../../Text/pages/TextTestPage.win';
|
||||
import ThemeTestPage from '../../Theme/pages/ThemeTestPage.win';
|
||||
|
||||
const PAGE_TIMEOUT = 45000;
|
||||
const BOOT_APP_TIMEOUT = 60000;
|
||||
const PAGE_TIMEOUT = 3000;
|
||||
|
||||
// Before testing begins, allow 45 seconds for bundle to load (WebDriverIO)
|
||||
beforeAll(() => {
|
||||
BootTestPage.waitForPageLoaded(PAGE_TIMEOUT);
|
||||
// Before testing begins, allow up to 60 seconds for bundle to load (WebDriverIO)
|
||||
describe('Open the app', function() {
|
||||
it('Boot app', () => {
|
||||
BootTestPage.waitForPageDisplayed(BOOT_APP_TIMEOUT);
|
||||
expect(BootTestPage.isPageLoaded()).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Click on each test page and check if it renders', function() {
|
||||
describe('Click on each test page and check if it renders', function () {
|
||||
it('Button Test Page', () => {
|
||||
BootTestPage.clickAndGoToButtonPage();
|
||||
ButtonTestPage.waitForPageDisplayed(PAGE_TIMEOUT);
|
||||
expect(ButtonTestPage.isPageLoaded()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('Callout Test Page', () => {
|
||||
BootTestPage.clickAndGoToCalloutPage();
|
||||
CalloutTestPage.waitForPageDisplayed(PAGE_TIMEOUT);
|
||||
expect(CalloutTestPage.isPageLoaded()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('Checkbox Test Page', () => {
|
||||
BootTestPage.clickAndGoToCheckboxPage();
|
||||
CheckboxTestPage.waitForPageDisplayed(PAGE_TIMEOUT);
|
||||
expect(CheckboxTestPage.isPageLoaded()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('Link Test Page', () => {
|
||||
BootTestPage.clickAndGoToLinkPage();
|
||||
LinkTestPage.waitForPageDisplayed(PAGE_TIMEOUT);
|
||||
expect(LinkTestPage.isPageLoaded()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('PersonaCoin Test Page', () => {
|
||||
BootTestPage.clickAndGoToPersonaCoinPage();
|
||||
PersonaCoinTestPage.waitForPageDisplayed(PAGE_TIMEOUT);
|
||||
expect(PersonaCoinTestPage.isPageLoaded()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('Pressable Test Page', () => {
|
||||
BootTestPage.clickAndGoToPressablePage();
|
||||
PressableTestPage.waitForPageDisplayed(PAGE_TIMEOUT);
|
||||
expect(PressableTestPage.isPageLoaded()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('RadioGroup Test Page', () => {
|
||||
BootTestPage.clickAndGoToRadioGroupPage();
|
||||
RadioGroupTestPage.waitForPageDisplayed(PAGE_TIMEOUT);
|
||||
expect(RadioGroupTestPage.isPageLoaded()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('Separator Test Page', () => {
|
||||
BootTestPage.clickAndGoToSeparatorPage();
|
||||
SeparatorTestPage.waitForPageDisplayed(PAGE_TIMEOUT);
|
||||
expect(SeparatorTestPage.isPageLoaded()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('Text Test Page', () => {
|
||||
BootTestPage.clickAndGoToTextPage();
|
||||
TextTestPage.waitForPageDisplayed(PAGE_TIMEOUT);
|
||||
expect(TextTestPage.isPageLoaded()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('Theme Test Page', () => {
|
||||
BootTestPage.clickAndGoToThemePage();
|
||||
ThemeTestPage.waitForPageDisplayed(PAGE_TIMEOUT);
|
||||
expect(ThemeTestPage.isPageLoaded()).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -5,6 +5,10 @@ class ButtonTestPage extends BasePage {
|
|||
get _testPage() {
|
||||
return By(BUTTON_TESTPAGE);
|
||||
}
|
||||
|
||||
get _pageName() {
|
||||
return BUTTON_TESTPAGE;
|
||||
}
|
||||
}
|
||||
|
||||
export default new ButtonTestPage();
|
||||
|
|
|
@ -5,6 +5,10 @@ class CalloutTestPage extends BasePage {
|
|||
get _testPage() {
|
||||
return By(CALLOUT_TESTPAGE);
|
||||
}
|
||||
|
||||
get _pageName() {
|
||||
return CALLOUT_TESTPAGE;
|
||||
}
|
||||
}
|
||||
|
||||
export default new CalloutTestPage();
|
||||
|
|
|
@ -5,6 +5,10 @@ class CheckboxTestPage extends BasePage {
|
|||
get _testPage() {
|
||||
return By(CHECKBOX_TESTPAGE);
|
||||
}
|
||||
|
||||
get _pageName() {
|
||||
return CHECKBOX_TESTPAGE;
|
||||
}
|
||||
}
|
||||
|
||||
export default new CheckboxTestPage();
|
||||
|
|
|
@ -5,6 +5,10 @@ class FocusTrapZonePage extends BasePage {
|
|||
get _testPage() {
|
||||
return By(FOCUSTRAPZONE_TESTPAGE);
|
||||
}
|
||||
|
||||
get _pageName() {
|
||||
return FOCUSTRAPZONE_TESTPAGE;
|
||||
}
|
||||
}
|
||||
|
||||
export default new FocusTrapZonePage();
|
||||
|
|
|
@ -5,6 +5,10 @@ class LinkTestPage extends BasePage {
|
|||
get _testPage() {
|
||||
return By(LINK_TESTPAGE);
|
||||
}
|
||||
|
||||
get _pageName() {
|
||||
return LINK_TESTPAGE;
|
||||
}
|
||||
}
|
||||
|
||||
export default new LinkTestPage();
|
||||
|
|
|
@ -5,6 +5,10 @@ class PersonaTestPage extends BasePage {
|
|||
get _testPage() {
|
||||
return By(PERSONA_TESTPAGE);
|
||||
}
|
||||
|
||||
get _pageName() {
|
||||
return PERSONA_TESTPAGE;
|
||||
}
|
||||
}
|
||||
|
||||
export default new PersonaTestPage();
|
||||
|
|
|
@ -5,6 +5,10 @@ class PersonaCoinTestPage extends BasePage {
|
|||
get _testPage() {
|
||||
return By(PERSONACOIN_TESTPAGE);
|
||||
}
|
||||
|
||||
get _pageName() {
|
||||
return PERSONACOIN_TESTPAGE;
|
||||
}
|
||||
}
|
||||
|
||||
export default new PersonaCoinTestPage();
|
||||
|
|
|
@ -5,6 +5,10 @@ class PressableTestPage extends BasePage {
|
|||
get _testPage() {
|
||||
return By(PRESSABLE_TESTPAGE);
|
||||
}
|
||||
|
||||
get _pageName() {
|
||||
return PRESSABLE_TESTPAGE;
|
||||
}
|
||||
}
|
||||
|
||||
export default new PressableTestPage();
|
||||
|
|
|
@ -7,6 +7,9 @@
|
|||
- **NOTE:** Please make sure you grab all of the items listed there and the appropriate versions.
|
||||
- [WinAppDriver](https://github.com/microsoft/WinAppDriver) - Version 1.1
|
||||
- Enable [_Developer Mode_](https://docs.microsoft.com/en-us/windows/uwp/get-started/enable-your-device-for-development) in Windows settings
|
||||
- [Java 1.8](https://www.oracle.com/java/technologies/javase/javase-jdk8-downloads.html) (Optional) - Used for generating in-depth after-action reports. More information in "Debugging E2E Failures" section below.
|
||||
- [Allure Command-Line](https://www.npmjs.com/package/allure-commandline) (Optional) - Used for creating in-depth reporting.
|
||||
- `npm install -g allure-commandline`
|
||||
|
||||
### UWP Additional Prerequisites
|
||||
|
||||
|
@ -92,3 +95,57 @@ describe('Click on each test page and check if it renders', function() {
|
|||
});
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
# Debugging E2E Failures (Locally)
|
||||
|
||||
If one tests fails, it will cause every subsequent test to fail as well. Due to this structure, if you get a failing E2E run, you should find the **first** failing test and focus on fixing that one.
|
||||
|
||||
Having a clear and concise report on testing failures is key in efficient debugging. We're utilizing two report generators:
|
||||
|
||||
1. **Spec Reporter** - Low overhead, easy to read, automatically runs with E2E testing. However, less information and less reliable.
|
||||
|
||||
2. **Allure Reporter** - Creates in-depth reports with key information about each test. Must have Java 1.8 downloaded to create reports and requires running additional script after E2E testing is complete.
|
||||
|
||||
## Using Spec Reporter
|
||||
|
||||
You can view the spec report right as E2E testing is finished. It shows the failing tests and a brief explanation of what went wrong. However, in some cases when a redbox error occurs (mostly in Win32), these messages and report will not exist. This is because the FluentTester app becomes a non-responsive window, and WebDriverIO cannot close the window, which leaves the spec reporter in a bad state. In this case, I would recommend using the Allure Reporter.
|
||||
|
||||
In the example below, the SVG test is the one failing the run, and at the bottom, you can see an error message.
|
||||
|
||||
![E2E Error Debugging](../../../../assets/E2E/E2E_spec_reporter.png)
|
||||
|
||||
When running E2E locally, after failing an E2E run, you will get a screenshot of the error in /errorShots/ of the platform you tested.
|
||||
|
||||
## Using Allure Reporter
|
||||
|
||||
Allure Framework is a flexible, lightweight multi-language test report tool that not only shows a very concise representation of what have been tested in a neat web report form, but allows everyone participating in the development process to extract maximum of useful information from everyday execution of tests.
|
||||
|
||||
After E2E testing runs, allure creates a folder of XML files with all relevant information from the tests. In order to generate the report, you need to run the following command:
|
||||
|
||||
- `yarn generate-report`
|
||||
|
||||
This will bundle all the generated information and create a report for you to read.
|
||||
|
||||
# Debugging E2E Failures (CI Pipeline)
|
||||
|
||||
When an E2E test run fails within our CI, crucial information is output to Azure-Pipelines to help you debug the failure. Follow these steps:
|
||||
|
||||
1. On the PR page, navigate to the "Checks" tab, ensure you've selected the "PR" tab on the left, and press "View more details on Azure Pipelines" at the bottom. ![E2E_Debugging_Step_1](../../../../assets/E2E/E2E_Debugging_Step_1.png)
|
||||
|
||||
2. Click on the "# published" section. ![E2E_Debugging_Step_2](../../../../assets/E2E/E2E_Debugging_Step_2.png)
|
||||
|
||||
3. Here, you have crucial information to help you debug the problem.
|
||||
|
||||
- The bottom files (green) are screenshots of the failing tests. The first one from the top (in this case, Svg-Test-Page), is the test failing the whole run. This is the one you're focus should be on.
|
||||
|
||||
- The middle file (pink) is the Appium output file. This contains more in-depth information on each test, including possible failures with WebDriverIO or the driver being used.
|
||||
|
||||
- The top folder (blue) is the Allure reporter output. In order to generate the report, you must:
|
||||
|
||||
1. Download the folder and unzip it
|
||||
|
||||
2. Navigate to it's location within your cmd, and type:
|
||||
- C:\pathToFolder\allure-report\E2E_win32_Dump> `allure open`
|
||||
|
||||
![E2E_Debugging_Step_3](../../../../assets/E2E/E2E_Debugging_Step_3.png)
|
||||
|
|
|
@ -5,6 +5,10 @@ class RadioGroupPage extends BasePage {
|
|||
get _testPage() {
|
||||
return By(RADIOGROUP_TESTPAGE);
|
||||
}
|
||||
|
||||
get _pageName() {
|
||||
return RADIOGROUP_TESTPAGE;
|
||||
}
|
||||
}
|
||||
|
||||
export default new RadioGroupPage();
|
||||
|
|
|
@ -5,6 +5,10 @@ class SeparatorTestPage extends BasePage {
|
|||
get _testPage() {
|
||||
return By(SEPARATOR_TESTPAGE);
|
||||
}
|
||||
|
||||
get _pageName() {
|
||||
return SEPARATOR_TESTPAGE;
|
||||
}
|
||||
}
|
||||
|
||||
export default new SeparatorTestPage();
|
||||
|
|
|
@ -5,6 +5,10 @@ class SvgTestPage extends BasePage {
|
|||
get _testPage() {
|
||||
return By(SVG_TESTPAGE);
|
||||
}
|
||||
|
||||
get _pageName() {
|
||||
return SVG_TESTPAGE;
|
||||
}
|
||||
}
|
||||
|
||||
export default new SvgTestPage();
|
||||
|
|
|
@ -5,6 +5,10 @@ class TextTestPage extends BasePage {
|
|||
get _testPage() {
|
||||
return By(TEXT_TESTPAGE);
|
||||
}
|
||||
|
||||
get _pageName() {
|
||||
return TEXT_TESTPAGE;
|
||||
}
|
||||
}
|
||||
|
||||
export default new TextTestPage();
|
||||
|
|
|
@ -5,6 +5,10 @@ class ThemeTestPage extends BasePage {
|
|||
get _testPage() {
|
||||
return By(THEME_TESTPAGE);
|
||||
}
|
||||
|
||||
get _pageName() {
|
||||
return THEME_TESTPAGE;
|
||||
}
|
||||
}
|
||||
|
||||
export default new ThemeTestPage();
|
||||
|
|
|
@ -10,25 +10,27 @@ export class BasePage {
|
|||
}
|
||||
|
||||
// Waits for page to be loaded. Timeout could differ depending on usage.
|
||||
waitForPageLoaded(timeout?: number) {
|
||||
waitForPageDisplayed(timeout?: number) {
|
||||
browser.waitUntil(
|
||||
() => {
|
||||
return this.isPageLoaded();
|
||||
},
|
||||
this.timeoutForPageLoaded(timeout),
|
||||
'Timeout Error - The page was not loaded in time.'
|
||||
timeout ?? this.waitForPageTimeout,
|
||||
this._pageName + ' did not render correctly. Please see /errorShots of the first failed test for more information.',
|
||||
1000
|
||||
);
|
||||
}
|
||||
|
||||
// Actual element on page
|
||||
get _testPage() {
|
||||
return By(DUMMY_CHAR);
|
||||
}
|
||||
|
||||
protected timeoutForPageLoaded(currentTimeout?: number) {
|
||||
if (currentTimeout) return currentTimeout;
|
||||
return this.waitforPageTimeout;
|
||||
// Title of page
|
||||
get _pageName() {
|
||||
return DUMMY_CHAR;
|
||||
}
|
||||
|
||||
// Default timeout for waitForPageLoaded command in PageObject
|
||||
private waitforPageTimeout: number = 45000;
|
||||
}
|
||||
// Default timeout to wait until page is displayed (10s)
|
||||
private waitForPageTimeout: number = 10000;
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
import * as React from 'react';
|
||||
import { findNodeHandle } from 'react-native';
|
||||
import { Button, IFocusable } from '@fluentui/react-native';
|
||||
import { View, findNodeHandle, Text } from 'react-native';
|
||||
import { Button, IFocusable, Separator } from '@fluentui/react-native';
|
||||
import { Stack } from '@fluentui-react-native/stack';
|
||||
import { stackStyle } from '../Common/styles';
|
||||
import { stackStyle, commonTestStyles as commonStyles } from '../Common/styles';
|
||||
import { BUTTON_TESTPAGE } from './consts';
|
||||
|
||||
export const ButtonFocusTest: React.FunctionComponent<{}> = () => {
|
||||
|
@ -22,14 +22,15 @@ export const ButtonFocusTest: React.FunctionComponent<{}> = () => {
|
|||
}, [state, setState]);
|
||||
|
||||
return (
|
||||
<Stack style={stackStyle}>
|
||||
<Button
|
||||
testID={BUTTON_TESTPAGE}
|
||||
content={state.focused ? 'Focused' : 'Not Focused'}
|
||||
componentRef={buttonRef}
|
||||
accessibilityLabel="overridden button name"
|
||||
/>
|
||||
<Button content="Click to focus" onClick={onFocus} tooltip="button tooltip" />
|
||||
</Stack>
|
||||
<View>
|
||||
<Text style={commonStyles.section} testID={BUTTON_TESTPAGE}>
|
||||
Basic Buttons
|
||||
</Text>
|
||||
<Separator />
|
||||
<Stack style={stackStyle}>
|
||||
<Button content={state.focused ? 'Focused' : 'Not Focused'} componentRef={buttonRef} accessibilityLabel="overridden button name" />
|
||||
<Button content="Click to focus" onClick={onFocus} tooltip="button tooltip" />
|
||||
</Stack>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
};
|
|
@ -1,7 +1,6 @@
|
|||
import * as React from 'react';
|
||||
import { View, TextInput, Text } from 'react-native';
|
||||
import { Checkbox } from '@fluentui/react-native';
|
||||
import { Separator } from '@fluentui/react-native';
|
||||
import { Checkbox, Separator } from '@fluentui/react-native';
|
||||
import { commonTestStyles as commonStyles } from '../Common/styles';
|
||||
import { useTheme } from '@uifabricshared/theming-react-native';
|
||||
import { CHECKBOX_TESTPAGE } from './consts';
|
||||
|
@ -65,12 +64,12 @@ export const CheckboxTest: React.FunctionComponent<{}> = () => {
|
|||
});
|
||||
|
||||
const [isCheckedControlled1, setCheckedControlled1] = React.useState(false);
|
||||
const onChangeControlled1 = React.useCallback(checked => {
|
||||
const onChangeControlled1 = React.useCallback((checked) => {
|
||||
setCheckedControlled1(checked);
|
||||
}, []);
|
||||
|
||||
const [isCheckedControlled2, setCheckedControlled2] = React.useState(true);
|
||||
const onChangeControlled2 = React.useCallback(checked => {
|
||||
const onChangeControlled2 = React.useCallback((checked) => {
|
||||
setCheckedControlled2(checked);
|
||||
}, []);
|
||||
|
||||
|
@ -131,4 +130,4 @@ export const CheckboxTest: React.FunctionComponent<{}> = () => {
|
|||
/>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
};
|
|
@ -1,9 +1,9 @@
|
|||
import { IFocusTrapZoneProps, Text, FocusTrapZone, KeyPressEvent } from '@fluentui/react-native';
|
||||
import { IFocusTrapZoneProps, Text, FocusTrapZone, KeyPressEvent, Separator } from '@fluentui/react-native';
|
||||
import { Stack } from '@fluentui-react-native/stack';
|
||||
import { TouchableHighlight, TouchableHighlightProps, View, ViewProps } from 'react-native';
|
||||
import { useFocusState } from '@fluentui/react-native';
|
||||
import * as React from 'react';
|
||||
import { stackStyle } from '../Common/styles';
|
||||
import { stackStyle, commonTestStyles as commonStyles } from '../Common/styles';
|
||||
import { FOCUSTRAPZONE_TESTPAGE } from './consts';
|
||||
|
||||
const trapZoneStyle: IFocusTrapZoneProps['style'] = {
|
||||
|
@ -91,42 +91,48 @@ export const FocusTrapTest: React.FunctionComponent<{}> = () => {
|
|||
}, [state, setState]);
|
||||
|
||||
return (
|
||||
<View {...{ onKeyDown: onKeyDown }}>
|
||||
<Stack style={stackStyle} gap={5}>
|
||||
<ComponentTwiddler label="Press space to render or enter to trap, f to focus" />
|
||||
<ComponentTwiddler
|
||||
label={state.ignoreExternalFocusing ? 'ignoreExternalFocusing: true' : 'ignoreExternalFocusing: false'}
|
||||
onPress={onTwiddleExternalFocusing}
|
||||
/>
|
||||
<ComponentTwiddler
|
||||
label={
|
||||
state.focusPreviouslyFocusedInnerElement
|
||||
? 'focusPreviouslyFocusedInnerElement: true'
|
||||
: 'focusPreviouslyFocusedInnerElement: false'
|
||||
}
|
||||
onPress={onTwiddleFocusPrevious}
|
||||
/>
|
||||
<ComponentTwiddler
|
||||
label={state.disableFirstFocus ? 'disableFirstFocus: true' : 'disableFirstFocus: false'}
|
||||
onPress={onTwiddleFirstFocus}
|
||||
/>
|
||||
{state.renderTrapZone && (
|
||||
<FocusTrapZone
|
||||
componentRef={ftzRef}
|
||||
disableFirstFocus={state.disableFirstFocus}
|
||||
ignoreExternalFocusing={state.ignoreExternalFocusing}
|
||||
focusPreviouslyFocusedInnerElement={state.focusPreviouslyFocusedInnerElement}
|
||||
disabled={!state.useTrapZone}
|
||||
style={state.useTrapZone ? activeTrapZoneStyle : trapZoneStyle}
|
||||
>
|
||||
<Text testID={FOCUSTRAPZONE_TESTPAGE}>{state.useTrapZone ? 'Trap Active' : 'Trap Active'}</Text>
|
||||
<ComponentTwiddler label="trapped" />
|
||||
<ComponentTwiddler label="trapped" />
|
||||
<ComponentTwiddler label="trapped" />
|
||||
<ComponentTwiddler label="trapped" />
|
||||
</FocusTrapZone>
|
||||
)}
|
||||
</Stack>
|
||||
<View>
|
||||
<Text style={commonStyles.section} testID={FOCUSTRAPZONE_TESTPAGE}>
|
||||
Basic FocusTrapZone Usage
|
||||
</Text>
|
||||
<Separator />
|
||||
<View {...{ onKeyDown: onKeyDown }}>
|
||||
<Stack style={stackStyle} gap={5}>
|
||||
<ComponentTwiddler label="Press space to render or enter to trap, f to focus" />
|
||||
<ComponentTwiddler
|
||||
label={state.ignoreExternalFocusing ? 'ignoreExternalFocusing: true' : 'ignoreExternalFocusing: false'}
|
||||
onPress={onTwiddleExternalFocusing}
|
||||
/>
|
||||
<ComponentTwiddler
|
||||
label={
|
||||
state.focusPreviouslyFocusedInnerElement
|
||||
? 'focusPreviouslyFocusedInnerElement: true'
|
||||
: 'focusPreviouslyFocusedInnerElement: false'
|
||||
}
|
||||
onPress={onTwiddleFocusPrevious}
|
||||
/>
|
||||
<ComponentTwiddler
|
||||
label={state.disableFirstFocus ? 'disableFirstFocus: true' : 'disableFirstFocus: false'}
|
||||
onPress={onTwiddleFirstFocus}
|
||||
/>
|
||||
{state.renderTrapZone && (
|
||||
<FocusTrapZone
|
||||
componentRef={ftzRef}
|
||||
disableFirstFocus={state.disableFirstFocus}
|
||||
ignoreExternalFocusing={state.ignoreExternalFocusing}
|
||||
focusPreviouslyFocusedInnerElement={state.focusPreviouslyFocusedInnerElement}
|
||||
disabled={!state.useTrapZone}
|
||||
style={state.useTrapZone ? activeTrapZoneStyle : trapZoneStyle}
|
||||
>
|
||||
<Text>{state.useTrapZone ? 'Trap Active' : 'Trap Active'}</Text>
|
||||
<ComponentTwiddler label="trapped" />
|
||||
<ComponentTwiddler label="trapped" />
|
||||
<ComponentTwiddler label="trapped" />
|
||||
<ComponentTwiddler label="trapped" />
|
||||
</FocusTrapZone>
|
||||
)}
|
||||
</Stack>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
};
|
|
@ -1,6 +1,6 @@
|
|||
import * as React from 'react';
|
||||
import { Alert } from 'react-native';
|
||||
import { Link } from '@fluentui/react-native';
|
||||
import { Link, Separator } from '@fluentui/react-native';
|
||||
import { Stack } from '@fluentui-react-native/stack';
|
||||
import { stackStyle } from '../Common/styles';
|
||||
import { LINK_TESTPAGE } from './consts';
|
||||
|
@ -16,10 +16,11 @@ export const LinkTest: React.FunctionComponent<{}> = () => {
|
|||
<Text style={commonStyles.section} testID={LINK_TESTPAGE}>
|
||||
Link Test Page
|
||||
</Text>
|
||||
<Separator />
|
||||
<Stack style={stackStyle}>
|
||||
<Link url="https://www.bing.com/" content="Click to navigate." />
|
||||
<Link onPress={doPress} content="Click to alert." />
|
||||
</Stack>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
};
|
|
@ -1,9 +1,10 @@
|
|||
import * as React from 'react';
|
||||
import { Text, Pressable, IPressableState } from '@fluentui/react-native';
|
||||
import { Stack } from '@fluentui-react-native/stack';
|
||||
import { useHoverState, useFocusState, usePressState } from '@fluentui/react-native';
|
||||
import { useHoverState, useFocusState, usePressState, Separator } from '@fluentui/react-native';
|
||||
import { Square } from '../Common/Square';
|
||||
import { Alert, GestureResponderEvent, StyleSheet, View, ViewProps, ViewStyle } from 'react-native';
|
||||
import { commonTestStyles as commonStyles } from '../Common/styles';
|
||||
import { PRESSABLE_TESTPAGE } from './consts';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
|
@ -54,7 +55,7 @@ const styles = StyleSheet.create({
|
|||
borderStyle: 'dashed',
|
||||
borderColor: 'black',
|
||||
backgroundColor: 'lightgreen'
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
function renderStyle(state: IPressableState): ViewStyle {
|
||||
|
@ -65,29 +66,35 @@ export const PressableTest: React.FunctionComponent<{}> = () => {
|
|||
const [hoverProps, hoverState] = useHoverState({});
|
||||
|
||||
return (
|
||||
<Stack horizontal gap={5}>
|
||||
<Square color="blue" />
|
||||
<Pressable renderStyle={renderStyle}>
|
||||
<Square />
|
||||
</Pressable>
|
||||
<Square color="green" />
|
||||
<Stack>
|
||||
<View {...hoverProps as any} style={hoverState.hovered ? styles.dottedBorder : styles.solidBorder}>
|
||||
<Text>{hoverState.hovered ? 'hovered' : 'not hovered'}</Text>
|
||||
</View>
|
||||
<View>
|
||||
<Text style={commonStyles.section} testID={PRESSABLE_TESTPAGE}>
|
||||
Pressable Test Page
|
||||
</Text>
|
||||
<Separator />
|
||||
<Stack horizontal gap={5}>
|
||||
<Square color="blue" />
|
||||
<Pressable renderStyle={renderStyle}>
|
||||
<Square />
|
||||
</Pressable>
|
||||
<Square color="green" />
|
||||
<Stack>
|
||||
<View {...(hoverProps as any)} style={hoverState.hovered ? styles.dottedBorder : styles.solidBorder}>
|
||||
<Text>{hoverState.hovered ? 'hovered' : 'not hovered'}</Text>
|
||||
</View>
|
||||
</Stack>
|
||||
<Stack>
|
||||
<Text>Click a component to initially focus and tab to keyboard focus to next component: </Text>
|
||||
<FocusComponent />
|
||||
<FocusComponent />
|
||||
<FocusComponent />
|
||||
<FocusComponent />
|
||||
</Stack>
|
||||
<Stack>
|
||||
<Text>Press to alert: </Text>
|
||||
<PressComponent />
|
||||
</Stack>
|
||||
</Stack>
|
||||
<Stack>
|
||||
<Text testID={PRESSABLE_TESTPAGE}>Click a component to initially focus and tab to keyboard focus to next component: </Text>
|
||||
<FocusComponent />
|
||||
<FocusComponent />
|
||||
<FocusComponent />
|
||||
<FocusComponent />
|
||||
</Stack>
|
||||
<Stack>
|
||||
<Text>Press to alert: </Text>
|
||||
<PressComponent />
|
||||
</Stack>
|
||||
</Stack>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -122,4 +129,4 @@ const PressComponent: React.FunctionComponent<ViewProps> = (props: ViewProps) =>
|
|||
/>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
};
|
|
@ -1,6 +1,7 @@
|
|||
import * as React from 'react';
|
||||
import { RadioButton, RadioGroup } from '@fluentui/react-native';
|
||||
import { View } from 'react-native';
|
||||
import { RadioButton, RadioGroup, Separator } from '@fluentui/react-native';
|
||||
import { View, Text } from 'react-native';
|
||||
import { commonTestStyles as commonStyles } from '../Common/styles';
|
||||
import { RADIOGROUP_TESTPAGE } from './consts';
|
||||
|
||||
export const RadioGroupTest: React.FunctionComponent<{}> = () => {
|
||||
|
@ -11,12 +12,16 @@ export const RadioGroupTest: React.FunctionComponent<{}> = () => {
|
|||
|
||||
return (
|
||||
<View>
|
||||
<Text style={commonStyles.section} testID={RADIOGROUP_TESTPAGE}>
|
||||
Basic RadioGroup Usage
|
||||
</Text>
|
||||
<Separator />
|
||||
<RadioGroup label="This is a test RadioGroup" defaultSelectedKey="A" onChange={onChange}>
|
||||
<RadioButton content="Option A" buttonKey="A" ariaLabel="Cool" testID={RADIOGROUP_TESTPAGE} />
|
||||
<RadioButton content="Option A" buttonKey="A" ariaLabel="Cool" />
|
||||
<RadioButton content="Option B" buttonKey="B" />
|
||||
<RadioButton content="Option C" buttonKey="C" disabled={true} />
|
||||
<RadioButton content="Option D" buttonKey="D" />
|
||||
</RadioGroup>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
};
|
|
@ -1,6 +1,7 @@
|
|||
import * as React from 'react';
|
||||
import { View } from 'react-native';
|
||||
import { Button, Separator, Text } from '@fluentui/react-native';
|
||||
import { stackStyle, separatorStackStyle } from '../Common/styles';
|
||||
import { stackStyle, separatorStackStyle, commonTestStyles as commonStyles } from '../Common/styles';
|
||||
import { Stack } from '@fluentui-react-native/stack';
|
||||
import { SEPARATOR_TESTPAGE } from './consts';
|
||||
|
||||
|
@ -9,18 +10,24 @@ const RedSeparator = Separator.customize({ tokens: { color: 'red' } });
|
|||
|
||||
export const SeparatorTest: React.FunctionComponent<{}> = () => {
|
||||
return (
|
||||
<Stack style={stackStyle} gap={5}>
|
||||
<Stack gap={4} style={separatorStackStyle}>
|
||||
<Button content="Button4" />
|
||||
<BlueSeparator vertical />
|
||||
<Button content="Button5" />
|
||||
<RedSeparator vertical />
|
||||
<Button content="Button6" />
|
||||
<Separator />
|
||||
</Stack>
|
||||
<Text testID={SEPARATOR_TESTPAGE}>This is a text element</Text>
|
||||
<View>
|
||||
<Text style={commonStyles.section} testID={SEPARATOR_TESTPAGE}>
|
||||
Separator Test Page
|
||||
</Text>
|
||||
<Separator />
|
||||
<Button content="This button has longer text" />
|
||||
</Stack>
|
||||
<Stack style={stackStyle} gap={5}>
|
||||
<Stack gap={4} style={separatorStackStyle}>
|
||||
<Button content="Button4" />
|
||||
<BlueSeparator vertical />
|
||||
<Button content="Button5" />
|
||||
<RedSeparator vertical />
|
||||
<Button content="Button6" />
|
||||
<Separator />
|
||||
</Stack>
|
||||
<Text>This is a text element</Text>
|
||||
<Separator />
|
||||
<Button content="This button has longer text" />
|
||||
</Stack>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
};
|
|
@ -21,7 +21,9 @@
|
|||
"run-win32": "rex-win32 --bundle index --component FluentTester --windowTitle \"FluentUI Tester\" --basePath ./dist --pluginProps",
|
||||
"run-win32-web": "rex-win32 --bundle index --component FluentTester --basePath ./dist --useWebDebugger --windowTitle \"FluentUI Tester\" --useLiveReload --pluginProps",
|
||||
"run-win32-devmain": "rex-win32 --bundle index --component FluentTester --basePath ./dist --useDevMain --windowTitle \"FluentUI Tester\" --pluginProps",
|
||||
"e2etest": "rimraf reports/* && wdio"
|
||||
"e2etest": "rimraf reports/* && wdio",
|
||||
"report": "allure generate allure-results --clean",
|
||||
"generate-report": "allure generate allure-results --clean && allure open"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fluentui-react-native/tester": "^0.0.1",
|
||||
|
@ -41,20 +43,22 @@
|
|||
"@types/react-native": "^0.62.0",
|
||||
"@uifabricshared/build-native": "^0.1.1",
|
||||
"@uifabricshared/eslint-config-rules": "^0.1.1",
|
||||
"@wdio/appium-service": "^5.0.0",
|
||||
"@wdio/cli": "^5.22.4",
|
||||
"@wdio/jasmine-framework": "^5.18.6",
|
||||
"@wdio/local-runner": "^5.22.4",
|
||||
"@wdio/spec-reporter": "^5.22.4",
|
||||
"@wdio/sync": "^5.20.1",
|
||||
"appium": "1.17.1",
|
||||
"metro-react-native-babel-preset": "^0.58.0",
|
||||
"react-native-svg-transformer": "^0.14.3",
|
||||
"react-test-renderer": "~16.11.0",
|
||||
"ts-node": "^8.10.1",
|
||||
"tsconfig-paths": "^3.9.0",
|
||||
"typescript": "3.8.3",
|
||||
"webdriverio": "5.22.4"
|
||||
"allure-commandline": "2.13.0",
|
||||
"appium": "1.17.1",
|
||||
"webdriverio": "5.22.4",
|
||||
"@wdio/appium-service": "^5.0.0",
|
||||
"@wdio/cli": "^5.22.4",
|
||||
"@wdio/jasmine-framework": "^5.18.6",
|
||||
"@wdio/local-runner": "^5.22.4",
|
||||
"@wdio/spec-reporter": "^5.22.4",
|
||||
"@wdio/sync": "^5.20.1",
|
||||
"@wdio/allure-reporter": "^5.22.4"
|
||||
},
|
||||
"workspaces": {
|
||||
"nohoist": [
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const rimraf = require('rimraf');
|
||||
|
||||
const appPath = path.resolve(path.dirname(require.resolve('@office-iss/rex-win32/rex-win32.js')), 'ReactTest.exe');
|
||||
const appArgs = 'basePath ' + path.resolve('dist') + ' plugin defaultplugin bundle index component FluentTester';
|
||||
|
@ -23,8 +25,8 @@ exports.config = {
|
|||
deviceName: 'WindowsPC',
|
||||
app: appPath,
|
||||
appArguments: appArgs,
|
||||
appWorkingDir: appDir
|
||||
}
|
||||
appWorkingDir: appDir,
|
||||
},
|
||||
],
|
||||
|
||||
/*
|
||||
|
@ -37,26 +39,34 @@ exports.config = {
|
|||
logLevel: 'info', // Level of logging verbosity: trace | debug | info | warn | error | silent
|
||||
|
||||
// If you only want to run your tests until a specific amount of tests have failed use bail (default is 0 - don't bail, run all tests).
|
||||
bail: 0,
|
||||
bail: 1,
|
||||
waitforTimeout: defaultWaitForTimeout, // Default timeout for all waitForXXX commands.
|
||||
connectionRetryTimeout: defaultConnectionRetryTimeout, // Timeout for any WebDriver request to a driver or grid.
|
||||
connectionRetryCount: 2, // Maximum count of request retries to the Selenium server.
|
||||
connectionRetryCount: 1, // Maximum count of request retries to the Selenium server.
|
||||
|
||||
port: 4723, // default appium port
|
||||
services: ['appium'],
|
||||
appium: {
|
||||
logPath: './reports/',
|
||||
args: {
|
||||
port: '4723'
|
||||
}
|
||||
port: '4723',
|
||||
},
|
||||
},
|
||||
|
||||
framework: 'jasmine',
|
||||
jasmineNodeOpts: {
|
||||
defaultTimeoutInterval: jasmineDefaultTimeout
|
||||
defaultTimeoutInterval: jasmineDefaultTimeout,
|
||||
},
|
||||
|
||||
reporters: ['spec'],
|
||||
reporters: [
|
||||
'spec',
|
||||
[
|
||||
'allure',
|
||||
{
|
||||
outputDir: 'allure-results',
|
||||
},
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
** ===================
|
||||
|
@ -95,8 +105,18 @@ exports.config = {
|
|||
* @param {Array.<Object>} capabilities list of capabilities details
|
||||
* @param {Array.<String>} specs List of spec file paths that are to be run
|
||||
*/
|
||||
// beforeSession: function (config, capabilities, specs) {
|
||||
// },
|
||||
beforeSession: function (config, capabilities, specs) {
|
||||
// Delete old screenshots and create empty directory
|
||||
if (fs.existsSync('./errorShots')) {
|
||||
rimraf.sync('./errorShots');
|
||||
}
|
||||
fs.mkdirSync('./errorShots');
|
||||
|
||||
if (fs.existsSync('./allure-results')) {
|
||||
rimraf.sync('./allure-results');
|
||||
}
|
||||
fs.mkdirSync('./allure-results');
|
||||
},
|
||||
/**
|
||||
* Gets executed before test execution begins. At this point you can access to all global
|
||||
* variables like `browser`. It is the perfect place to define custom commands.
|
||||
|
@ -141,11 +161,20 @@ exports.config = {
|
|||
/**
|
||||
* Function to be executed after a test (in Mocha/Jasmine).
|
||||
*/
|
||||
afterTest: function(test) {
|
||||
if (test.error !== undefined) {
|
||||
const name = 'ERROR-' + Date.now();
|
||||
browser.saveScreenshot('./reports/errorShots/' + name + '.png');
|
||||
afterTest: function (test) {
|
||||
// if test passed, ignore, else take and save screenshot.
|
||||
if (test.passed) {
|
||||
return;
|
||||
}
|
||||
|
||||
// get current test title and clean it, to use it as file name
|
||||
const fileName = encodeURIComponent(test.title.replace(/\s+/g, '-'));
|
||||
|
||||
// build file path
|
||||
const filePath = './errorShots/' + fileName + '.png';
|
||||
|
||||
// save screenshot
|
||||
browser.saveScreenshot(filePath);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -190,7 +219,7 @@ exports.config = {
|
|||
*/
|
||||
onComplete: function(exitCode, config, capabilities, results) {
|
||||
console.log('<<< TESTING FINISHED >>>');
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Gets executed when a refresh happens.
|
||||
* @param {String} oldSessionId session ID of the old session
|
||||
|
@ -198,4 +227,4 @@ exports.config = {
|
|||
*/
|
||||
//onReload: function(oldSessionId, newSessionId) {
|
||||
//}
|
||||
};
|
||||
};
|
|
@ -8,7 +8,9 @@
|
|||
"lint": "eslint .",
|
||||
"windows": "react-native run-windows",
|
||||
"bundle": "fluentui-scripts metro --cli",
|
||||
"e2etest": "rimraf reports/* && wdio"
|
||||
"e2etest": "rimraf reports/* && wdio",
|
||||
"report": "allure generate allure-results --clean",
|
||||
"generate-report": "allure generate allure-results --clean && allure open"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fluentui-react-native/tester": "^0.0.1",
|
||||
|
@ -28,18 +30,20 @@
|
|||
"metro-config": "^0.58.0",
|
||||
"metro-react-native-babel-preset": "^0.58.0",
|
||||
"react-test-renderer": "~16.11.0",
|
||||
"ts-node": "^8.10.1",
|
||||
"tsconfig-paths": "^3.9.0",
|
||||
"typescript": "3.8.3",
|
||||
"@types/jasmine": "3.5.10",
|
||||
"allure-commandline": "2.13.0",
|
||||
"appium": "1.17.1",
|
||||
"webdriverio": "5.22.4",
|
||||
"@wdio/cli": "^5.22.4",
|
||||
"@wdio/jasmine-framework": "^5.18.6",
|
||||
"@wdio/local-runner": "^5.22.4",
|
||||
"@wdio/sync": "^5.20.1",
|
||||
"@wdio/spec-reporter": "^5.22.4",
|
||||
"@wdio/appium-service": "^5.0.0",
|
||||
"ts-node": "^8.10.1",
|
||||
"tsconfig-paths": "^3.9.0",
|
||||
"typescript": "3.8.3",
|
||||
"@types/jasmine": "3.5.10",
|
||||
"webdriverio": "5.22.4"
|
||||
"@wdio/allure-reporter": "^5.22.4"
|
||||
},
|
||||
"workspaces": {
|
||||
"nohoist": [
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
const fs = require('fs');
|
||||
const rimraf = require('rimraf');
|
||||
|
||||
const defaultWaitForTimeout = 10000;
|
||||
const defaultConnectionRetryTimeout = 15000;
|
||||
const jasmineDefaultTimeout = 45000; // 45 seconds for Jasmine test timeout
|
||||
|
@ -29,10 +32,10 @@ exports.config = {
|
|||
logLevel: 'info', // Level of logging verbosity: trace | debug | info | warn | error | silent
|
||||
|
||||
// If you only want to run your tests until a specific amount of tests have failed use bail (default is 0 - don't bail, run all tests).
|
||||
bail: 0,
|
||||
bail: 1,
|
||||
waitforTimeout: defaultWaitForTimeout, // Default timeout for all waitForXXX commands.
|
||||
connectionRetryTimeout: defaultConnectionRetryTimeout, // Timeout for any WebDriver request to a driver or grid.
|
||||
connectionRetryCount: 2, // Maximum count of request retries to the Selenium server.
|
||||
connectionRetryCount: 1, // Maximum count of request retries to the Selenium server.
|
||||
|
||||
port: 4723, // default appium port
|
||||
services: ['appium'],
|
||||
|
@ -48,7 +51,15 @@ exports.config = {
|
|||
defaultTimeoutInterval: jasmineDefaultTimeout,
|
||||
},
|
||||
|
||||
reporters: ['spec'],
|
||||
reporters: [
|
||||
'spec',
|
||||
[
|
||||
'allure',
|
||||
{
|
||||
outputDir: 'allure-results',
|
||||
},
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
** ===================
|
||||
|
@ -87,8 +98,18 @@ exports.config = {
|
|||
* @param {Array.<Object>} capabilities list of capabilities details
|
||||
* @param {Array.<String>} specs List of spec file paths that are to be run
|
||||
*/
|
||||
// beforeSession: function (config, capabilities, specs) {
|
||||
// },
|
||||
beforeSession: function (config, capabilities, specs) {
|
||||
// Delete old screenshots and create empty directory
|
||||
if (fs.existsSync('./errorShots')) {
|
||||
rimraf.sync('./errorShots');
|
||||
}
|
||||
fs.mkdirSync('./errorShots');
|
||||
|
||||
if (fs.existsSync('./allure-results')) {
|
||||
rimraf.sync('./allure-results');
|
||||
}
|
||||
fs.mkdirSync('./allure-results');
|
||||
},
|
||||
/**
|
||||
* Gets executed before test execution begins. At this point you can access to all global
|
||||
* variables like `browser`. It is the perfect place to define custom commands.
|
||||
|
@ -133,11 +154,20 @@ exports.config = {
|
|||
/**
|
||||
* Function to be executed after a test (in Mocha/Jasmine).
|
||||
*/
|
||||
afterTest: function(test, context) {
|
||||
if (test.error !== undefined) {
|
||||
const name = 'ERROR-' + Date.now();
|
||||
browser.saveScreenshot('./errorShots/' + name + '.png');
|
||||
afterTest: function (test, context) {
|
||||
// if test passed, ignore, else take and save screenshot.
|
||||
if (test.passed) {
|
||||
return;
|
||||
}
|
||||
|
||||
// get current test title and clean it, to use it as file name
|
||||
const fileName = encodeURIComponent(test.title.replace(/\s+/g, '-'));
|
||||
|
||||
// build file path
|
||||
const filePath = './errorShots/' + fileName + '.png';
|
||||
|
||||
// save screenshot
|
||||
browser.saveScreenshot(filePath);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -190,4 +220,4 @@ exports.config = {
|
|||
*/
|
||||
//onReload: function(oldSessionId, newSessionId) {
|
||||
//}
|
||||
};
|
||||
};
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 203 KiB |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 119 KiB |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 57 KiB |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 91 KiB |
53
yarn.lock
53
yarn.lock
|
@ -3613,6 +3613,14 @@
|
|||
resolved "https://registry.yarnpkg.com/@uifabric/prettier-rules/-/prettier-rules-7.0.4.tgz#03082a6cc4b586104738a24892c56e0f1d3df1b6"
|
||||
integrity sha512-PK60wLAjPrG2l93r1l/VY2BmnC1la0oxIXYBuln5DOAFJcSXMea9RxoNjuqhhqZGZo6aLjO7lW070MeVOvkABQ==
|
||||
|
||||
"@wdio/allure-reporter@^5.22.4":
|
||||
version "5.22.4"
|
||||
resolved "https://registry.yarnpkg.com/@wdio/allure-reporter/-/allure-reporter-5.22.4.tgz#3d0e3a5d28db5ea37fb1f1c356ed76b130d9aac3"
|
||||
integrity sha512-iE6LOlrkBSt6JsKxsI2tzaUjUJx+dz9yHVl0yoBW/NYBzxE0sn/0jr8OR+lUV35GdIi90D59MMIrZ/BYS1occA==
|
||||
dependencies:
|
||||
"@wdio/reporter" "5.22.4"
|
||||
allure-js-commons "^1.3.2"
|
||||
|
||||
"@wdio/appium-service@^5.0.0":
|
||||
version "5.18.2"
|
||||
resolved "https://registry.yarnpkg.com/@wdio/appium-service/-/appium-service-5.18.2.tgz#e6461c939393c3edbb027e69780f0fd2dadff6a4"
|
||||
|
@ -4261,6 +4269,23 @@ ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.5.5:
|
|||
json-schema-traverse "^0.4.1"
|
||||
uri-js "^4.2.2"
|
||||
|
||||
allure-commandline@2.13.0:
|
||||
version "2.13.0"
|
||||
resolved "https://registry.yarnpkg.com/allure-commandline/-/allure-commandline-2.13.0.tgz#54d77ab8733ded6a8fe2379b60213cd00f5c6425"
|
||||
integrity sha512-KUByc0nT+vVVsnW/p36R92+FS2o3xVX27p+AW/8T4dlXVh7jx6V0v6+rRBCpK7AhBKD9/T3jsSq14hS0sxp64Q==
|
||||
|
||||
allure-js-commons@^1.3.2:
|
||||
version "1.3.2"
|
||||
resolved "https://registry.yarnpkg.com/allure-js-commons/-/allure-js-commons-1.3.2.tgz#e1cf0466e36695bb3ced1228f6570eac6c2e9eda"
|
||||
integrity sha512-FTmoqP36ZjHFT4iLdYamyCFhyj1jqD6BIdiZ5pBlyafDJrFRV76XIXNxwRqbHpSw40o1vHzYi4vGpmREnhnHVw==
|
||||
dependencies:
|
||||
file-type "^7.7.1"
|
||||
fs-extra "^6.0.1"
|
||||
js2xmlparser "^3.0.0"
|
||||
mime "^2.3.1"
|
||||
object-assign "^4.1.1"
|
||||
uuid "^3.0.0"
|
||||
|
||||
anser@^1.4.9:
|
||||
version "1.4.9"
|
||||
resolved "https://registry.yarnpkg.com/anser/-/anser-1.4.9.tgz#1f85423a5dcf8da4631a341665ff675b96845760"
|
||||
|
@ -9066,6 +9091,11 @@ file-entry-cache@^5.0.1:
|
|||
dependencies:
|
||||
flat-cache "^2.0.1"
|
||||
|
||||
file-type@^7.7.1:
|
||||
version "7.7.1"
|
||||
resolved "https://registry.yarnpkg.com/file-type/-/file-type-7.7.1.tgz#91c2f5edb8ce70688b9b68a90d931bbb6cb21f65"
|
||||
integrity sha512-bTrKkzzZI6wH+NXhyD3SOXtb2zXTw2SbwI2RxUlRcXVsnN7jNL5hJzVQLYv7FOQhxFkK4XWdAflEaWFpaLLWpQ==
|
||||
|
||||
file-type@^9.0.0:
|
||||
version "9.0.0"
|
||||
resolved "https://registry.yarnpkg.com/file-type/-/file-type-9.0.0.tgz#a68d5ad07f486414dfb2c8866f73161946714a18"
|
||||
|
@ -9357,6 +9387,15 @@ fs-extra@^1.0.0:
|
|||
jsonfile "^2.1.0"
|
||||
klaw "^1.0.0"
|
||||
|
||||
fs-extra@^6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-6.0.1.tgz#8abc128f7946e310135ddc93b98bddb410e7a34b"
|
||||
integrity sha512-GnyIkKhhzXZUWFCaJzvyDLEEgDkPfb4/TPvJCJVuS8MWZgoSsErf++QpiAlDnKFcqhRlm+tIOcencCjyJE6ZCA==
|
||||
dependencies:
|
||||
graceful-fs "^4.1.2"
|
||||
jsonfile "^4.0.0"
|
||||
universalify "^0.1.0"
|
||||
|
||||
fs-extra@^7.0.1, fs-extra@~7.0.1:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9"
|
||||
|
@ -11447,6 +11486,13 @@ js2xmlparser2@^0.2.0:
|
|||
resolved "https://registry.yarnpkg.com/js2xmlparser2/-/js2xmlparser2-0.2.0.tgz#a7ca2089b83d02331d631892dd6743864125033f"
|
||||
integrity sha1-p8ogibg9AjMdYxiS3WdDhkElAz8=
|
||||
|
||||
js2xmlparser@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/js2xmlparser/-/js2xmlparser-3.0.0.tgz#3fb60eaa089c5440f9319f51760ccd07e2499733"
|
||||
integrity sha1-P7YOqgicVED5MZ9RdgzNB+JJlzM=
|
||||
dependencies:
|
||||
xmlcreate "^1.0.1"
|
||||
|
||||
jsbn@~0.1.0:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
|
||||
|
@ -17907,7 +17953,7 @@ uuid-js@^0.7.5:
|
|||
resolved "https://registry.yarnpkg.com/uuid-js/-/uuid-js-0.7.5.tgz#6c886d02a53d2d40dcf25d91a170b4a7b25b94d0"
|
||||
integrity sha1-bIhtAqU9LUDc8l2RoXC0p7JblNA=
|
||||
|
||||
uuid@^3.0.1, uuid@^3.3.2:
|
||||
uuid@^3.0.0, uuid@^3.0.1, uuid@^3.3.2:
|
||||
version "3.4.0"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
|
||||
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
|
||||
|
@ -18696,6 +18742,11 @@ xmlbuilder@~11.0.0:
|
|||
resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3"
|
||||
integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==
|
||||
|
||||
xmlcreate@^1.0.1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/xmlcreate/-/xmlcreate-1.0.2.tgz#fa6bf762a60a413fb3dd8f4b03c5b269238d308f"
|
||||
integrity sha1-+mv3YqYKQT+z3Y9LA8WyaSONMI8=
|
||||
|
||||
xmldoc@^1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/xmldoc/-/xmldoc-1.1.2.tgz#6666e029fe25470d599cd30e23ff0d1ed50466d7"
|
||||
|
|
Загрузка…
Ссылка в новой задаче