初始化提交

This commit is contained in:
2026-02-03 16:52:44 +08:00
commit d2f9806384
512 changed files with 65167 additions and 0 deletions

172
test/unit/utils-specs.js Normal file
View File

@@ -0,0 +1,172 @@
import { getXctestrunFilePath, getAdditionalRunContent, getXctestrunFileName } from '../../lib/utils';
import { PLATFORM_NAME_IOS, PLATFORM_NAME_TVOS } from '../../lib/constants';
import { withMocks } from '@appium/test-support';
import { fs } from '@appium/support';
import path from 'path';
import { fail } from 'assert';
import { arch } from 'os';
function get_arch() {
return arch() === 'arm64' ? 'arm64' : 'x86_64';
}
describe('utils', function () {
let chai;
before(async function() {
chai = await import('chai');
const chaiAsPromised = await import('chai-as-promised');
chai.should();
chai.use(chaiAsPromised.default);
});
describe('#getXctestrunFilePath', withMocks({fs}, function (mocks) {
const platformVersion = '12.0';
const sdkVersion = '12.2';
const udid = 'xxxxxyyyyyyzzzzzz';
const bootstrapPath = 'path/to/data';
const platformName = PLATFORM_NAME_IOS;
afterEach(function () {
mocks.verify();
});
it('should return sdk based path with udid', async function () {
mocks.fs.expects('exists')
.withExactArgs(path.resolve(`${bootstrapPath}/${udid}_${sdkVersion}.xctestrun`))
.returns(true);
mocks.fs.expects('copyFile')
.never();
const deviceInfo = {isRealDevice: true, udid, platformVersion, platformName};
await getXctestrunFilePath(deviceInfo, sdkVersion, bootstrapPath)
.should.eventually.equal(path.resolve(`${bootstrapPath}/${udid}_${sdkVersion}.xctestrun`));
});
it('should return sdk based path without udid, copy them', async function () {
mocks.fs.expects('exists')
.withExactArgs(path.resolve(`${bootstrapPath}/${udid}_${sdkVersion}.xctestrun`))
.returns(false);
mocks.fs.expects('exists')
.withExactArgs(path.resolve(`${bootstrapPath}/WebDriverAgentRunner_iphoneos${sdkVersion}-arm64.xctestrun`))
.returns(true);
mocks.fs.expects('copyFile')
.withExactArgs(
path.resolve(`${bootstrapPath}/WebDriverAgentRunner_iphoneos${sdkVersion}-arm64.xctestrun`),
path.resolve(`${bootstrapPath}/${udid}_${sdkVersion}.xctestrun`)
)
.returns(true);
const deviceInfo = {isRealDevice: true, udid, platformVersion};
await getXctestrunFilePath(deviceInfo, sdkVersion, bootstrapPath)
.should.eventually.equal(path.resolve(`${bootstrapPath}/${udid}_${sdkVersion}.xctestrun`));
});
it('should return platform based path', async function () {
mocks.fs.expects('exists')
.withExactArgs(path.resolve(`${bootstrapPath}/${udid}_${sdkVersion}.xctestrun`))
.returns(false);
mocks.fs.expects('exists')
.withExactArgs(path.resolve(`${bootstrapPath}/WebDriverAgentRunner_iphonesimulator${sdkVersion}-${get_arch()}.xctestrun`))
.returns(false);
mocks.fs.expects('exists')
.withExactArgs(path.resolve(`${bootstrapPath}/${udid}_${platformVersion}.xctestrun`))
.returns(true);
mocks.fs.expects('copyFile')
.never();
const deviceInfo = {isRealDevice: false, udid, platformVersion};
await getXctestrunFilePath(deviceInfo, sdkVersion, bootstrapPath)
.should.eventually.equal(path.resolve(`${bootstrapPath}/${udid}_${platformVersion}.xctestrun`));
});
it('should return platform based path without udid, copy them', async function () {
mocks.fs.expects('exists')
.withExactArgs(path.resolve(`${bootstrapPath}/${udid}_${sdkVersion}.xctestrun`))
.returns(false);
mocks.fs.expects('exists')
.withExactArgs(path.resolve(`${bootstrapPath}/WebDriverAgentRunner_iphonesimulator${sdkVersion}-${get_arch()}.xctestrun`))
.returns(false);
mocks.fs.expects('exists')
.withExactArgs(path.resolve(`${bootstrapPath}/${udid}_${platformVersion}.xctestrun`))
.returns(false);
mocks.fs.expects('exists')
.withExactArgs(path.resolve(`${bootstrapPath}/WebDriverAgentRunner_iphonesimulator${platformVersion}-${get_arch()}.xctestrun`))
.returns(true);
mocks.fs.expects('copyFile')
.withExactArgs(
path.resolve(`${bootstrapPath}/WebDriverAgentRunner_iphonesimulator${platformVersion}-${get_arch()}.xctestrun`),
path.resolve(`${bootstrapPath}/${udid}_${platformVersion}.xctestrun`)
)
.returns(true);
const deviceInfo = {isRealDevice: false, udid, platformVersion};
await getXctestrunFilePath(deviceInfo, sdkVersion, bootstrapPath)
.should.eventually.equal(path.resolve(`${bootstrapPath}/${udid}_${platformVersion}.xctestrun`));
});
it('should raise an exception because of no files', async function () {
const expected = path.resolve(`${bootstrapPath}/WebDriverAgentRunner_iphonesimulator${sdkVersion}-${get_arch()}.xctestrun`);
mocks.fs.expects('exists').exactly(4).returns(false);
const deviceInfo = {isRealDevice: false, udid, platformVersion};
try {
await getXctestrunFilePath(deviceInfo, sdkVersion, bootstrapPath);
fail();
} catch (err) {
err.message.should.equal(`If you are using 'useXctestrunFile' capability then you need to have a xctestrun file (expected: '${expected}')`);
}
});
}));
describe('#getAdditionalRunContent', function () {
it('should return ios format', function () {
const wdaPort = getAdditionalRunContent(PLATFORM_NAME_IOS, 8000);
wdaPort.WebDriverAgentRunner
.EnvironmentVariables.USE_PORT
.should.equal('8000');
});
it('should return tvos format', function () {
const wdaPort = getAdditionalRunContent(PLATFORM_NAME_TVOS, '9000');
wdaPort.WebDriverAgentRunner_tvOS
.EnvironmentVariables.USE_PORT
.should.equal('9000');
});
});
describe('#getXctestrunFileName', function () {
const platformVersion = '12.0';
const udid = 'xxxxxyyyyyyzzzzzz';
it('should return ios format, real device', function () {
const platformName = 'iOs';
const deviceInfo = {isRealDevice: true, udid, platformVersion, platformName};
getXctestrunFileName(deviceInfo, '10.2.0').should.equal(
'WebDriverAgentRunner_iphoneos10.2.0-arm64.xctestrun');
});
it('should return ios format, simulator', function () {
const platformName = 'ios';
const deviceInfo = {isRealDevice: false, udid, platformVersion, platformName};
getXctestrunFileName(deviceInfo, '10.2.0').should.equal(
`WebDriverAgentRunner_iphonesimulator10.2.0-${get_arch()}.xctestrun`);
});
it('should return tvos format, real device', function () {
const platformName = 'tVos';
const deviceInfo = {isRealDevice: true, udid, platformVersion, platformName};
getXctestrunFileName(deviceInfo, '10.2.0').should.equal(
'WebDriverAgentRunner_tvOS_appletvos10.2.0-arm64.xctestrun');
});
it('should return tvos format, simulator', function () {
const platformName = 'tvOS';
const deviceInfo = {isRealDevice: false, udid, platformVersion, platformName};
getXctestrunFileName(deviceInfo, '10.2.0').should.equal(
`WebDriverAgentRunner_tvOS_appletvsimulator10.2.0-${get_arch()}.xctestrun`);
});
});
});

View File

@@ -0,0 +1,412 @@
import { BOOTSTRAP_PATH } from '../../lib/utils';
import { WebDriverAgent } from '../../lib/webdriveragent';
import * as utils from '../../lib/utils';
import path from 'path';
import _ from 'lodash';
import sinon from 'sinon';
const fakeConstructorArgs = {
device: 'some sim',
platformVersion: '9',
host: 'me',
port: '5000',
realDevice: false
};
const defaultAgentPath = path.resolve(BOOTSTRAP_PATH, 'WebDriverAgent.xcodeproj');
const customBootstrapPath = '/path/to/wda';
const customAgentPath = '/path/to/some/agent/WebDriverAgent.xcodeproj';
const customDerivedDataPath = '/path/to/some/agent/DerivedData/';
describe('Constructor', function () {
let chai;
before(async function() {
chai = await import('chai');
const chaiAsPromised = await import('chai-as-promised');
chai.should();
chai.use(chaiAsPromised.default);
});
it('should have a default wda agent if not specified', function () {
let agent = new WebDriverAgent({}, fakeConstructorArgs);
agent.bootstrapPath.should.eql(BOOTSTRAP_PATH);
agent.agentPath.should.eql(defaultAgentPath);
});
it('should have custom wda bootstrap and default agent if only bootstrap specified', function () {
let agent = new WebDriverAgent({}, _.defaults({
bootstrapPath: customBootstrapPath,
}, fakeConstructorArgs));
agent.bootstrapPath.should.eql(customBootstrapPath);
agent.agentPath.should.eql(path.resolve(customBootstrapPath, 'WebDriverAgent.xcodeproj'));
});
it('should have custom wda bootstrap and agent if both specified', function () {
let agent = new WebDriverAgent({}, _.defaults({
bootstrapPath: customBootstrapPath,
agentPath: customAgentPath,
}, fakeConstructorArgs));
agent.bootstrapPath.should.eql(customBootstrapPath);
agent.agentPath.should.eql(customAgentPath);
});
it('should have custom derivedDataPath if specified', function () {
let agent = new WebDriverAgent({}, _.defaults({
derivedDataPath: customDerivedDataPath
}, fakeConstructorArgs));
agent.xcodebuild.derivedDataPath.should.eql(customDerivedDataPath);
});
});
describe('launch', function () {
it('should use webDriverAgentUrl override and return current status', async function () {
const override = 'http://mockurl:8100/';
const args = Object.assign({}, fakeConstructorArgs);
args.webDriverAgentUrl = override;
const agent = new WebDriverAgent({}, args);
const wdaStub = sinon.stub(agent, 'getStatus');
wdaStub.callsFake(function () {
return {build: 'data'};
});
await agent.launch('sessionId').should.eventually.eql({build: 'data'});
agent.url.href.should.eql(override);
agent.jwproxy.server.should.eql('mockurl');
agent.jwproxy.port.should.eql(8100);
agent.jwproxy.base.should.eql('');
agent.jwproxy.scheme.should.eql('http');
agent.noSessionProxy.server.should.eql('mockurl');
agent.noSessionProxy.port.should.eql(8100);
agent.noSessionProxy.base.should.eql('');
agent.noSessionProxy.scheme.should.eql('http');
wdaStub.reset();
});
});
describe('use wda proxy url', function () {
it('should use webDriverAgentUrl wda proxy url', async function () {
const override = 'http://127.0.0.1:8100/aabbccdd';
const args = Object.assign({}, fakeConstructorArgs);
args.webDriverAgentUrl = override;
const agent = new WebDriverAgent({}, args);
const wdaStub = sinon.stub(agent, 'getStatus');
wdaStub.callsFake(function () {
return {build: 'data'};
});
await agent.launch('sessionId').should.eventually.eql({build: 'data'});
agent.url.port.should.eql('8100');
agent.url.hostname.should.eql('127.0.0.1');
agent.url.path.should.eql('/aabbccdd');
agent.jwproxy.server.should.eql('127.0.0.1');
agent.jwproxy.port.should.eql(8100);
agent.jwproxy.base.should.eql('/aabbccdd');
agent.jwproxy.scheme.should.eql('http');
agent.noSessionProxy.server.should.eql('127.0.0.1');
agent.noSessionProxy.port.should.eql(8100);
agent.noSessionProxy.base.should.eql('/aabbccdd');
agent.noSessionProxy.scheme.should.eql('http');
});
});
describe('get url', function () {
it('should use default WDA listening url', function () {
const args = Object.assign({}, fakeConstructorArgs);
const agent = new WebDriverAgent({}, args);
agent.url.href.should.eql('http://127.0.0.1:8100/');
agent.setupProxies('mysession');
agent.jwproxy.scheme.should.eql('http');
agent.noSessionProxy.scheme.should.eql('http');
});
it('should use default WDA listening url with emply base url', function () {
const wdaLocalPort = '9100';
const wdaBaseUrl = '';
const args = Object.assign({}, fakeConstructorArgs);
args.wdaBaseUrl = wdaBaseUrl;
args.wdaLocalPort = wdaLocalPort;
const agent = new WebDriverAgent({}, args);
agent.url.href.should.eql('http://127.0.0.1:9100/');
agent.setupProxies('mysession');
agent.jwproxy.scheme.should.eql('http');
agent.noSessionProxy.scheme.should.eql('http');
});
it('should use customised WDA listening url', function () {
const wdaLocalPort = '9100';
const wdaBaseUrl = 'http://mockurl';
const args = Object.assign({}, fakeConstructorArgs);
args.wdaBaseUrl = wdaBaseUrl;
args.wdaLocalPort = wdaLocalPort;
const agent = new WebDriverAgent({}, args);
agent.url.href.should.eql('http://mockurl:9100/');
agent.setupProxies('mysession');
agent.jwproxy.scheme.should.eql('http');
agent.noSessionProxy.scheme.should.eql('http');
});
it('should use customised WDA listening url with slash', function () {
const wdaLocalPort = '9100';
const wdaBaseUrl = 'http://mockurl/';
const args = Object.assign({}, fakeConstructorArgs);
args.wdaBaseUrl = wdaBaseUrl;
args.wdaLocalPort = wdaLocalPort;
const agent = new WebDriverAgent({}, args);
agent.url.href.should.eql('http://mockurl:9100/');
agent.setupProxies('mysession');
agent.jwproxy.scheme.should.eql('http');
agent.noSessionProxy.scheme.should.eql('http');
});
it('should use the given webDriverAgentUrl and ignore other params', function () {
const args = Object.assign({}, fakeConstructorArgs);
args.wdaBaseUrl = 'http://mockurl/';
args.wdaLocalPort = '9100';
args.webDriverAgentUrl = 'https://127.0.0.1:8100/';
const agent = new WebDriverAgent({}, args);
agent.url.href.should.eql('https://127.0.0.1:8100/');
});
it('should set scheme to https for https webDriverAgentUrl', function () {
const args = Object.assign({}, fakeConstructorArgs);
args.webDriverAgentUrl = 'https://127.0.0.1:8100/';
const agent = new WebDriverAgent({}, args);
agent.setupProxies('mysession');
agent.jwproxy.scheme.should.eql('https');
agent.noSessionProxy.scheme.should.eql('https');
});
});
describe('setupCaching()', function () {
let wda;
let wdaStub;
let wdaStubUninstall;
const getTimestampStub = sinon.stub(utils, 'getWDAUpgradeTimestamp');
beforeEach(function () {
wda = new WebDriverAgent('1');
wdaStub = sinon.stub(wda, 'getStatus');
wdaStubUninstall = sinon.stub(wda, 'uninstall');
});
afterEach(function () {
for (const stub of [wdaStub, wdaStubUninstall, getTimestampStub]) {
if (stub) {
stub.reset();
}
}
});
it('should not call uninstall since no Running WDA', async function () {
wdaStub.callsFake(function () {
return null;
});
wdaStubUninstall.callsFake(_.noop);
await wda.setupCaching();
wdaStub.calledOnce.should.be.true;
wdaStubUninstall.notCalled.should.be.true;
_.isUndefined(wda.webDriverAgentUrl).should.be.true;
});
it('should not call uninstall since running WDA has only time', async function () {
wdaStub.callsFake(function () {
return {build: { time: 'Jun 24 2018 17:08:21' }};
});
wdaStubUninstall.callsFake(_.noop);
await wda.setupCaching();
wdaStub.calledOnce.should.be.true;
wdaStubUninstall.notCalled.should.be.true;
wda.webDriverAgentUrl.should.equal('http://127.0.0.1:8100/');
});
it('should call uninstall once since bundle id is not default without updatedWDABundleId capability', async function () {
wdaStub.callsFake(function () {
return {build: { time: 'Jun 24 2018 17:08:21', productBundleIdentifier: 'com.example.WebDriverAgent' }};
});
wdaStubUninstall.callsFake(_.noop);
await wda.setupCaching();
wdaStub.calledOnce.should.be.true;
wdaStubUninstall.calledOnce.should.be.true;
_.isUndefined(wda.webDriverAgentUrl).should.be.true;
});
it('should call uninstall once since bundle id is different with updatedWDABundleId capability', async function () {
wdaStub.callsFake(function () {
return {build: { time: 'Jun 24 2018 17:08:21', productBundleIdentifier: 'com.example.different.WebDriverAgent' }};
});
wdaStubUninstall.callsFake(_.noop);
await wda.setupCaching();
wdaStub.calledOnce.should.be.true;
wdaStubUninstall.calledOnce.should.be.true;
_.isUndefined(wda.webDriverAgentUrl).should.be.true;
});
it('should not call uninstall since bundle id is equal to updatedWDABundleId capability', async function () {
wda = new WebDriverAgent('1', { updatedWDABundleId: 'com.example.WebDriverAgent' });
wdaStub = sinon.stub(wda, 'getStatus');
wdaStubUninstall = sinon.stub(wda, 'uninstall');
wdaStub.callsFake(function () {
return {build: { time: 'Jun 24 2018 17:08:21', productBundleIdentifier: 'com.example.WebDriverAgent' }};
});
wdaStubUninstall.callsFake(_.noop);
await wda.setupCaching();
wdaStub.calledOnce.should.be.true;
wdaStubUninstall.notCalled.should.be.true;
wda.webDriverAgentUrl.should.equal('http://127.0.0.1:8100/');
});
it('should call uninstall if current revision differs from the bundled one', async function () {
wdaStub.callsFake(function () {
return {build: { upgradedAt: '1' }};
});
getTimestampStub.callsFake(() => '2');
wdaStubUninstall.callsFake(_.noop);
await wda.setupCaching();
wdaStub.calledOnce.should.be.true;
wdaStubUninstall.calledOnce.should.be.true;
});
it('should not call uninstall if current revision is the same as the bundled one', async function () {
wdaStub.callsFake(function () {
return {build: { upgradedAt: '1' }};
});
getTimestampStub.callsFake(() => '1');
wdaStubUninstall.callsFake(_.noop);
await wda.setupCaching();
wdaStub.calledOnce.should.be.true;
wdaStubUninstall.notCalled.should.be.true;
});
it('should not call uninstall if current revision cannot be retrieved from WDA status', async function () {
wdaStub.callsFake(function () {
return {build: {}};
});
getTimestampStub.callsFake(() => '1');
wdaStubUninstall.callsFake(_.noop);
await wda.setupCaching();
wdaStub.calledOnce.should.be.true;
wdaStubUninstall.notCalled.should.be.true;
});
it('should not call uninstall if current revision cannot be retrieved from the file system', async function () {
wdaStub.callsFake(function () {
return {build: { upgradedAt: '1' }};
});
getTimestampStub.callsFake(() => null);
wdaStubUninstall.callsFake(_.noop);
await wda.setupCaching();
wdaStub.calledOnce.should.be.true;
wdaStubUninstall.notCalled.should.be.true;
});
describe('uninstall', function () {
let device;
let wda;
let deviceGetBundleIdsStub;
let deviceRemoveAppStub;
beforeEach(function () {
device = {
getUserInstalledBundleIdsByBundleName: () => {},
removeApp: () => {}
};
wda = new WebDriverAgent('1', {device});
deviceGetBundleIdsStub = sinon.stub(device, 'getUserInstalledBundleIdsByBundleName');
deviceRemoveAppStub = sinon.stub(device, 'removeApp');
});
afterEach(function () {
for (const stub of [deviceGetBundleIdsStub, deviceRemoveAppStub]) {
if (stub) {
stub.reset();
}
}
});
it('should not call uninstall', async function () {
deviceGetBundleIdsStub.callsFake(() => []);
await wda.uninstall();
deviceGetBundleIdsStub.calledOnce.should.be.true;
deviceRemoveAppStub.notCalled.should.be.true;
});
it('should call uninstall once', async function () {
const uninstalledBundIds = [];
deviceGetBundleIdsStub.callsFake(() => ['com.appium.WDA1']);
deviceRemoveAppStub.callsFake((id) => uninstalledBundIds.push(id));
await wda.uninstall();
deviceGetBundleIdsStub.calledOnce.should.be.true;
deviceRemoveAppStub.calledOnce.should.be.true;
uninstalledBundIds.should.eql(['com.appium.WDA1']);
});
it('should call uninstall twice', async function () {
const uninstalledBundIds = [];
deviceGetBundleIdsStub.callsFake(() => ['com.appium.WDA1', 'com.appium.WDA2']);
deviceRemoveAppStub.callsFake((id) => uninstalledBundIds.push(id));
await wda.uninstall();
deviceGetBundleIdsStub.calledOnce.should.be.true;
deviceRemoveAppStub.calledTwice.should.be.true;
uninstalledBundIds.should.eql(['com.appium.WDA1', 'com.appium.WDA2']);
});
});
});
describe('usePreinstalledWDA related functions', function () {
describe('bundleIdForXctest', function () {
it('should have xctrunner automatically', function () {
const args = Object.assign({}, fakeConstructorArgs);
args.updatedWDABundleId = 'io.appium.wda';
const agent = new WebDriverAgent({}, args);
agent.bundleIdForXctest.should.equal('io.appium.wda.xctrunner');
});
it('should have xctrunner automatically with default bundle id', function () {
const args = Object.assign({}, fakeConstructorArgs);
const agent = new WebDriverAgent({}, args);
agent.bundleIdForXctest.should.equal('com.facebook.WebDriverAgentRunner.xctrunner');
});
it('should allow an empty string as xctrunner suffix', function () {
const args = Object.assign({}, fakeConstructorArgs);
args.updatedWDABundleId = 'io.appium.wda';
args.updatedWDABundleIdSuffix = '';
const agent = new WebDriverAgent({}, args);
agent.bundleIdForXctest.should.equal('io.appium.wda');
});
it('should allow an empty string as xctrunner suffix with default bundle id', function () {
const args = Object.assign({}, fakeConstructorArgs);
args.updatedWDABundleIdSuffix = '';
const agent = new WebDriverAgent({}, args);
agent.bundleIdForXctest.should.equal('com.facebook.WebDriverAgentRunner');
});
it('should have an arbitrary xctrunner suffix', function () {
const args = Object.assign({}, fakeConstructorArgs);
args.updatedWDABundleId = 'io.appium.wda';
args.updatedWDABundleIdSuffix = '.customsuffix';
const agent = new WebDriverAgent({}, args);
agent.bundleIdForXctest.should.equal('io.appium.wda.customsuffix');
});
});
});