Remove appium from package dependency (#6636)

* remove appium

* update lint

* fix wdio build error

* fix the import error

* remove fake appium package

* implict timeout

* correct config to ms:experimental-webdriver

* remove implicit timeout, and fix some error on pipeline

* update doc and comment

* resolve merge conflicts on yarn.lock and move savescreenshot to jasmineopts

* Update code to new packages for isUtf8 and yargs

* Change files

* update doc and resolve lint isutf8 error

* remove appium

* update lint

* fix wdio build error

* fix the import error

* remove fake appium package

* implict timeout

* correct config to ms:experimental-webdriver

* remove implicit timeout, and fix some error on pipeline

* update doc and comment

* resolve merge conflicts on yarn.lock and move savescreenshot to jasmineopts

* Update code to new packages for isUtf8 and yargs

* Change files

* update doc and resolve lint isutf8 error

* resolve yarn.lock conflict with master

* Add Pyton to handle installing new npm dependency for appium

* yarn update

* Update UnitTests

* Change files

* install python for gyp

* Fix typo in path for sourcedirectoyr on python install step

* Update to import to const for isutf8 due to babel limmitations

* Fix diff file due to updated diffing algorythm

* Fix JS Check unittest

* Remove color from Lage build, it conflicts with updating execa or yargs-parser. And it wasn't needed anyways

* Update Yarn lock

* restore color args

* Lage doesn't work with fetch depth of 1

* Remove fetch depth because lage is missing files otherwise

* resolve conflict with master

* resolve yarn build error because of isUtf8

* async io test case

* Revert "async io test case"

This reverts commit e0920d5b3f.

* resolve conflicts on webdriver

* update test

* rollback change on sanitize test

* remove const comment

* try use the python in microsoft hosted agent

* Update docs/e2e-testing.md

Co-authored-by: Alexander Sklar <asklar@microsoft.com>

* Update docs/e2e-testing.md

Co-authored-by: Alexander Sklar <asklar@microsoft.com>

* fix break on yaml file

* update usepython

* revert the python change

Co-authored-by: Danny van Velzen <dannyvv@microsoft.com>
Co-authored-by: Alexander Sklar <asklar@microsoft.com>
This commit is contained in:
Canhua Li 2021-01-14 15:54:44 -08:00 коммит произвёл GitHub
Родитель ec3f4920e6
Коммит 3ffbf9311a
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
21 изменённых файлов: 874 добавлений и 2343 удалений

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

@ -56,13 +56,6 @@ jobs:
workingDirectory: $(Build.SourcesDirectory)
condition: false # Must be manually enabled, since it causes a 5x perf reduction that causes test instability
# See Issue 6393 for details
- powershell: |
Invoke-WebRequest https://github.com/microsoft/WinAppDriver/releases/download/v1.1/WindowsApplicationDriver.msi -OutFile $(Agent.TempDirectory)\WinAppDriver.msi
Start-Process msiexec -ArgumentList "/quiet","/x","{087BBF93-D9E3-4D27-BDBE-9C702E0066FC}" -Verb runAs -Wait
Start-Process msiexec -ArgumentList "/quiet","/i","$(Agent.TempDirectory)\WinAppDriver.msi" -Verb runAs -Wait
displayName: Replace WinAppDriver 1.2.1 with WinAppDriver 1.1
- task: CmdLine@2
displayName: run-windows
inputs:
@ -140,10 +133,10 @@ jobs:
condition: succeededOrFailed()
- task: PowerShell@2
displayName: "Show appium log"
displayName: "Show winappdriver log"
inputs:
targetType: "inline"
script: "Get-Content packages/E2ETest/reports/appium.txt | foreach {Write-Output $_}"
script: "Get-Content packages/E2ETest/reports/winappdriver.log | foreach {Write-Output $_}"
condition: failed()
- task: PowerShell@2

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

@ -38,6 +38,12 @@ steps:
inputs:
script: npm install -g yarn
# For Appium windows we have to depend on a node package that depends on python (gyp) to perform some install scripts
- script: |
nuget.exe install python -ExcludeVersion -OutputDirectory $(Build.SourcesDirectory)\Python
npm config set python $(Build.SourcesDirectory)/Python/python/tools/python.exe
displayName: Get Python
- template: yarn-install.yml
- task: CmdLine@2

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

@ -0,0 +1,7 @@
{
"type": "prerelease",
"comment": "Remove appium from package dependency",
"packageName": "@react-native-windows/telemetry",
"email": "dannyvv@microsoft.com",
"dependentChangeType": "patch"
}

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

@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "Update code to new packages for isUtf8 and yargs",
"packageName": "@rnw-scripts/integrate-rn",
"email": "canli@microsoft.com",
"dependentChangeType": "patch"
}

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

@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "Update code to new packages for isUtf8 and yargs",
"packageName": "react-native-platform-override",
"email": "canli@microsoft.com",
"dependentChangeType": "patch"
}

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

@ -194,9 +194,11 @@ jasmine runs in a child node process, so the "autoAttachChildProcesses" : true i
A `node` test runner is the first choice since we started the investigation for E2E test. React Native apps are written in JavaScript and it&#39;s a good choose to select a JavaScript framework to author the test case. It would be more friendly to the community, so MSTest with C# is excluded in our option.
### Appium
[Appium](http://appium.io/) is a W3C-standards based technology based on Selenium, a popular test technology for the web. Appium supports Windows as well as Android, iOS, and MacOS. It's recommended that you get some training on this technology before writing tests. Plurualsight has some great courses, see:
[Appium the Big Picture](https://app.pluralsight.com/player?course=appium-big-picture&author=marcel-devries&name=6d3fc4a8-e30e-41f2-aaaf-252483d2b017&clip=5&mode=live)
[Getting Started with Appium](https://www.pluralsight.com/courses/getting-started-ui-testing-appium)
[Appium](http://appium.io/) is a W3C-standards based technology based on Selenium, a popular test technology for the web. Appium supports Windows as well as Android, iOS, and MacOS.
Because WinAppDriver implements part of W3C-standards, appium is an optional component in Windows.
Note: Appium is finally removed from RNW project in [PR 6636](https://github.com/microsoft/react-native-windows/pull/6636). So wdio communicates with WinAppDriver directly. Instead of Appium service, [WinAppDriver service](https://github.com/licanhua/wdio-winappdriver-service) is used to launch and stop WinAppDriver during the testing.
### WinAppDriver + WebDriverIO + Jasmine
There is not existing example we can follow to setup E2E testing on Windows for React Native, and I spent weeks to investigate, test and prototype for our E2E. Hereafter I explain what kind of decisions I made and why I made these decisions
@ -243,9 +245,7 @@ There are two possible setup in dev environment based on with/without appium:
- Option 2
![SetupOptions1](img/SetupOptions2.png)
Option 1 is recommended and implemented by default. Appium and WinAppDriver are launched before spec is executed and they are killed after the spec is finished.
Option 2 is for advance user only. Each time we release a new WinAppDriver, we also need to update code on Appium. Before Appium has a new release, option 2 is the only way to verify the new features provides by WinAppDriver.
Because of project [wdio-winappdriver-service](https://github.com/licanhua/wdio-winappdriver-service), option 2 is recommended and implemented in RNW e2e testing.
If `yarn install` is run as admin privilege, WinAppDriver would be installed automatically, otherwise you need to install WinAppDriver manually.
@ -296,16 +296,6 @@ A unique `accessiblity id`/`testID` per Window is recommended for React Native W
| $(&#39;~AppNameTitle&#39;) | accessibility id |
| $(&#39;TextBlock&#39;) | class name |
### [Locators selenium-appium supports](https://github.com/react-native-windows/selenium-appium/blob/master/src/by2.ts) for selenium-webdriver
| **Client API by Example** | **Locator Strategy** |
| --- | --- |
| By2.nativeAccessibilityId(&#39;AppNameTitle&#39;) | accessibility id |
| By2.nativeClassName(&#39;TextBlock&#39;) | class name |
| By2.nativeXpath(&#39;//Button[0]&#39;) | xpath |
| By2.nativeName(&#39;Calculator&#39;) | name |
| By2.nativeId(&#39;42.333896.3.1&#39;) | id |
## Timers
1. wdio.conf.js (see [https://webdriver.io/docs/timeouts.html](https://webdriver.io/docs/timeouts.html) )
@ -345,22 +335,16 @@ LoginPage.waitForPageLoaded(15000)
### Capabilities
capabilities are the configuration which appium/WinAppDriver used to identify the app and launch the app. Below configurations supports both with and without appium in the setup.
**&#39;appium:app&#39;** is used by appium when the setup is WebDriverIO <-> Appium <-> WinAppDriver,
**&#39;app&#39;** is used for directly connection between WebDriverIO <-> WinAppDriver.
the `capabilities` array is the set of configuration options that WinAppDriver uses to identify the app, and launch it. The below configuration options are supported when appium is not involved in the setup.
`ms:experimental-webdriver` is a mandatory setting to make WinAppDriver use the W3C protocol.
`ms:experimental-webdriver` is an mandatory setting to make WinAppDriver use W3C protocol.
```
capabilities: [
{
maxInstances: 1,
platformName: 'windows',
'appium:deviceName': 'WindowsPC',
'appium:app': 'ReactUWPTestApp_cezq6h4ygq1hw!App',
'deviceName': 'WindowsPC',
'app': 'ReactUWPTestApp_cezq6h4ygq1hw!App',
'winAppDriver:experimental-w3c': true,
app: 'ReactUWPTestApp_cezq6h4ygq1hw!App',
'ms:experimental-webdriver': true,
},
```
@ -376,38 +360,18 @@ capabilities are the configuration which appium/WinAppDriver used to identify th
waitforTimeout: 10000,
```
### appium service
### winappdriver service
Below configuration lets the framework launch/kill appium automatically during the testing, and logs are saved as `reports\appium.txt`.
The below configuration lets the framework launch/terminate WinAppDriver automatically during testing, and logs are saved as `reports\winappdriver.txt`.
```
port: 4723,
services: ['appium'],
appium: {
services: ['winappdriver'],
winappdriver: {
logPath: './reports/',
args: {
port: '4723',
}
},
```
But the log like below is not that readable because it has a lot of control characters which is used by terminal.
![Bad Readability](img/BadReadability.png)
You could get nice output if you launch the appium by yourself:
1. Modify the configuration and remove `appium` from services
```
services: [],
```
2. Start appium by yourself
```
.\node\_modules\.bin\appium
```
### Test framework
```
framework: 'jasmine'
@ -516,4 +480,3 @@ You can get the symbols from the `appxsym` (just download it and rename it to `.
After doing a sync, E2E tests currently fail, see details in https://github.com/microsoft/react-native-windows/issues/5762
The workaround is to do a yarn install --force, then re-run the tests.

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

@ -34,28 +34,10 @@
"lage": "^0.24.0"
},
"resolutions": {
"@wdio/appium-service": "5.12.1",
"@wdio/cli": "5.12.1",
"@wdio/config": "5.12.1",
"@wdio/dot-reporter": "5.12.1",
"@wdio/jasmine-framework": "5.12.1",
"@wdio/junit-reporter": "5.12.1",
"@wdio/local-runner": "5.12.1",
"@wdio/logger": "5.12.1",
"@wdio/repl": "5.12.1",
"@wdio/runner": "5.12.1",
"@wdio/sync": "5.12.1",
"@wdio/utils": "5.12.1",
"appium-android-driver": "4.12.0-stub.0",
"appium-selendroid-driver": "1.13.4-stub.0",
"appium-tizen-driver": "1.1.1-stub.0",
"axios": "^0.21.1",
"eslint-plugin-react-hooks": "4.0.7",
"kind-of": "6.0.3",
"node-fetch": "2.6.1",
"node-notifier": "^9.0.0",
"webdriver": "git+https://github.com/react-native-windows/webdriver.git",
"webdriverio": "5.12.1",
"yargs-parser": "^18.1.1"
},
"beachball": {

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

@ -39,7 +39,7 @@ let logger: Logger;
},
})
.check(args => {
if (args._.length === 1 && semver.valid(args._[0])) {
if (args._.length === 1 && semver.valid(<string>args._[0])) {
return true;
} else {
throw new Error('Usage: integrate-rn <version>');
@ -47,7 +47,7 @@ let logger: Logger;
})
.showHelpOnFail(false);
const version = argv._[0];
const version = <string>argv._[0];
logger = new CompositeLogger([
new ConsoleLogger(process.stdout),

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

@ -34,14 +34,12 @@
"@types/node": "^10.14.8",
"@types/react": "16.9.0",
"@types/react-native": "^0.63.18",
"@wdio/appium-service": "5.12.1",
"@wdio/cli": "5.12.1",
"@wdio/dot-reporter": "5.12.1",
"@wdio/jasmine-framework": "5.12.1",
"@wdio/junit-reporter": "5.12.1",
"@wdio/local-runner": "5.12.1",
"@wdio/sync": "5.12.1",
"appium": "1.14.1",
"@wdio/cli": "^6.9.0",
"@wdio/dot-reporter": "^6.8.1",
"@wdio/jasmine-framework": "^6.8.0",
"@wdio/junit-reporter": "^6.8.1",
"@wdio/local-runner": "^6.11.3",
"@wdio/sync": "^6.9.0",
"eslint": "7.12.0",
"just-scripts": "^0.44.7",
"metro-react-native-babel-preset": "^0.56.0",
@ -51,7 +49,7 @@
"ts-node": "^7.0.1",
"tsconfig-paths": "^3.8.0",
"typescript": "^3.8.3",
"webdriver": "git+https://github.com/react-native-windows/webdriver.git",
"webdriverio": "5.12.1"
"webdriverio": "^6.9.0",
"wdio-winappdriver-service": "^0.1.0"
}
}

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

@ -94,7 +94,10 @@ function parseLog(logfile) {
function parseLogs() {
const reportsDir = path.join(__dirname, 'reports');
const logs = fs.readdirSync(reportsDir).filter(x => x.endsWith('.log'));
const logs = fs
.readdirSync(reportsDir)
.filter(x => x.endsWith('.log'))
.filter(x => x !== 'winappdriver.log');
const names = logs
.map(x => parseLog(path.join(reportsDir, x)))
.filter(x => x !== null && x !== '');

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

@ -1,3 +1,5 @@
require('ts-node').register({ transpileOnly: true });
const baseUrl = 'https://webdriver.io';
exports.config = {
@ -66,16 +68,9 @@ exports.config = {
// grid with only 5 firefox instances available you can make sure that not more than
// 5 instances get started at a time.
maxInstances: 1,
//
platformName: 'windows',
// For W3C the appium capabilities need to have an extension prefix
// http://appium.io/docs/en/writing-running-appium/caps/
// This is `appium:` for all Appium Capabilities which can be found here
'appium:deviceName': 'WindowsPC',
'appium:app': 'ReactUWPTestApp_cezq6h4ygq1hw!App',
deviceName: 'WindowsPC',
app: 'ReactUWPTestApp_cezq6h4ygq1hw!App',
'winAppDriver:experimental-w3c': true,
'ms:experimental-webdriver': true,
},
// {
// // maxInstances can get overwritten per capability. So if you have an in house Selenium
@ -130,13 +125,14 @@ exports.config = {
// Services take over a specific job you don't want to take care of. They enhance
// your test setup with almost no effort. Unlike plugins, they don't add new
// commands. Instead, they hook themselves up into the test process.
services: ['appium'],
appium: {
logPath: './reports/',
args: {
port: '4723',
},
},
services: [
[
'winappdriver',
{
logPath: './reports/',
},
],
],
//
// Framework you want to run your specs with.
@ -167,6 +163,17 @@ exports.config = {
jasmineNodeOpts: {
// Needs to be longer than the 1m webdriverio timeout to let its errors be propgated
defaultTimeoutInterval: 100000,
expectationResultHandler: function(passed, assertion) {
/**
* only take screenshot if assertion failed
*/
if (passed) {
return;
}
let name = 'ERROR-' + Date.now();
browser.saveScreenshot('./errorShots/' + name + '.png');
},
},
//
@ -239,12 +246,8 @@ exports.config = {
* Function to be executed after a test (in Mocha/Jasmine) or a step (in Cucumber) starts.
* @param {Object} test test details
*/
afterTest: function(test) {
if (test.error !== undefined) {
let name = 'ERROR-' + Date.now();
browser.saveScreenshot('./errorShots/' + name + '.png');
}
},
// afterTest: function(test) {
// },
/**
* Hook that gets executed after the suite has ended
* @param {Object} suite suite details

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

@ -19,26 +19,26 @@ export class BasePage {
}
waitForElementLoaded(element: string) {
browser.waitUntil(
() => this.isElementLoaded(element),
ELEMENT_LOADED_TIMEOUT,
`Failed to find element with testId "${element}" within ${ELEMENT_LOADED_TIMEOUT}ms`
);
browser.waitUntil(() => this.isElementLoaded(element), {
timeout: ELEMENT_LOADED_TIMEOUT,
timeoutMsg: `Failed to find element with testId "${element}" within ${ELEMENT_LOADED_TIMEOUT}ms`,
});
}
waitForElementHidden(element: string) {
browser.waitUntil(
() => !this.isElementLoaded(element),
ELEMENT_LOADED_TIMEOUT,
`Element with testId "${element}" was not hidden within ${ELEMENT_LOADED_TIMEOUT}ms`
);
browser.waitUntil(() => !this.isElementLoaded(element), {
timeout: ELEMENT_LOADED_TIMEOUT,
timeoutMsg: `Element with testId "${element}" was not hidden within ${ELEMENT_LOADED_TIMEOUT}ms`,
});
}
waitForTreeDumpPassed(errorMessage: string) {
browser.waitUntil(
() => this.treeDumpResult.getText() === 'TreeDump:Passed',
ELEMENT_LOADED_TIMEOUT,
errorMessage
{
timeout: ELEMENT_LOADED_TIMEOUT,
timeoutMsg: errorMessage,
}
);
}

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

@ -1,7 +0,0 @@
{
"name": "appium-android-driver",
"version": "4.12.0-stub.0",
"private": true,
"description": "Stub package form appium-android-driver while we're stuck on an old version of Appium with insecure dependency tree",
"license": "MIT"
}

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

@ -1,7 +0,0 @@
{
"name": "appium-selendroid-driver",
"version": "1.13.4-stub.0",
"private": true,
"description": "Stub package form appium-android-driver while we're stuck on an old version of Appium with insecure dependency tree",
"license": "MIT"
}

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

@ -1,7 +0,0 @@
{
"name": "appium-tizen-driver",
"version": "1.1.1-stub.0",
"private": true,
"description": "Stub package form appium-tizen-driver so we don't bring in its web of insecure dependencies",
"license": "MIT"
}

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

@ -9,7 +9,7 @@ import * as crypto from 'crypto';
import * as path from 'path';
import {normalizePath, unixPath} from './PathUtils';
import FileRepository from './FileRepository';
import isutf8 from 'isutf8';
import isUtf8 from 'isutf8';
export type HashOpts = {
/**
@ -28,7 +28,7 @@ function normalizeContent(
): string | Buffer {
if (
opts.insensitivity === 'none' ||
(typeof content !== 'string' && !isutf8(content))
(typeof content !== 'string' && !isUtf8(content))
) {
return content;
}

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

@ -10,7 +10,7 @@ import * as path from 'path';
import {WritableFileRepository, bindVersion} from './FileRepository';
import GitReactFileRepository from './GitReactFileRepository';
import {hashFileOrDirectory} from './Hash';
import isutf8 from 'isutf8';
import isUtf8 from 'isutf8';
export interface UpgradeResult {
overrideName: string;
@ -79,7 +79,7 @@ export const UpgradeStrategies = {
}
const prettyPatched =
hasConflicts && isutf8(patchedFile)
hasConflicts && isUtf8(patchedFile)
? patchedFile
.toString('utf8')
.replace(/<<<<<<< ours/g, '<<<<<<< Upstream')

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

@ -15,7 +15,7 @@ import UpgradeStrategy, {
import {acquireGitRepo, usingFiles} from './Resource';
import GitReactFileRepository from '../GitReactFileRepository';
import {hashFileOrDirectory} from '../Hash';
import isutf8 from 'isutf8';
import isUtf8 from 'isutf8';
let gitReactRepo: GitReactFileRepository;
let disposeReactRepo: () => Promise<void>;
@ -337,7 +337,7 @@ async function evaluateStrategy(opts: {
path.join(__dirname, 'collateral', referenceFile),
);
if (isutf8(actualContent) && isutf8(expectedContent)) {
if (isUtf8(actualContent) && isUtf8(expectedContent)) {
expect(actualContent.toString().replace(/\r\n/g, '\n')).toBe(
expectedContent.toString().replace(/\r\n/g, '\n'),
);

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

@ -18,7 +18,8 @@ import GitReactFileRepository from '../GitReactFileRepository';
import {diff_match_patch} from 'diff-match-patch';
import {getInstalledRNVersion} from '../PackageUtils';
import {hashContent} from '../Hash';
import isutf8 from 'isutf8';
import isUtf8 from 'isutf8';
const {extensions, directory} = yargs
.options({
@ -204,7 +205,7 @@ function computeSimilarity(
override: Buffer,
source: Buffer,
): {similar: boolean; editDistance: number} {
if (!isutf8(override) || !isutf8(source)) {
if (!isUtf8(override) || !isUtf8(source)) {
return {similar: false, editDistance: NaN};
}

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

@ -7,7 +7,7 @@
import {Hasher, hashContent, hashFileOrDirectory} from '../Hash';
import MockFileRepository from './MockFileRepository';
import isutf8 from 'isutf8';
import isUtf8 from 'isutf8';
test('hashContent - Same String', () => {
expect(hashContent('a')).toBe(hashContent('a'));
@ -63,8 +63,8 @@ test('hashContent - Different Binary (Not UTF8)', () => {
const binary2 = Buffer.from(new Int8Array([0xff, 1, 42]));
// Make sure we really are testing invalid strings
expect(isutf8(binary1)).toBe(false);
expect(isutf8(binary2)).toBe(false);
expect(isUtf8(binary1)).toBe(false);
expect(isUtf8(binary2)).toBe(false);
expect(hashContent(Buffer.from(binary1))).not.toBe(
hashContent(Buffer.from(binary2)),

2952
yarn.lock

Разница между файлами не показана из-за своего большого размера Загрузить разницу