mirror of
https://github.com/expo/eas-cli.git
synced 2026-11-14 17:50:32 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3f74ea369d | ||
|
|
28f80da648 | ||
|
|
a7260dfed3 | ||
|
|
5cd1b5eee3 | ||
|
|
9d627c6917 | ||
|
|
66dbcaf432 |
No files matched your search
@@ -0,0 +1,90 @@
|
||||
name: Update Appium command list
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 6 * * 1' # Every Monday at 06:00 UTC
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- .github/workflows/update-appium-commands.yml
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
generate:
|
||||
runs-on: ubuntu-latest
|
||||
name: Regenerate Appium command list
|
||||
outputs:
|
||||
changed: ${{ steps.diff.outputs.changed }}
|
||||
steps:
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Setup tools
|
||||
uses: ./.github/actions/setup-mise
|
||||
- name: Install dependencies
|
||||
run: yarn install --immutable
|
||||
- name: Regenerate the Appium command list
|
||||
# The generator installs Appium, XCUITest, and UiAutomator2 on demand,
|
||||
# so it always picks up the latest supported command sets.
|
||||
run: yarn workspace @expo/build-tools generate-appium-commands
|
||||
- name: Check whether the command list changed
|
||||
id: diff
|
||||
run: |
|
||||
if git diff --quiet -- packages/build-tools/src/steps/utils/appiumCommands.generated.ts; then
|
||||
echo 'changed=false' >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo 'changed=true' >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
- name: Upload the generated command list
|
||||
if: steps.diff.outputs.changed == 'true'
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
with:
|
||||
name: appium-command-list
|
||||
path: packages/build-tools/src/steps/utils/appiumCommands.generated.ts
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
|
||||
publish:
|
||||
needs: generate
|
||||
if: needs.generate.outputs.changed == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
name: Publish Appium command list update
|
||||
steps:
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Download the generated command list
|
||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
||||
with:
|
||||
name: appium-command-list
|
||||
path: packages/build-tools/src/steps/utils
|
||||
- name: Open a pull request
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.EXPO_BOT_PAT }}
|
||||
run: |
|
||||
branch=bot/update-appium-commands
|
||||
title='[build-tools] Update Appium command list'
|
||||
body=$'Automated update of the Appium, XCUITest, and UiAutomator2 command list.\n\nIf this PR adds new commands, typechecking will fail until a curated summary is added for each of them in `packages/build-tools/src/steps/utils/appiumCommandSummary.ts`.'
|
||||
|
||||
gh auth setup-git
|
||||
if git ls-remote --exit-code --heads origin "$branch" >/dev/null; then
|
||||
git fetch origin "refs/heads/$branch:refs/remotes/origin/$branch"
|
||||
fi
|
||||
|
||||
git config user.name 'Expo Bot'
|
||||
git config user.email [email protected]
|
||||
git checkout -B "$branch"
|
||||
git add packages/build-tools/src/steps/utils/appiumCommands.generated.ts
|
||||
git commit -m "$title"
|
||||
git push --force-with-lease origin "$branch"
|
||||
|
||||
if gh pr view "$branch" --json state --jq '.state' 2>/dev/null | grep -q OPEN; then
|
||||
gh pr edit "$branch" --title "$title" --body "$body" --add-reviewer sjchmiela
|
||||
else
|
||||
gh pr create --base main --head "$branch" --title "$title" --body "$body" \
|
||||
--label 'no changelog' --reviewer sjchmiela
|
||||
fi
|
||||
+23
-5
@@ -8,8 +8,30 @@ This is the log of notable changes to EAS CLI and related packages.
|
||||
|
||||
### 🎉 New features
|
||||
|
||||
- [eas-cli] Add an opt-in `--max-idle-time-minutes` flag to `eas simulator`; the session stops automatically after that many minutes without activity. ([#4156](https://github.com/expo/eas-cli/pull/4156) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- [build-tools] Preapprove custom URL schemes before opening them in iOS Simulator sessions to avoid the first-use confirmation prompt. ([#4274](https://github.com/expo/eas-cli/pull/4274) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
## [22.4.0](https://github.com/expo/eas-cli/releases/tag/v22.4.0) - 2026-08-24
|
||||
|
||||
### 🎉 New features
|
||||
|
||||
- [eas-cli] Add `--launch-arg`, `--open-url`, and Expo Go `--sdk-version` options to `eas simulator`. ([#4264](https://github.com/expo/eas-cli/pull/4264) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
- [build-tools] Pass launch arguments and a URL to open when launching applications in simulator sessions. ([#4263](https://github.com/expo/eas-cli/pull/4263) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
- [eas-cli] Add `eas billing:subscribe <plan>` to start a Stripe checkout for an EAS plan, and `eas billing:manage` to open the Stripe customer portal. ([#4007](https://github.com/expo/eas-cli/pull/4007) by [@jonsamp](https://github.com/jonsamp))
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- [build-tools] Support downloading iOS simulator archives whose app bundle contents are stored at the archive root. ([#4262](https://github.com/expo/eas-cli/pull/4262) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
|
||||
## [22.3.0](https://github.com/expo/eas-cli/releases/tag/v22.3.0) - 2026-08-24
|
||||
|
||||
### 🎉 New features
|
||||
|
||||
- [build-tools] Add `ios_signing_backend` option to the repack step. ([#4239](https://github.com/expo/eas-cli/pull/4239) by [@gabrieldonadel](https://github.com/gabrieldonadel))
|
||||
- [build-tools] Support an optional `package_version` input on `eas/start_serve_sim_remote_session`, so a simulator session can pin the `@expo/serve-sim` version instead of always running `latest`. ([#4253](https://github.com/expo/eas-cli/pull/4253) by [@gwdp](https://github.com/gwdp))
|
||||
- [build-tools] Add composable custom build functions for downloading, installing, and launching application archives in simulator sessions. ([#4222](https://github.com/expo/eas-cli/pull/4222) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
@@ -17,9 +39,7 @@ This is the log of notable changes to EAS CLI and related packages.
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
- [build-tools] Support downloading iOS simulator archives whose app bundle contents are stored at the archive root. ([#4262](https://github.com/expo/eas-cli/pull/4262) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
|
||||
### 🧹 Chores
|
||||
- [build-tools] Upload tvOS (and other non-iOS) IPAs to the App Store Connect platform declared in the bundle's `DTPlatformName`, instead of always uploading to the iOS train. ([#4234](https://github.com/expo/eas-cli/pull/4234) by [@douglowder](https://github.com/douglowder))
|
||||
|
||||
## [22.2.0](https://github.com/expo/eas-cli/releases/tag/v22.2.0) - 2026-08-20
|
||||
|
||||
@@ -27,7 +47,6 @@ This is the log of notable changes to EAS CLI and related packages.
|
||||
|
||||
- [eas-cli] Add `--device` flag to `eas simulator` for selecting the virtual device to start. ([#4172](https://github.com/expo/eas-cli/pull/4172) by [@szdziedzic](https://github.com/szdziedzic))
|
||||
- [eas-cli] Add support for Appium EAS Simulator sessions. ([#4190](https://github.com/expo/eas-cli/pull/4190) by [@sjchmiela](https://github.com/sjchmiela))
|
||||
- [eas-cli] Add `eas billing:subscribe <plan>` to start a Stripe checkout for an EAS plan, and `eas billing:manage` to open the Stripe customer portal. ([#4007](https://github.com/expo/eas-cli/pull/4007) by [@jonsamp](https://github.com/jonsamp))
|
||||
|
||||
### 🐛 Bug fixes
|
||||
|
||||
@@ -35,7 +54,6 @@ This is the log of notable changes to EAS CLI and related packages.
|
||||
- [eas-cli] Send the expected `Content-MD5` checksum with GCS signed URL uploads so GCS rejects corrupted uploads. ([#4208](https://github.com/expo/eas-cli/pull/4208) by [@wschurman](https://github.com/wschurman))
|
||||
- [eas-cli] Resolve the `eas go` SDK version to its repack target, so `--sdk-version 57` no longer fails with "No Expo Go repack target is published for SDK 57". ([#4215](https://github.com/expo/eas-cli/pull/4215) by [@gwdp](https://github.com/gwdp))
|
||||
- [eas-cli] Handle missing fingerprint source in fingerprint:compare command. ([#4210](https://github.com/expo/eas-cli/pull/4210) by [@wschurman](https://github.com/wschurman))
|
||||
- [build-tools] Upload tvOS (and other non-iOS) IPAs to the App Store Connect platform declared in the bundle's `DTPlatformName`, instead of always uploading to the iOS train. ([#4234](https://github.com/expo/eas-cli/pull/4234) by [@douglowder](https://github.com/douglowder))
|
||||
|
||||
### 🧹 Chores
|
||||
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"STAGING": "22.2.0",
|
||||
"PRODUCTION": "21.5.0"
|
||||
"STAGING": "22.4.0",
|
||||
"PRODUCTION": "22.4.0"
|
||||
}
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"npmClient": "yarn",
|
||||
"version": "22.4.0",
|
||||
"version": "22.5.0",
|
||||
"$schema": "node_modules/lerna/schemas/lerna-schema.json"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@expo/build-tools",
|
||||
"version": "22.4.0",
|
||||
"version": "22.5.0",
|
||||
"bugs": "https://github.com/expo/eas-cli/issues",
|
||||
"license": "BUSL-1.1",
|
||||
"author": "Expo <[email protected]>",
|
||||
@@ -25,6 +25,7 @@
|
||||
"build": "tsc",
|
||||
"build:record-sim": "mkdir -p bin && record_sim_bin_path=$(swift build -c release --package-path resources/record-sim --build-path resources/record-sim/.build --show-bin-path) && swift build -c release --package-path resources/record-sim --build-path resources/record-sim/.build && cp \"$record_sim_bin_path/record-sim\" bin/record-sim && chmod +x bin/record-sim",
|
||||
"typecheck": "tsc",
|
||||
"generate-appium-commands": "mise exec [email protected] -- node scripts/generate-appium-commands.js",
|
||||
"prepack": "rimraf dist \"*.tsbuildinfo\" && yarn gql && tsc -p tsconfig.build.json",
|
||||
"jest-unit": "jest --config jest/unit-config.ts",
|
||||
"jest-integration": "jest --config jest/integration-config.ts",
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
// Regenerates src/steps/utils/appiumCommands.generated.ts from Appium and the
|
||||
// XCUITest and UiAutomator2 drivers, so TypeScript can verify that we handle
|
||||
// every command that can appear in Appium event timings.
|
||||
//
|
||||
// Appium and its drivers are NOT dependencies of this package. Like repack, we
|
||||
// install them on demand into a temp directory just for this script, so nothing
|
||||
// heavy ships with @expo/build-tools.
|
||||
//
|
||||
// Run with: yarn workspace @expo/build-tools generate-appium-commands
|
||||
const { execFileSync } = require('node:child_process');
|
||||
const fs = require('node:fs');
|
||||
const os = require('node:os');
|
||||
const path = require('node:path');
|
||||
const { pathToFileURL } = require('node:url');
|
||||
|
||||
// Track the Appium 3 line, matching the appium@^3 the worker installs for
|
||||
// simulator sessions (see startAppiumRemoteSession.ts).
|
||||
const APPIUM_VERSION = '^3';
|
||||
const DRIVERS = [
|
||||
{
|
||||
packageName: 'appium-xcuitest-driver',
|
||||
exportName: 'XCUITestDriver',
|
||||
typeName: 'XCUITestAppiumCommand',
|
||||
},
|
||||
{
|
||||
packageName: 'appium-uiautomator2-driver',
|
||||
exportName: 'AndroidUiautomator2Driver',
|
||||
typeName: 'UiAutomator2AppiumCommand',
|
||||
},
|
||||
];
|
||||
|
||||
function renderCommandType(typeName, commands) {
|
||||
return `export type ${typeName} =\n${commands.map(command => ` | ${serializeStringLiteral(command)}`).join('\n')};`;
|
||||
}
|
||||
|
||||
function serializeStringLiteral(value) {
|
||||
const jsonContents = JSON.stringify(value).slice(1, -1).replaceAll('\\"', '"');
|
||||
return `'${jsonContents.replaceAll("'", "\\'")}'`;
|
||||
}
|
||||
|
||||
function readRouteCommands(methodMap) {
|
||||
return Object.values(methodMap).flatMap(route =>
|
||||
Object.values(route).map(method => method.command)
|
||||
);
|
||||
}
|
||||
|
||||
async function readDriverCommands({ packageName, exportName }, sandbox) {
|
||||
const packageJsonPath = require.resolve(`${packageName}/package.json`, { paths: [sandbox] });
|
||||
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
||||
const modulePath = path.join(path.dirname(packageJsonPath), packageJson.exports['.'].import);
|
||||
const driverModule = await import(pathToFileURL(modulePath).href);
|
||||
const Driver = driverModule[exportName];
|
||||
return [
|
||||
...readRouteCommands(Driver.newMethodMap),
|
||||
...Object.values(Driver.executeMethodMap).map(method => method.command),
|
||||
];
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const sandbox = fs.mkdtempSync(path.join(os.tmpdir(), 'appium-command-generator-'));
|
||||
try {
|
||||
fs.writeFileSync(
|
||||
path.join(sandbox, 'package.json'),
|
||||
`${JSON.stringify({ name: 'appium-command-generator-sandbox', private: true })}\n`
|
||||
);
|
||||
execFileSync(
|
||||
'npm',
|
||||
[
|
||||
'install',
|
||||
'--prefix',
|
||||
sandbox,
|
||||
`appium@${APPIUM_VERSION}`,
|
||||
...DRIVERS.map(({ packageName }) => packageName),
|
||||
],
|
||||
{ stdio: 'inherit' }
|
||||
);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const { ALL_COMMANDS } = require(require.resolve('@appium/base-driver', { paths: [sandbox] }));
|
||||
const baseDriverCommands = [...new Set(ALL_COMMANDS)].sort();
|
||||
const baseDriverCommandSet = new Set(baseDriverCommands);
|
||||
const driverCommands = await Promise.all(
|
||||
DRIVERS.map(async driver => ({
|
||||
typeName: driver.typeName,
|
||||
commands: [...new Set(await readDriverCommands(driver, sandbox))]
|
||||
.filter(command => !baseDriverCommandSet.has(command))
|
||||
.sort(),
|
||||
}))
|
||||
);
|
||||
const commands = [
|
||||
...new Set([...baseDriverCommands, ...driverCommands.flatMap(({ commands }) => commands)]),
|
||||
];
|
||||
|
||||
const outputPath = path.join(
|
||||
__dirname,
|
||||
'..',
|
||||
'src',
|
||||
'steps',
|
||||
'utils',
|
||||
'appiumCommands.generated.ts'
|
||||
);
|
||||
const contents = `// AUTO-GENERATED. Do not edit by hand.
|
||||
// Run \`yarn workspace @expo/build-tools generate-appium-commands\` to refresh this
|
||||
// list from Appium, XCUITest, and UiAutomator2 (installed on demand by the generator).
|
||||
${renderCommandType('BaseDriverAppiumCommand', baseDriverCommands)}
|
||||
|
||||
${driverCommands
|
||||
.map(({ typeName, commands: commandsForDriver }) =>
|
||||
renderCommandType(typeName, commandsForDriver)
|
||||
)
|
||||
.join('\n\n')}
|
||||
|
||||
export type AppiumCommand =
|
||||
| BaseDriverAppiumCommand
|
||||
| XCUITestAppiumCommand
|
||||
| UiAutomator2AppiumCommand;
|
||||
`;
|
||||
fs.writeFileSync(outputPath, contents);
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`Wrote ${commands.length} Appium commands to ${outputPath}`);
|
||||
} finally {
|
||||
fs.rmSync(sandbox, { recursive: true, force: true });
|
||||
}
|
||||
}
|
||||
|
||||
main().catch(error => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(error);
|
||||
process.exitCode = 1;
|
||||
});
|
||||
@@ -56,11 +56,80 @@ describe(launchApplicationAsync, () => {
|
||||
expect(mockedSpawn).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
'xcrun',
|
||||
[
|
||||
'simctl',
|
||||
'spawn',
|
||||
'booted',
|
||||
'defaults',
|
||||
'write',
|
||||
'com.apple.launchservices.schemeapproval',
|
||||
'com.apple.CoreSimulator.CoreSimulatorBridge-->exp',
|
||||
'-string',
|
||||
'host.exp.Exponent',
|
||||
],
|
||||
{ env: {}, logger }
|
||||
);
|
||||
expect(mockedSpawn).toHaveBeenNthCalledWith(
|
||||
3,
|
||||
'xcrun',
|
||||
['simctl', 'openurl', 'booted', 'exp://example.test'],
|
||||
{ env: {}, logger }
|
||||
);
|
||||
});
|
||||
|
||||
it('opens a web URL on iOS without preapproving its scheme', async () => {
|
||||
const logger = createMockLogger();
|
||||
|
||||
await launchApplicationAsync({
|
||||
applicationIdentifier: 'com.example.app',
|
||||
openUrl: 'https://example.test',
|
||||
runtimePlatform: BuildRuntimePlatform.DARWIN,
|
||||
env: {},
|
||||
logger,
|
||||
});
|
||||
|
||||
expect(mockedSpawn).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
'xcrun',
|
||||
['simctl', 'launch', 'booted', 'com.example.app'],
|
||||
{ env: {}, logger }
|
||||
);
|
||||
expect(mockedSpawn).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
'xcrun',
|
||||
['simctl', 'openurl', 'booted', 'https://example.test'],
|
||||
{ env: {}, logger }
|
||||
);
|
||||
expect(mockedSpawn).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('opens a custom URL on iOS when scheme preapproval fails', async () => {
|
||||
const logger = createMockLogger();
|
||||
const approvalError = new Error('Scheme approval is unavailable.');
|
||||
mockedSpawn
|
||||
.mockResolvedValueOnce({ stdout: '', stderr: '' } as any)
|
||||
.mockRejectedValueOnce(approvalError);
|
||||
|
||||
await launchApplicationAsync({
|
||||
applicationIdentifier: 'host.exp.Exponent',
|
||||
openUrl: 'exp://example.test',
|
||||
runtimePlatform: BuildRuntimePlatform.DARWIN,
|
||||
env: {},
|
||||
logger,
|
||||
});
|
||||
|
||||
expect(mockedSpawn).toHaveBeenNthCalledWith(
|
||||
3,
|
||||
'xcrun',
|
||||
['simctl', 'openurl', 'booted', 'exp://example.test'],
|
||||
{ env: {}, logger }
|
||||
);
|
||||
expect(logger.warn).toHaveBeenCalledWith(
|
||||
{ err: approvalError },
|
||||
'Could not preapprove the exp URL scheme for host.exp.Exponent. Opening the URL anyway; the Simulator might require manual confirmation.'
|
||||
);
|
||||
});
|
||||
|
||||
it('launches an Android Emulator application by package and activity', async () => {
|
||||
const logger = createMockLogger();
|
||||
|
||||
|
||||
@@ -9,6 +9,9 @@ import {
|
||||
} from '@expo/steps';
|
||||
import spawn from '@expo/turtle-spawn';
|
||||
|
||||
const IOS_URL_SCHEME_APPROVAL_DOMAIN = 'com.apple.launchservices.schemeapproval';
|
||||
const IOS_URL_SCHEME_APPROVAL_KEY_PREFIX = 'com.apple.CoreSimulator.CoreSimulatorBridge-->';
|
||||
|
||||
export function createLaunchApplicationFunction(): BuildFunction {
|
||||
return new BuildFunction({
|
||||
namespace: 'eas',
|
||||
@@ -86,6 +89,7 @@ export async function launchApplicationAsync({
|
||||
logger,
|
||||
});
|
||||
if (openUrl) {
|
||||
await preapproveIosUrlSchemeAsync({ applicationIdentifier, openUrl, env, logger });
|
||||
logger.info(`Opening ${openUrl} in ${applicationIdentifier}.`);
|
||||
await spawn('xcrun', ['simctl', 'openurl', 'booted', openUrl], { env, logger });
|
||||
}
|
||||
@@ -130,6 +134,46 @@ export async function launchApplicationAsync({
|
||||
}
|
||||
}
|
||||
|
||||
async function preapproveIosUrlSchemeAsync({
|
||||
applicationIdentifier,
|
||||
openUrl,
|
||||
env,
|
||||
logger,
|
||||
}: {
|
||||
applicationIdentifier: string;
|
||||
openUrl: string;
|
||||
env: BuildStepEnv;
|
||||
logger: bunyan;
|
||||
}): Promise<void> {
|
||||
const urlScheme = new URL(openUrl).protocol.slice(0, -1);
|
||||
if (urlScheme === 'http' || urlScheme === 'https') {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await spawn(
|
||||
'xcrun',
|
||||
[
|
||||
'simctl',
|
||||
'spawn',
|
||||
'booted',
|
||||
'defaults',
|
||||
'write',
|
||||
IOS_URL_SCHEME_APPROVAL_DOMAIN,
|
||||
`${IOS_URL_SCHEME_APPROVAL_KEY_PREFIX}${urlScheme}`,
|
||||
'-string',
|
||||
applicationIdentifier,
|
||||
],
|
||||
{ env, logger }
|
||||
);
|
||||
} catch (error) {
|
||||
logger.warn(
|
||||
{ err: error },
|
||||
`Could not preapprove the ${urlScheme} URL scheme for ${applicationIdentifier}. Opening the URL anyway; the Simulator might require manual confirmation.`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function logApplicationLaunch(
|
||||
logger: bunyan,
|
||||
applicationIdentifier: string,
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import { APPIUM_COMMAND_SUMMARIES, humanizeAppiumCommand } from '../appiumCommandSummary';
|
||||
|
||||
// Coverage of the generated Appium and driver command sets is enforced at compile time by the
|
||||
// `Record<AppiumCommand, string>` type in appiumCommandSummary.ts, so it is not
|
||||
// re-checked here.
|
||||
|
||||
describe('APPIUM_COMMAND_SUMMARIES', () => {
|
||||
it('has a non-empty summary that differs from the command name for every entry', () => {
|
||||
for (const [command, summary] of Object.entries(APPIUM_COMMAND_SUMMARIES)) {
|
||||
expect(summary).toBeTruthy();
|
||||
expect(summary).not.toBe(command);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe(humanizeAppiumCommand, () => {
|
||||
it.each([
|
||||
['getWindowRect', 'Read the screen size'],
|
||||
['getScreenshot', 'Took a screenshot'],
|
||||
['getPageSource', 'Read the screen contents'],
|
||||
['click', 'Tapped an element'],
|
||||
['setValue', 'Typed into an element'],
|
||||
['createSession', 'Started the session'],
|
||||
['deleteSession', 'Ended the session'],
|
||||
['postAcceptAlert', 'Accepted an alert'],
|
||||
['mobileScroll', 'Scrolled the screen'],
|
||||
['openNotifications', 'Opened notifications'],
|
||||
])('maps the known command %s to a curated summary', (command, expected) => {
|
||||
expect(humanizeAppiumCommand(command)).toBe(expected);
|
||||
});
|
||||
|
||||
it.each(['proxyReqRes', 'mobile: scroll', 'getDeviceInfo', 'toString', 'constructor', ''])(
|
||||
'returns the raw command %p when it has no curated summary',
|
||||
command => {
|
||||
expect(humanizeAppiumCommand(command)).toBe(command);
|
||||
}
|
||||
);
|
||||
});
|
||||
@@ -58,7 +58,10 @@ describe(startAppiumEventCollectionAsync, () => {
|
||||
producer: 'appium',
|
||||
type: 'operation.completed',
|
||||
durationMs: 25,
|
||||
summary: 'getOrientation',
|
||||
summary: 'Read the screen orientation',
|
||||
data: {
|
||||
command: 'getOrientation',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -67,10 +67,17 @@ describe(listArgentArtifactsAsync, () => {
|
||||
artifacts: [
|
||||
{
|
||||
id: 'artifact-id',
|
||||
kind: 'native-profile-report',
|
||||
filename: 'report.json',
|
||||
mimeType: 'application/json',
|
||||
isDirectory: false,
|
||||
},
|
||||
{
|
||||
id: 'legacy-artifact-id',
|
||||
filename: 'screen.png',
|
||||
mimeType: 'image/png',
|
||||
isDirectory: false,
|
||||
},
|
||||
],
|
||||
})
|
||||
)
|
||||
@@ -84,10 +91,17 @@ describe(listArgentArtifactsAsync, () => {
|
||||
expect(artifacts).toEqual([
|
||||
{
|
||||
id: 'artifact-id',
|
||||
kind: 'native-profile-report',
|
||||
filename: 'report.json',
|
||||
mimeType: 'application/json',
|
||||
isDirectory: false,
|
||||
},
|
||||
{
|
||||
id: 'legacy-artifact-id',
|
||||
filename: 'screen.png',
|
||||
mimeType: 'image/png',
|
||||
isDirectory: false,
|
||||
},
|
||||
]);
|
||||
expect(jest.mocked(fetch)).toHaveBeenCalledWith('http://127.0.0.1:1234/artifacts', {
|
||||
headers: { Authorization: 'Bearer tools-token' },
|
||||
@@ -143,6 +157,7 @@ describe(uploadArgentArtifactAsync, () => {
|
||||
|
||||
async function uploadAsync(artifact: {
|
||||
id: string;
|
||||
kind?: string;
|
||||
filename: string;
|
||||
mimeType: string;
|
||||
isDirectory?: boolean;
|
||||
@@ -185,7 +200,42 @@ describe(uploadArgentArtifactAsync, () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('leaves directory artifacts unclassified because they are uploaded as a tarball', async () => {
|
||||
it('uses the semantic kind reported by Argent', async () => {
|
||||
const ctx = await uploadAsync({
|
||||
id: 'artifact-id',
|
||||
kind: 'native-profile-report',
|
||||
filename: 'native-profile-report.md',
|
||||
mimeType: 'text/markdown',
|
||||
});
|
||||
|
||||
expect(jest.mocked(uploadDeviceRunSessionArtifactAsync)).toHaveBeenCalledWith(
|
||||
ctx,
|
||||
expect.objectContaining({
|
||||
filename: 'native-profile-report.md',
|
||||
kind: 'native-profile-report',
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('keeps the semantic kind when a directory is uploaded as a tarball', async () => {
|
||||
const ctx = await uploadAsync({
|
||||
id: 'artifact-id',
|
||||
kind: 'native-profile-trace',
|
||||
filename: 'native-profile.trace',
|
||||
mimeType: 'application/octet-stream',
|
||||
isDirectory: true,
|
||||
});
|
||||
|
||||
expect(jest.mocked(uploadDeviceRunSessionArtifactAsync)).toHaveBeenCalledWith(
|
||||
ctx,
|
||||
expect.objectContaining({
|
||||
filename: 'native-profile.trace.tar.gz',
|
||||
kind: 'native-profile-trace',
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('leaves an older unclassified directory artifact without a kind', async () => {
|
||||
const ctx = await uploadAsync({
|
||||
id: 'artifact-id',
|
||||
filename: 'screenshots',
|
||||
|
||||
@@ -0,0 +1,418 @@
|
||||
import { type AppiumCommand } from './appiumCommands.generated';
|
||||
|
||||
export const APPIUM_COMMAND_SUMMARIES: Record<AppiumCommand, string> = {
|
||||
// Session lifecycle
|
||||
createSession: 'Started the session',
|
||||
deleteSession: 'Ended the session',
|
||||
getSession: 'Read the session details',
|
||||
getStatus: 'Checked the server status',
|
||||
getAppiumSessions: 'Listed Appium sessions',
|
||||
getAppiumSessionCapabilities: 'Read the session capabilities',
|
||||
getTimeouts: 'Read timeouts',
|
||||
timeouts: 'Set timeouts',
|
||||
getSettings: 'Read Appium settings',
|
||||
updateSettings: 'Updated Appium settings',
|
||||
listCommands: 'Listed available commands',
|
||||
listExtensions: 'Listed available extensions',
|
||||
receiveAsyncResponse: 'Received an async response',
|
||||
|
||||
// Screen and media
|
||||
getScreenshot: 'Took a screenshot',
|
||||
getElementScreenshot: 'Took a screenshot of an element',
|
||||
getPageSource: 'Read the screen contents',
|
||||
printPage: 'Printed the page',
|
||||
|
||||
// Window and orientation
|
||||
getWindowRect: 'Read the screen size',
|
||||
setWindowRect: 'Resized the window',
|
||||
maximizeWindow: 'Maximized the window',
|
||||
minimizeWindow: 'Minimized the window',
|
||||
fullScreenWindow: 'Made the window full screen',
|
||||
getWindowHandle: 'Read the current window',
|
||||
getWindowHandles: 'Listed open windows',
|
||||
setWindow: 'Switched window',
|
||||
closeWindow: 'Closed the window',
|
||||
createNewWindow: 'Opened a new window',
|
||||
setFrame: 'Switched to a frame',
|
||||
switchToParentFrame: 'Switched to the parent frame',
|
||||
getOrientation: 'Read the screen orientation',
|
||||
setOrientation: 'Changed the screen orientation',
|
||||
getRotation: 'Read the device rotation',
|
||||
setRotation: 'Rotated the device',
|
||||
|
||||
// Device
|
||||
getDeviceTime: 'Read the device time',
|
||||
getGeoLocation: 'Read the device location',
|
||||
setGeoLocation: 'Set the device location',
|
||||
getNetworkConnection: 'Read the network connection',
|
||||
setNetworkConnection: 'Changed the network connection',
|
||||
setDevicePosture: 'Set the device posture',
|
||||
clearDevicePosture: 'Cleared the device posture',
|
||||
setPermissions: 'Set app permissions',
|
||||
|
||||
// Element discovery
|
||||
findElement: 'Found an element',
|
||||
findElements: 'Found elements',
|
||||
findElementFromElement: 'Found a nested element',
|
||||
findElementsFromElement: 'Found nested elements',
|
||||
findElementFromShadowRoot: 'Found an element in a shadow root',
|
||||
findElementsFromShadowRoot: 'Found elements in a shadow root',
|
||||
elementShadowRoot: 'Read an element shadow root',
|
||||
active: 'Read the focused element',
|
||||
|
||||
// Element interaction
|
||||
click: 'Tapped an element',
|
||||
clear: 'Cleared an element',
|
||||
setValue: 'Typed into an element',
|
||||
getText: 'Read element text',
|
||||
getName: 'Read an element tag',
|
||||
getAttribute: 'Read an element attribute',
|
||||
getProperty: 'Read an element property',
|
||||
getCssProperty: 'Read an element style',
|
||||
getComputedLabel: 'Read an element label',
|
||||
getComputedRole: 'Read an element role',
|
||||
getElementRect: 'Read an element position and size',
|
||||
elementDisplayed: 'Checked if an element was visible',
|
||||
elementEnabled: 'Checked if an element was enabled',
|
||||
elementSelected: 'Checked if an element was selected',
|
||||
|
||||
// Gestures
|
||||
performActions: 'Performed a gesture',
|
||||
releaseActions: 'Finished a gesture',
|
||||
|
||||
// App management
|
||||
installApp: 'Installed an app',
|
||||
removeApp: 'Removed an app',
|
||||
isAppInstalled: 'Checked if an app was installed',
|
||||
activateApp: 'Activated an app',
|
||||
terminateApp: 'Terminated an app',
|
||||
queryAppState: 'Read the app state',
|
||||
background: 'Sent the app to the background',
|
||||
|
||||
// Contexts
|
||||
getContexts: 'Listed available contexts',
|
||||
getCurrentContext: 'Read the current context',
|
||||
setContext: 'Switched context',
|
||||
|
||||
// Web navigation
|
||||
setUrl: 'Opened a URL',
|
||||
getUrl: 'Read the current URL',
|
||||
back: 'Navigated back',
|
||||
forward: 'Navigated forward',
|
||||
refresh: 'Refreshed the page',
|
||||
title: 'Read the page title',
|
||||
|
||||
// Cookies
|
||||
getCookie: 'Read a cookie',
|
||||
getCookies: 'Read all cookies',
|
||||
setCookie: 'Set a cookie',
|
||||
deleteCookie: 'Deleted a cookie',
|
||||
deleteCookies: 'Deleted all cookies',
|
||||
|
||||
// Alerts
|
||||
getAlertText: 'Read an alert',
|
||||
setAlertText: 'Typed into an alert',
|
||||
postAcceptAlert: 'Accepted an alert',
|
||||
postDismissAlert: 'Dismissed an alert',
|
||||
|
||||
// Keyboard and input methods
|
||||
hideKeyboard: 'Hid the keyboard',
|
||||
isKeyboardShown: 'Checked if the keyboard was shown',
|
||||
activateIMEEngine: 'Activated an input method',
|
||||
deactivateIMEEngine: 'Deactivated the input method',
|
||||
getActiveIMEEngine: 'Read the active input method',
|
||||
availableIMEEngines: 'Listed input methods',
|
||||
isIMEActivated: 'Checked if an input method was active',
|
||||
|
||||
// Clipboard (driver-level, not declared by base-driver)
|
||||
getClipboard: 'Read the clipboard',
|
||||
setClipboard: 'Wrote to the clipboard',
|
||||
|
||||
// Files
|
||||
pushFile: 'Pushed a file to the device',
|
||||
pullFile: 'Pulled a file from the device',
|
||||
pullFolder: 'Pulled a folder from the device',
|
||||
|
||||
// Logs
|
||||
getLog: 'Read device logs',
|
||||
getLogTypes: 'Listed log types',
|
||||
getLogEvents: 'Read logged events',
|
||||
logCustomEvent: 'Logged a custom event',
|
||||
generateTestReport: 'Generated a test report',
|
||||
|
||||
// Scripts
|
||||
execute: 'Ran a script command',
|
||||
executeAsync: 'Ran an async script command',
|
||||
executeCdp: 'Ran a Chrome DevTools command',
|
||||
|
||||
// Web authentication (virtual authenticators)
|
||||
addVirtualAuthenticator: 'Added a virtual authenticator',
|
||||
removeVirtualAuthenticator: 'Removed a virtual authenticator',
|
||||
addAuthCredential: 'Added an auth credential',
|
||||
getAuthCredential: 'Read auth credentials',
|
||||
removeAuthCredential: 'Removed an auth credential',
|
||||
removeAllAuthCredentials: 'Removed all auth credentials',
|
||||
setUserAuthVerified: 'Set the user verification state',
|
||||
|
||||
// Federated sign-in (FedCM)
|
||||
fedCMGetAccounts: 'Listed federated sign-in accounts',
|
||||
fedCMSelectAccount: 'Selected a federated sign-in account',
|
||||
fedCMGetDialogType: 'Read the federated sign-in dialog type',
|
||||
fedCMGetTitle: 'Read the federated sign-in dialog title',
|
||||
fedCMClickDialogButton: 'Clicked a federated sign-in dialog button',
|
||||
fedCMCancelDialog: 'Canceled the federated sign-in dialog',
|
||||
fedCMResetCooldown: 'Reset the federated sign-in cooldown',
|
||||
fedCMSetDelayEnabled: 'Toggled the federated sign-in delay',
|
||||
|
||||
// Virtual sensors and pressure sources
|
||||
createVirtualSensor: 'Created a virtual sensor',
|
||||
updateVirtualSensorReading: 'Updated a virtual sensor reading',
|
||||
getVirtualSensorInfo: 'Read virtual sensor info',
|
||||
deleteVirtualSensor: 'Removed a virtual sensor',
|
||||
createVirtualPressureSource: 'Created a virtual pressure source',
|
||||
updateVirtualPressureSource: 'Updated a virtual pressure source',
|
||||
deleteVirtualPressureSource: 'Removed a virtual pressure source',
|
||||
|
||||
// Privacy / experimental web platform
|
||||
getGlobalPrivacyControl: 'Read the Global Privacy Control setting',
|
||||
setGlobalPrivacyControl: 'Set the Global Privacy Control setting',
|
||||
setStorageAccess: 'Set storage access',
|
||||
setRPHRegistrationMode: 'Set the protocol handler registration mode',
|
||||
setSPCTransactionMode: 'Set the payment transaction mode',
|
||||
|
||||
// Driver routes and compatibility commands
|
||||
asyncScriptTimeout: 'Set the async script timeout',
|
||||
closeApp: 'Closed the app',
|
||||
disableConditionInducer: 'Disabled the condition inducer',
|
||||
enableConditionInducer: 'Enabled the condition inducer',
|
||||
fingerprint: 'Simulated a fingerprint',
|
||||
getCurrentActivity: 'Read the current activity',
|
||||
getCurrentPackage: 'Read the current package',
|
||||
getDisplayDensity: 'Read the display density',
|
||||
getLocation: 'Read an element location',
|
||||
getLocationInView: 'Read an element location in view',
|
||||
getPerformanceData: 'Read performance data',
|
||||
getPerformanceDataTypes: 'Listed performance data types',
|
||||
getScreenInfo: 'Read screen information',
|
||||
getSize: 'Read an element size',
|
||||
getStrings: 'Read app strings',
|
||||
getSystemBars: 'Read system bar information',
|
||||
getViewportRect: 'Read the viewport size',
|
||||
getViewportScreenshot: 'Took a viewport screenshot',
|
||||
getWindowSize: 'Read the window size',
|
||||
gsmCall: 'Simulated a phone call',
|
||||
gsmSignal: 'Changed the cellular signal',
|
||||
gsmVoice: 'Changed the cellular voice state',
|
||||
implicitWait: 'Set the element lookup timeout',
|
||||
isLocationServicesEnabled: 'Checked if location services were enabled',
|
||||
isLocked: 'Checked if the device was locked',
|
||||
keyevent: 'Sent a key event',
|
||||
keys: 'Sent keyboard input',
|
||||
launchApp: 'Launched the app',
|
||||
listConditionInducers: 'Listed condition inducers',
|
||||
lock: 'Locked the device',
|
||||
longPressKeyCode: 'Long-pressed a key',
|
||||
networkSpeed: 'Changed the network speed',
|
||||
openNotifications: 'Opened notifications',
|
||||
powerAC: 'Changed the charging state',
|
||||
powerCapacity: 'Changed the battery level',
|
||||
pressKeyCode: 'Pressed a key',
|
||||
replaceValue: 'Replaced an element value',
|
||||
reset: 'Reset the app',
|
||||
sendSMS: 'Simulated a text message',
|
||||
sensorSet: 'Changed a sensor value',
|
||||
setStylusHandwriting: 'Changed stylus handwriting mode',
|
||||
setValueImmediate: 'Typed into an element immediately',
|
||||
startActivity: 'Started an activity',
|
||||
startAudioRecording: 'Started audio recording',
|
||||
startRecordingScreen: 'Started screen recording',
|
||||
stopAudioRecording: 'Stopped audio recording',
|
||||
stopRecordingScreen: 'Stopped screen recording',
|
||||
submit: 'Submitted an element',
|
||||
toggleData: 'Toggled mobile data',
|
||||
toggleEnrollTouchId: 'Changed Touch ID enrollment',
|
||||
toggleFlightMode: 'Toggled airplane mode',
|
||||
toggleLocationServices: 'Toggled location services',
|
||||
toggleWiFi: 'Toggled Wi-Fi',
|
||||
touchId: 'Simulated Touch ID',
|
||||
unlock: 'Unlocked the device',
|
||||
|
||||
// Driver mobile commands
|
||||
mobileAcceptAlert: 'Accepted an alert',
|
||||
mobileActivateApp: 'Activated an app',
|
||||
mobileBackgroundApp: 'Sent the app to the background',
|
||||
mobileBluetooth: 'Changed Bluetooth state',
|
||||
mobileBroadcast: 'Sent an Android broadcast',
|
||||
mobileCalibrateWebToRealCoordinatesTranslation: 'Calibrated web coordinates',
|
||||
mobileChangePermissions: 'Changed app permissions',
|
||||
mobileClearApp: 'Cleared app data',
|
||||
mobileClearKeychains: 'Cleared keychains',
|
||||
mobileClickGesture: 'Performed a click gesture',
|
||||
mobileConfigureLocalization: 'Changed device localization',
|
||||
mobileDeepLink: 'Opened a deep link',
|
||||
mobileDeleteFile: 'Deleted a device file',
|
||||
mobileDeleteFolder: 'Deleted a device folder',
|
||||
mobileDeviceidle: 'Changed device idle state',
|
||||
mobileDisableVoiceOver: 'Disabled VoiceOver',
|
||||
mobileDismissAlert: 'Dismissed an alert',
|
||||
mobileDoubleClickGesture: 'Performed a double-click gesture',
|
||||
mobileDoubleTap: 'Double-tapped the screen',
|
||||
mobileDragFromToForDuration: 'Dragged between screen points',
|
||||
mobileDragFromToWithVelocity: 'Dragged with a set velocity',
|
||||
mobileDragGesture: 'Performed a drag gesture',
|
||||
mobileEnableVoiceOver: 'Enabled VoiceOver',
|
||||
mobileEnrollBiometric: 'Changed biometric enrollment',
|
||||
mobileExecEmuConsoleCommand: 'Ran an emulator console command',
|
||||
mobileExpectNotification: 'Waited for a notification',
|
||||
mobileFingerprint: 'Simulated a fingerprint',
|
||||
mobileFlingGesture: 'Performed a fling gesture',
|
||||
mobileForcePress: 'Force-pressed the screen',
|
||||
mobileGetActionHistory: 'Read scheduled action history',
|
||||
mobileGetActiveAppInfo: 'Read active app information',
|
||||
mobileGetAppearance: 'Read the device appearance',
|
||||
mobileGetBatteryInfo: 'Read battery information',
|
||||
mobileGetChromeCapabilities: 'Read Chrome capabilities',
|
||||
mobileGetConnectivity: 'Read device connectivity',
|
||||
mobileGetContentSize: 'Read scrollable content size',
|
||||
mobileGetContexts: 'Listed available contexts',
|
||||
mobileGetDeviceInfo: 'Read device information',
|
||||
mobileGetDeviceTime: 'Read the device time',
|
||||
mobileGetDeclaredOrientation: 'Read the declared device orientation',
|
||||
mobileGetGeolocation: 'Read the device location',
|
||||
mobileGetIncreaseContrast: 'Read the Increase Contrast setting',
|
||||
mobileGetNotifications: 'Read notifications',
|
||||
mobileGetPasteboard: 'Read the pasteboard',
|
||||
mobileGetPerformanceData: 'Read app performance data',
|
||||
mobileGetPermission: 'Read an app permission',
|
||||
mobileGetPermissions: 'Read app permissions',
|
||||
mobileGetSimulatedLocation: 'Read the simulated location',
|
||||
mobileGetSource: 'Read the screen contents',
|
||||
mobileGetUiMode: 'Read the Android UI mode',
|
||||
mobileGetXctestScreenRecordingInfo: 'Read XCTest screen recording information',
|
||||
mobileGsmCall: 'Simulated a phone call',
|
||||
mobileGsmSignal: 'Changed the cellular signal',
|
||||
mobileGsmVoice: 'Changed the cellular voice state',
|
||||
mobileHandleAlert: 'Handled an alert',
|
||||
mobileHideKeyboard: 'Hid the keyboard',
|
||||
mobileInjectEmulatorCameraImage: 'Injected an emulator camera image',
|
||||
mobileInstallApp: 'Installed an app',
|
||||
mobileInstallCertificate: 'Installed a certificate',
|
||||
mobileInstallMultipleApks: 'Installed multiple APKs',
|
||||
mobileInstallXCTestBundle: 'Installed an XCTest bundle',
|
||||
mobileIsAppInstalled: 'Checked if an app was installed',
|
||||
mobileIsBiometricEnrolled: 'Checked biometric enrollment',
|
||||
mobileIsMediaProjectionRecordingRunning: 'Checked media projection recording',
|
||||
mobileIsVoiceOverEnabled: 'Checked if VoiceOver was enabled',
|
||||
mobileKeys: 'Sent keyboard input',
|
||||
mobileKillApp: 'Stopped an app',
|
||||
mobileLaunchApp: 'Launched an app',
|
||||
mobileListApps: 'Listed installed apps',
|
||||
mobileListCertificates: 'Listed certificates',
|
||||
mobileListDisplays: 'Listed displays',
|
||||
mobileListSms: 'Listed text messages',
|
||||
mobileListWindows: 'Listed app windows',
|
||||
mobileListXCTestBundles: 'Listed XCTest bundles',
|
||||
mobileLongClickGesture: 'Performed a long-click gesture',
|
||||
mobileNetworkSpeed: 'Changed the network speed',
|
||||
mobileNfc: 'Changed NFC state',
|
||||
mobilePerformAccessibilityAudit: 'Ran an accessibility audit',
|
||||
mobilePerformEditorAction: 'Performed a keyboard editor action',
|
||||
mobilePerformHandGesture: 'Performed a hand gesture',
|
||||
mobilePerformIndigoHidEvent: 'Performed an Indigo HID event',
|
||||
mobilePerformIoHidEvent: 'Performed an IOHID event',
|
||||
mobilePerformStatusBarCommand: 'Performed a status bar command',
|
||||
mobilePinch: 'Pinched the screen',
|
||||
mobilePinchCloseGesture: 'Performed a pinch-close gesture',
|
||||
mobilePinchOpenGesture: 'Performed a pinch-open gesture',
|
||||
mobilePowerAc: 'Changed the charging state',
|
||||
mobilePowerCapacity: 'Changed the battery level',
|
||||
mobilePressButton: 'Pressed a device button',
|
||||
mobilePressKey: 'Pressed a key',
|
||||
mobilePullFile: 'Pulled a file from the device',
|
||||
mobilePullFolder: 'Pulled a folder from the device',
|
||||
mobilePushFile: 'Pushed a file to the device',
|
||||
mobilePushNotification: 'Sent a push notification',
|
||||
mobileQueryAppState: 'Read the app state',
|
||||
mobileRefreshGpsCache: 'Refreshed the GPS cache',
|
||||
mobileRemoveApp: 'Removed an app',
|
||||
mobileRemoveCertificate: 'Removed a certificate',
|
||||
mobileReplaceElementValue: 'Replaced an element value',
|
||||
mobileResetAccessibilityCache: 'Reset the accessibility cache',
|
||||
mobileResetGeolocation: 'Reset the device location',
|
||||
mobileResetLocationService: 'Reset location services',
|
||||
mobileResetPermission: 'Reset an app permission',
|
||||
mobileResetSimulatedLocation: 'Reset the simulated location',
|
||||
mobileRotateDigitalCrown: 'Rotated the Digital Crown',
|
||||
mobileRotateElement: 'Rotated an element',
|
||||
mobileRunXCTest: 'Ran an XCTest bundle',
|
||||
mobileScheduleAction: 'Scheduled a device action',
|
||||
mobileScreenshots: 'Took display screenshots',
|
||||
mobileScroll: 'Scrolled the screen',
|
||||
mobileScrollBackTo: 'Scrolled back to an element',
|
||||
mobileScrollGesture: 'Performed a scroll gesture',
|
||||
mobileScrollToElement: 'Scrolled to an element',
|
||||
mobileSelectPickerWheelValue: 'Changed a picker wheel value',
|
||||
mobileSendBiometricMatch: 'Simulated biometric authentication',
|
||||
mobileSendMemoryWarning: 'Sent a memory warning',
|
||||
mobileSendSms: 'Simulated a text message',
|
||||
mobileSendTrimMemory: 'Sent an Android memory trim event',
|
||||
mobileSetAppearance: 'Changed the device appearance',
|
||||
mobileSetConnectivity: 'Changed device connectivity',
|
||||
mobileSetContentSize: 'Changed the content size',
|
||||
mobileSetGeolocation: 'Set the device location',
|
||||
mobileSetIncreaseContrast: 'Changed the Increase Contrast setting',
|
||||
mobileSetPasteboard: 'Wrote to the pasteboard',
|
||||
mobileSetPermissions: 'Set app permissions',
|
||||
mobileSetSimulatedLocation: 'Set a simulated location',
|
||||
mobileSetUiMode: 'Changed the Android UI mode',
|
||||
mobileShake: 'Shook the device',
|
||||
mobileShell: 'Ran an Android shell command',
|
||||
mobileSimctl: 'Ran a simctl command',
|
||||
mobileSiriCommand: 'Sent a Siri command',
|
||||
mobileStartActivity: 'Started an activity',
|
||||
mobileStartLogsBroadcast: 'Started log streaming',
|
||||
mobileStartMediaProjectionRecording: 'Started media projection recording',
|
||||
mobileStartNetworkMonitor: 'Started network monitoring',
|
||||
mobileStartPerfRecord: 'Started performance recording',
|
||||
mobileStartScreenRecording: 'Started screen recording',
|
||||
mobileStartScreenStreaming: 'Started screen streaming',
|
||||
mobileStartService: 'Started a service',
|
||||
mobileStartSystemMonitor: 'Started system monitoring',
|
||||
mobileStartXctestScreenRecording: 'Started XCTest screen recording',
|
||||
mobileStopLogsBroadcast: 'Stopped log streaming',
|
||||
mobileStopMediaProjectionRecording: 'Stopped media projection recording',
|
||||
mobileStopNetworkMonitor: 'Stopped network monitoring',
|
||||
mobileStopPerfRecord: 'Stopped performance recording',
|
||||
mobileStopScreenRecording: 'Stopped screen recording',
|
||||
mobileStopScreenStreaming: 'Stopped screen streaming',
|
||||
mobileStopService: 'Stopped a service',
|
||||
mobileStopSystemMonitor: 'Stopped system monitoring',
|
||||
mobileStopXctestScreenRecording: 'Stopped XCTest screen recording',
|
||||
mobileSwipe: 'Swiped the screen',
|
||||
mobileSwipeGesture: 'Performed a swipe gesture',
|
||||
mobileTap: 'Tapped the screen',
|
||||
mobileTapWithNumberOfTaps: 'Tapped the screen multiple times',
|
||||
mobileTerminateApp: 'Terminated an app',
|
||||
mobileTouchAndHold: 'Touched and held the screen',
|
||||
mobileTwoFingerTap: 'Tapped with two fingers',
|
||||
mobileType: 'Typed text',
|
||||
mobileUnlock: 'Unlocked the device',
|
||||
mobileUnscheduleAction: 'Canceled a scheduled action',
|
||||
mobileUpdateSafariPreferences: 'Updated Safari preferences',
|
||||
mobileViewPortRect: 'Read the viewport size',
|
||||
mobileViewportScreenshot: 'Took a viewport screenshot',
|
||||
mobileVoiceOverCurrentSpeech: 'Read current VoiceOver speech',
|
||||
mobileVoiceOverMove: 'Moved the VoiceOver cursor',
|
||||
};
|
||||
|
||||
/**
|
||||
* Translate a raw Appium command name into a short, human-readable summary.
|
||||
*
|
||||
* If a command has no curated summary, the raw command name is returned
|
||||
* unchanged — we intentionally do not guess a phrasing.
|
||||
*/
|
||||
export function humanizeAppiumCommand(command: string): string {
|
||||
return Object.hasOwn(APPIUM_COMMAND_SUMMARIES, command)
|
||||
? (APPIUM_COMMAND_SUMMARIES as Record<string, string>)[command]
|
||||
: command;
|
||||
}
|
||||
@@ -0,0 +1,401 @@
|
||||
// AUTO-GENERATED. Do not edit by hand.
|
||||
// Run `yarn workspace @expo/build-tools generate-appium-commands` to refresh this
|
||||
// list from Appium, XCUITest, and UiAutomator2 (installed on demand by the generator).
|
||||
export type BaseDriverAppiumCommand =
|
||||
| 'activateApp'
|
||||
| 'activateIMEEngine'
|
||||
| 'active'
|
||||
| 'addAuthCredential'
|
||||
| 'addVirtualAuthenticator'
|
||||
| 'availableIMEEngines'
|
||||
| 'back'
|
||||
| 'clear'
|
||||
| 'clearDevicePosture'
|
||||
| 'click'
|
||||
| 'closeWindow'
|
||||
| 'createNewWindow'
|
||||
| 'createSession'
|
||||
| 'createVirtualPressureSource'
|
||||
| 'createVirtualSensor'
|
||||
| 'deactivateIMEEngine'
|
||||
| 'deleteCookie'
|
||||
| 'deleteCookies'
|
||||
| 'deleteSession'
|
||||
| 'deleteVirtualPressureSource'
|
||||
| 'deleteVirtualSensor'
|
||||
| 'elementDisplayed'
|
||||
| 'elementEnabled'
|
||||
| 'elementSelected'
|
||||
| 'elementShadowRoot'
|
||||
| 'execute'
|
||||
| 'executeAsync'
|
||||
| 'executeCdp'
|
||||
| 'fedCMCancelDialog'
|
||||
| 'fedCMClickDialogButton'
|
||||
| 'fedCMGetAccounts'
|
||||
| 'fedCMGetDialogType'
|
||||
| 'fedCMGetTitle'
|
||||
| 'fedCMResetCooldown'
|
||||
| 'fedCMSelectAccount'
|
||||
| 'fedCMSetDelayEnabled'
|
||||
| 'findElement'
|
||||
| 'findElementFromElement'
|
||||
| 'findElementFromShadowRoot'
|
||||
| 'findElements'
|
||||
| 'findElementsFromElement'
|
||||
| 'findElementsFromShadowRoot'
|
||||
| 'forward'
|
||||
| 'fullScreenWindow'
|
||||
| 'generateTestReport'
|
||||
| 'getActiveIMEEngine'
|
||||
| 'getAlertText'
|
||||
| 'getAppiumSessionCapabilities'
|
||||
| 'getAppiumSessions'
|
||||
| 'getAttribute'
|
||||
| 'getAuthCredential'
|
||||
| 'getComputedLabel'
|
||||
| 'getComputedRole'
|
||||
| 'getContexts'
|
||||
| 'getCookie'
|
||||
| 'getCookies'
|
||||
| 'getCssProperty'
|
||||
| 'getCurrentContext'
|
||||
| 'getDeviceTime'
|
||||
| 'getElementRect'
|
||||
| 'getElementScreenshot'
|
||||
| 'getGeoLocation'
|
||||
| 'getGlobalPrivacyControl'
|
||||
| 'getLog'
|
||||
| 'getLogEvents'
|
||||
| 'getLogTypes'
|
||||
| 'getName'
|
||||
| 'getNetworkConnection'
|
||||
| 'getOrientation'
|
||||
| 'getPageSource'
|
||||
| 'getProperty'
|
||||
| 'getRotation'
|
||||
| 'getScreenshot'
|
||||
| 'getSession'
|
||||
| 'getSettings'
|
||||
| 'getStatus'
|
||||
| 'getText'
|
||||
| 'getTimeouts'
|
||||
| 'getUrl'
|
||||
| 'getVirtualSensorInfo'
|
||||
| 'getWindowHandle'
|
||||
| 'getWindowHandles'
|
||||
| 'getWindowRect'
|
||||
| 'hideKeyboard'
|
||||
| 'installApp'
|
||||
| 'isAppInstalled'
|
||||
| 'isIMEActivated'
|
||||
| 'isKeyboardShown'
|
||||
| 'listCommands'
|
||||
| 'listExtensions'
|
||||
| 'logCustomEvent'
|
||||
| 'maximizeWindow'
|
||||
| 'minimizeWindow'
|
||||
| 'performActions'
|
||||
| 'postAcceptAlert'
|
||||
| 'postDismissAlert'
|
||||
| 'printPage'
|
||||
| 'pullFile'
|
||||
| 'pullFolder'
|
||||
| 'pushFile'
|
||||
| 'queryAppState'
|
||||
| 'receiveAsyncResponse'
|
||||
| 'refresh'
|
||||
| 'releaseActions'
|
||||
| 'removeAllAuthCredentials'
|
||||
| 'removeApp'
|
||||
| 'removeAuthCredential'
|
||||
| 'removeVirtualAuthenticator'
|
||||
| 'setAlertText'
|
||||
| 'setContext'
|
||||
| 'setCookie'
|
||||
| 'setDevicePosture'
|
||||
| 'setFrame'
|
||||
| 'setGeoLocation'
|
||||
| 'setGlobalPrivacyControl'
|
||||
| 'setNetworkConnection'
|
||||
| 'setOrientation'
|
||||
| 'setPermissions'
|
||||
| 'setRPHRegistrationMode'
|
||||
| 'setRotation'
|
||||
| 'setSPCTransactionMode'
|
||||
| 'setStorageAccess'
|
||||
| 'setUrl'
|
||||
| 'setUserAuthVerified'
|
||||
| 'setValue'
|
||||
| 'setWindow'
|
||||
| 'setWindowRect'
|
||||
| 'switchToParentFrame'
|
||||
| 'terminateApp'
|
||||
| 'timeouts'
|
||||
| 'title'
|
||||
| 'updateSettings'
|
||||
| 'updateVirtualPressureSource'
|
||||
| 'updateVirtualSensorReading';
|
||||
|
||||
export type XCUITestAppiumCommand =
|
||||
| 'asyncScriptTimeout'
|
||||
| 'background'
|
||||
| 'closeApp'
|
||||
| 'disableConditionInducer'
|
||||
| 'enableConditionInducer'
|
||||
| 'getClipboard'
|
||||
| 'getLocation'
|
||||
| 'getLocationInView'
|
||||
| 'getScreenInfo'
|
||||
| 'getSize'
|
||||
| 'getStrings'
|
||||
| 'getViewportRect'
|
||||
| 'getViewportScreenshot'
|
||||
| 'getWindowSize'
|
||||
| 'implicitWait'
|
||||
| 'isLocked'
|
||||
| 'keys'
|
||||
| 'launchApp'
|
||||
| 'listConditionInducers'
|
||||
| 'lock'
|
||||
| 'mobileActivateApp'
|
||||
| 'mobileCalibrateWebToRealCoordinatesTranslation'
|
||||
| 'mobileClearApp'
|
||||
| 'mobileClearKeychains'
|
||||
| 'mobileConfigureLocalization'
|
||||
| 'mobileDeepLink'
|
||||
| 'mobileDeleteFile'
|
||||
| 'mobileDeleteFolder'
|
||||
| 'mobileDisableVoiceOver'
|
||||
| 'mobileDoubleTap'
|
||||
| 'mobileDragFromToForDuration'
|
||||
| 'mobileDragFromToWithVelocity'
|
||||
| 'mobileEnableVoiceOver'
|
||||
| 'mobileEnrollBiometric'
|
||||
| 'mobileExpectNotification'
|
||||
| 'mobileForcePress'
|
||||
| 'mobileGetActiveAppInfo'
|
||||
| 'mobileGetAppearance'
|
||||
| 'mobileGetBatteryInfo'
|
||||
| 'mobileGetContentSize'
|
||||
| 'mobileGetContexts'
|
||||
| 'mobileGetDeviceInfo'
|
||||
| 'mobileGetDeviceTime'
|
||||
| 'mobileGetIncreaseContrast'
|
||||
| 'mobileGetPasteboard'
|
||||
| 'mobileGetPermission'
|
||||
| 'mobileGetSimulatedLocation'
|
||||
| 'mobileGetSource'
|
||||
| 'mobileGetXctestScreenRecordingInfo'
|
||||
| 'mobileHandleAlert'
|
||||
| 'mobileHideKeyboard'
|
||||
| 'mobileInstallApp'
|
||||
| 'mobileInstallCertificate'
|
||||
| 'mobileInstallXCTestBundle'
|
||||
| 'mobileIsAppInstalled'
|
||||
| 'mobileIsBiometricEnrolled'
|
||||
| 'mobileIsVoiceOverEnabled'
|
||||
| 'mobileKeys'
|
||||
| 'mobileKillApp'
|
||||
| 'mobileLaunchApp'
|
||||
| 'mobileListApps'
|
||||
| 'mobileListCertificates'
|
||||
| 'mobileListXCTestBundles'
|
||||
| 'mobilePerformAccessibilityAudit'
|
||||
| 'mobilePerformHandGesture'
|
||||
| 'mobilePerformIndigoHidEvent'
|
||||
| 'mobilePerformIoHidEvent'
|
||||
| 'mobilePinch'
|
||||
| 'mobilePressButton'
|
||||
| 'mobilePullFile'
|
||||
| 'mobilePullFolder'
|
||||
| 'mobilePushFile'
|
||||
| 'mobilePushNotification'
|
||||
| 'mobileQueryAppState'
|
||||
| 'mobileRemoveApp'
|
||||
| 'mobileRemoveCertificate'
|
||||
| 'mobileResetLocationService'
|
||||
| 'mobileResetPermission'
|
||||
| 'mobileResetSimulatedLocation'
|
||||
| 'mobileRotateDigitalCrown'
|
||||
| 'mobileRotateElement'
|
||||
| 'mobileRunXCTest'
|
||||
| 'mobileScroll'
|
||||
| 'mobileScrollToElement'
|
||||
| 'mobileSelectPickerWheelValue'
|
||||
| 'mobileSendBiometricMatch'
|
||||
| 'mobileSendMemoryWarning'
|
||||
| 'mobileSetAppearance'
|
||||
| 'mobileSetContentSize'
|
||||
| 'mobileSetIncreaseContrast'
|
||||
| 'mobileSetPasteboard'
|
||||
| 'mobileSetPermissions'
|
||||
| 'mobileSetSimulatedLocation'
|
||||
| 'mobileShake'
|
||||
| 'mobileSimctl'
|
||||
| 'mobileSiriCommand'
|
||||
| 'mobileStartLogsBroadcast'
|
||||
| 'mobileStartNetworkMonitor'
|
||||
| 'mobileStartPerfRecord'
|
||||
| 'mobileStartScreenRecording'
|
||||
| 'mobileStartSystemMonitor'
|
||||
| 'mobileStartXctestScreenRecording'
|
||||
| 'mobileStopLogsBroadcast'
|
||||
| 'mobileStopNetworkMonitor'
|
||||
| 'mobileStopPerfRecord'
|
||||
| 'mobileStopScreenRecording'
|
||||
| 'mobileStopSystemMonitor'
|
||||
| 'mobileStopXctestScreenRecording'
|
||||
| 'mobileSwipe'
|
||||
| 'mobileTap'
|
||||
| 'mobileTapWithNumberOfTaps'
|
||||
| 'mobileTerminateApp'
|
||||
| 'mobileTouchAndHold'
|
||||
| 'mobileTwoFingerTap'
|
||||
| 'mobileUpdateSafariPreferences'
|
||||
| 'mobileVoiceOverCurrentSpeech'
|
||||
| 'mobileVoiceOverMove'
|
||||
| 'reset'
|
||||
| 'setClipboard'
|
||||
| 'setValueImmediate'
|
||||
| 'startAudioRecording'
|
||||
| 'startRecordingScreen'
|
||||
| 'stopAudioRecording'
|
||||
| 'stopRecordingScreen'
|
||||
| 'submit'
|
||||
| 'toggleEnrollTouchId'
|
||||
| 'touchId'
|
||||
| 'unlock';
|
||||
|
||||
export type UiAutomator2AppiumCommand =
|
||||
| 'background'
|
||||
| 'fingerprint'
|
||||
| 'getClipboard'
|
||||
| 'getCurrentActivity'
|
||||
| 'getCurrentPackage'
|
||||
| 'getDisplayDensity'
|
||||
| 'getLocation'
|
||||
| 'getLocationInView'
|
||||
| 'getPerformanceData'
|
||||
| 'getPerformanceDataTypes'
|
||||
| 'getSize'
|
||||
| 'getStrings'
|
||||
| 'getSystemBars'
|
||||
| 'getWindowSize'
|
||||
| 'gsmCall'
|
||||
| 'gsmSignal'
|
||||
| 'gsmVoice'
|
||||
| 'implicitWait'
|
||||
| 'isLocationServicesEnabled'
|
||||
| 'isLocked'
|
||||
| 'keyevent'
|
||||
| 'keys'
|
||||
| 'lock'
|
||||
| 'longPressKeyCode'
|
||||
| 'mobileAcceptAlert'
|
||||
| 'mobileBackgroundApp'
|
||||
| 'mobileBluetooth'
|
||||
| 'mobileBroadcast'
|
||||
| 'mobileChangePermissions'
|
||||
| 'mobileClearApp'
|
||||
| 'mobileClickGesture'
|
||||
| 'mobileDeepLink'
|
||||
| 'mobileDeleteFile'
|
||||
| 'mobileDeviceidle'
|
||||
| 'mobileDismissAlert'
|
||||
| 'mobileDoubleClickGesture'
|
||||
| 'mobileDragGesture'
|
||||
| 'mobileExecEmuConsoleCommand'
|
||||
| 'mobileFingerprint'
|
||||
| 'mobileFlingGesture'
|
||||
| 'mobileGetActionHistory'
|
||||
| 'mobileGetBatteryInfo'
|
||||
| 'mobileGetChromeCapabilities'
|
||||
| 'mobileGetConnectivity'
|
||||
| 'mobileGetContexts'
|
||||
| 'mobileGetDeclaredOrientation'
|
||||
| 'mobileGetDeviceInfo'
|
||||
| 'mobileGetDeviceTime'
|
||||
| 'mobileGetGeolocation'
|
||||
| 'mobileGetNotifications'
|
||||
| 'mobileGetPerformanceData'
|
||||
| 'mobileGetPermissions'
|
||||
| 'mobileGetUiMode'
|
||||
| 'mobileGsmCall'
|
||||
| 'mobileGsmSignal'
|
||||
| 'mobileGsmVoice'
|
||||
| 'mobileInjectEmulatorCameraImage'
|
||||
| 'mobileInstallApp'
|
||||
| 'mobileInstallMultipleApks'
|
||||
| 'mobileIsAppInstalled'
|
||||
| 'mobileIsMediaProjectionRecordingRunning'
|
||||
| 'mobileListApps'
|
||||
| 'mobileListDisplays'
|
||||
| 'mobileListSms'
|
||||
| 'mobileListWindows'
|
||||
| 'mobileLongClickGesture'
|
||||
| 'mobileNetworkSpeed'
|
||||
| 'mobileNfc'
|
||||
| 'mobilePerformEditorAction'
|
||||
| 'mobilePerformStatusBarCommand'
|
||||
| 'mobilePinchCloseGesture'
|
||||
| 'mobilePinchOpenGesture'
|
||||
| 'mobilePowerAc'
|
||||
| 'mobilePowerCapacity'
|
||||
| 'mobilePressKey'
|
||||
| 'mobileRefreshGpsCache'
|
||||
| 'mobileRemoveApp'
|
||||
| 'mobileReplaceElementValue'
|
||||
| 'mobileResetAccessibilityCache'
|
||||
| 'mobileResetGeolocation'
|
||||
| 'mobileScheduleAction'
|
||||
| 'mobileScreenshots'
|
||||
| 'mobileScroll'
|
||||
| 'mobileScrollBackTo'
|
||||
| 'mobileScrollGesture'
|
||||
| 'mobileSendSms'
|
||||
| 'mobileSendTrimMemory'
|
||||
| 'mobileSetConnectivity'
|
||||
| 'mobileSetGeolocation'
|
||||
| 'mobileSetUiMode'
|
||||
| 'mobileShell'
|
||||
| 'mobileStartActivity'
|
||||
| 'mobileStartLogsBroadcast'
|
||||
| 'mobileStartMediaProjectionRecording'
|
||||
| 'mobileStartScreenStreaming'
|
||||
| 'mobileStartService'
|
||||
| 'mobileStopLogsBroadcast'
|
||||
| 'mobileStopMediaProjectionRecording'
|
||||
| 'mobileStopScreenStreaming'
|
||||
| 'mobileStopService'
|
||||
| 'mobileSwipeGesture'
|
||||
| 'mobileTerminateApp'
|
||||
| 'mobileType'
|
||||
| 'mobileUnlock'
|
||||
| 'mobileUnscheduleAction'
|
||||
| 'mobileViewPortRect'
|
||||
| 'mobileViewportScreenshot'
|
||||
| 'networkSpeed'
|
||||
| 'openNotifications'
|
||||
| 'powerAC'
|
||||
| 'powerCapacity'
|
||||
| 'pressKeyCode'
|
||||
| 'replaceValue'
|
||||
| 'sendSMS'
|
||||
| 'sensorSet'
|
||||
| 'setClipboard'
|
||||
| 'setStylusHandwriting'
|
||||
| 'setValueImmediate'
|
||||
| 'startActivity'
|
||||
| 'startRecordingScreen'
|
||||
| 'stopRecordingScreen'
|
||||
| 'toggleData'
|
||||
| 'toggleFlightMode'
|
||||
| 'toggleLocationServices'
|
||||
| 'toggleWiFi'
|
||||
| 'unlock';
|
||||
|
||||
export type AppiumCommand =
|
||||
| BaseDriverAppiumCommand
|
||||
| XCUITestAppiumCommand
|
||||
| UiAutomator2AppiumCommand;
|
||||
@@ -7,6 +7,7 @@ import { z } from 'zod';
|
||||
|
||||
import { type CustomBuildContext } from '../../customBuildContext';
|
||||
import { turtleFetch } from '../../utils/turtleFetch';
|
||||
import { humanizeAppiumCommand } from './appiumCommandSummary';
|
||||
import {
|
||||
type DeviceRunSessionEventCollection,
|
||||
type DeviceRunSessionEventSource,
|
||||
@@ -146,7 +147,7 @@ function createAppiumEventSource(eventFile: string): DeviceRunSessionEventSource
|
||||
type: 'operation.completed',
|
||||
operationId,
|
||||
durationMs: Math.max(0, command.endTime - command.startTime),
|
||||
summary: command.cmd,
|
||||
summary: humanizeAppiumCommand(command.cmd),
|
||||
data: {
|
||||
command: command.cmd,
|
||||
appiumSessionId: command.appiumSessionId,
|
||||
|
||||
@@ -18,11 +18,11 @@ const ARGENT_ARTIFACT_UPLOAD_POLL_INTERVAL_MS = 5_000;
|
||||
const ARGENT_ARTIFACT_UPLOAD_CLEANUP_TIMEOUT_MS = 30_000;
|
||||
const ARGENT_ARTIFACT_FETCH_TIMEOUT_MS = 10_000;
|
||||
|
||||
// Kinds the EAS dashboard groups device run session artifacts by. Only media types the dashboard
|
||||
// renders specially are mapped; anything else stays unclassified and lands in its "Other" group.
|
||||
// Note that these kinds only affect grouping and labelling. The dashboard gates its inline video
|
||||
// player on the `__eas_screen_recording` metadata flag, which Argent artifacts deliberately do not
|
||||
// set, so a `screen-recording` kind here does not add a player to the session page.
|
||||
// Fallback kinds for Argent versions that do not include a semantic artifact kind. Only media types
|
||||
// the EAS dashboard renders specially can be inferred safely. Note that these kinds only affect
|
||||
// grouping and labelling. The dashboard gates its inline video player on the
|
||||
// `__eas_screen_recording` metadata flag, which Argent artifacts deliberately do not set, so a
|
||||
// `screen-recording` kind here does not add a player to the session page.
|
||||
const ARGENT_ARTIFACT_KIND_BY_MIME_TYPE = new Map<string, string>([
|
||||
['image/png', 'screenshot'],
|
||||
['image/jpeg', 'screenshot'],
|
||||
@@ -32,6 +32,9 @@ const ARGENT_ARTIFACT_KIND_BY_MIME_TYPE = new Map<string, string>([
|
||||
|
||||
const ArgentArtifactSchema = z.object({
|
||||
id: z.string(),
|
||||
// Optional for compatibility with older Argent tool servers. Keep this open to new kinds so a
|
||||
// newer Argent version can add a category without requiring an EAS worker release first.
|
||||
kind: z.string().optional(),
|
||||
filename: z.string(),
|
||||
mimeType: z.string(),
|
||||
isDirectory: z.boolean().optional(),
|
||||
@@ -43,8 +46,12 @@ const ArgentArtifactsListResponseSchema = z.object({
|
||||
type ArgentArtifact = z.infer<typeof ArgentArtifactSchema>;
|
||||
|
||||
function getArgentArtifactKind(artifact: ArgentArtifact): string | undefined {
|
||||
if (artifact.kind) {
|
||||
return artifact.kind;
|
||||
}
|
||||
// Directories are repackaged as a tarball before upload, so the reported media type describes the
|
||||
// contents rather than the file we actually store.
|
||||
// contents rather than the file we actually store. A semantic kind above remains valid because it
|
||||
// describes what the artifact represents, not its transport format.
|
||||
if (artifact.isDirectory) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
+138
-129
File diff suppressed because it is too large.
Load diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "eas-cli",
|
||||
"version": "22.4.0",
|
||||
"version": "22.5.0",
|
||||
"description": "EAS command line tool",
|
||||
"keywords": [
|
||||
"cli",
|
||||
|
||||
@@ -396,6 +396,26 @@ describe(Simulator, () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('passes --max-idle-time-minutes to the createDeviceRunSession mutation', async () => {
|
||||
const { command } = createCommand([
|
||||
'--platform',
|
||||
'ios',
|
||||
'--non-interactive',
|
||||
'--max-idle-time-minutes',
|
||||
'30',
|
||||
]);
|
||||
await command.runAsync();
|
||||
|
||||
expect(mockCreateDeviceRunSessionAsync).toHaveBeenCalledWith(graphqlClient, {
|
||||
appId: 'project-123',
|
||||
name: undefined,
|
||||
packageVersion: undefined,
|
||||
platform: AppPlatform.Ios,
|
||||
type: DeviceRunSessionType.AgentDevice,
|
||||
maxIdleTimeMinutes: 30,
|
||||
});
|
||||
});
|
||||
|
||||
it(`throws when ${EAS_SIMULATOR_SESSION_ID} is already present with --no-force`, async () => {
|
||||
process.env[EAS_SIMULATOR_SESSION_ID] = 'existing-session';
|
||||
|
||||
|
||||
@@ -114,6 +114,11 @@ export default class Simulator extends EasCommand {
|
||||
'Maximum duration of the simulator session in minutes before it is automatically stopped. Only customizable on paid plans. Defaults to a value derived from the job run priority when omitted.',
|
||||
min: 0,
|
||||
}),
|
||||
'max-idle-time-minutes': Flags.integer({
|
||||
description:
|
||||
'Stop the simulator session automatically after this many minutes without session activity. When omitted, the session has no idle timeout and runs until its maximum duration.',
|
||||
min: 0,
|
||||
}),
|
||||
force: Flags.boolean({
|
||||
description:
|
||||
'[default: true] Create a new simulator session even when an existing simulator session is present in the environment.',
|
||||
@@ -229,6 +234,7 @@ export default class Simulator extends EasCommand {
|
||||
...(launchArgs?.length ? { launchArgs } : {}),
|
||||
...(openUrl ? { openUrl } : {}),
|
||||
maxRunTimeMinutes: flags['max-duration-minutes'],
|
||||
maxIdleTimeMinutes: flags['max-idle-time-minutes'],
|
||||
});
|
||||
deviceRunSessionId = session.id;
|
||||
nullthrows(session.turtleJobRun?.id, 'Expected simulator session to start');
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "eas-cli-local-build-plugin",
|
||||
"version": "22.4.0",
|
||||
"version": "22.5.0",
|
||||
"description": "Tool for running EAS compatible builds on a local machine.",
|
||||
"bugs": "https://github.com/expo/eas-cli/issues",
|
||||
"license": "BUSL-1.1",
|
||||
@@ -26,7 +26,7 @@
|
||||
"clean": "rimraf node_modules dist coverage \"*.tsbuildinfo\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@expo/build-tools": "22.4.0",
|
||||
"@expo/build-tools": "22.5.0",
|
||||
"@expo/eas-build-job": "22.0.0",
|
||||
"@expo/logger": "22.0.0",
|
||||
"@expo/spawn-async": "^1.7.2",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@expo/worker",
|
||||
"version": "22.4.0",
|
||||
"version": "22.5.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "yarn watch",
|
||||
@@ -19,7 +19,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/core": "7.24.7",
|
||||
"@expo/build-tools": "22.4.0",
|
||||
"@expo/build-tools": "22.5.0",
|
||||
"@expo/config-plugins": "55.0.7",
|
||||
"@expo/config-types": "55.0.5",
|
||||
"@expo/downloader": "22.0.0",
|
||||
|
||||
@@ -2291,7 +2291,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@expo/build-tools@npm:22.4.0, @expo/build-tools@workspace:packages/build-tools":
|
||||
"@expo/build-tools@npm:22.5.0, @expo/build-tools@workspace:packages/build-tools":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@expo/build-tools@workspace:packages/build-tools"
|
||||
dependencies:
|
||||
@@ -2962,7 +2962,7 @@ __metadata:
|
||||
resolution: "@expo/worker@workspace:packages/worker"
|
||||
dependencies:
|
||||
"@babel/core": "npm:7.24.7"
|
||||
"@expo/build-tools": "npm:22.4.0"
|
||||
"@expo/build-tools": "npm:22.5.0"
|
||||
"@expo/config-plugins": "npm:55.0.7"
|
||||
"@expo/config-types": "npm:55.0.5"
|
||||
"@expo/downloader": "npm:22.0.0"
|
||||
@@ -10300,7 +10300,7 @@ __metadata:
|
||||
version: 0.0.0-use.local
|
||||
resolution: "eas-cli-local-build-plugin@workspace:packages/local-build-plugin"
|
||||
dependencies:
|
||||
"@expo/build-tools": "npm:22.4.0"
|
||||
"@expo/build-tools": "npm:22.5.0"
|
||||
"@expo/eas-build-job": "npm:22.0.0"
|
||||
"@expo/logger": "npm:22.0.0"
|
||||
"@expo/spawn-async": "npm:^1.7.2"
|
||||
|
||||
Reference in new issue
Block a user