4 |
5 | Awesome Capacitor App
6 |
10 |
11 |
12 |
13 |
17 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/docs/index/functions/writeFile.md:
--------------------------------------------------------------------------------
1 | [**@onslip/automation**](../../README.md) • **Docs**
2 |
3 | ***
4 |
5 | [@onslip/automation](../../README.md) / [index](../README.md) / writeFile
6 |
7 | # Function: writeFile()
8 |
9 | > **writeFile**(`path`, `data`, `options`?): `Promise`\<`void`\>
10 |
11 | Asynchronously writes data to a file, replacing the file if it already exists.
12 |
13 | ## Parameters
14 |
15 | • **path**: `PathOrFileDescriptor`
16 |
17 | A path to a file. If a URL is provided, it must use the `file:` protocol.
18 | URL support is _experimental_.
19 | If a file descriptor is provided, the underlying file will _not_ be closed automatically.
20 |
21 | • **data**: `string` \| `ArrayBufferView`
22 |
23 | The data to write. If something other than a Buffer or Uint8Array is provided, the value is coerced to a string.
24 |
25 | • **options?**: `WriteFileOptions`
26 |
27 | Either the encoding for the file, or an object optionally specifying the encoding, file mode, and flag.
28 | If `encoding` is not supplied, the default of `'utf8'` is used.
29 | If `mode` is not supplied, the default of `0o666` is used.
30 | If `mode` is a string, it is parsed as an octal integer.
31 | If `flag` is not supplied, the default of `'w'` is used.
32 |
33 | ## Returns
34 |
35 | `Promise`\<`void`\>
36 |
37 | ## Defined in
38 |
39 | [src/utils.ts:10](https://github.com/Onslip/automation/blob/13befc40996d96bb2935315b372b921212adc8b4/src/utils.ts#L10)
40 |
--------------------------------------------------------------------------------
/docs/index/README.md:
--------------------------------------------------------------------------------
1 | [**@onslip/automation**](../README.md) • **Docs**
2 |
3 | ***
4 |
5 | [@onslip/automation](../README.md) / index
6 |
7 | # index
8 |
9 | ## Index
10 |
11 | ### Classes
12 |
13 | - [AndroidDevice](classes/AndroidDevice.md)
14 | - [Device](classes/Device.md)
15 | - [Locator](classes/Locator.md)
16 | - [Mouse](classes/Mouse.md)
17 | - [Page](classes/Page.md)
18 | - [Touchscreen](classes/Touchscreen.md)
19 | - [iOSDevice](classes/iOSDevice.md)
20 |
21 | ### Interfaces
22 |
23 | - [AndroidLogOptions](interfaces/AndroidLogOptions.md)
24 | - [AutomationContext](interfaces/AutomationContext.md)
25 | - [AutomationOptions](interfaces/AutomationOptions.md)
26 | - [DeviceOptions](interfaces/DeviceOptions.md)
27 | - [LocatorOptions](interfaces/LocatorOptions.md)
28 | - [ReaderOptions](interfaces/ReaderOptions.md)
29 | - [ResolvedContextPath](interfaces/ResolvedContextPath.md)
30 | - [SelectorOptions](interfaces/SelectorOptions.md)
31 | - [StartOptions](interfaces/StartOptions.md)
32 | - [iOSLogOptions](interfaces/iOSLogOptions.md)
33 |
34 | ### Functions
35 |
36 | - [collectLines](functions/collectLines.md)
37 | - [execFile](functions/execFile.md)
38 | - [findWebViewContexts](functions/findWebViewContexts.md)
39 | - [mkdir](functions/mkdir.md)
40 | - [openWebView](functions/openWebView.md)
41 | - [pipeline](functions/pipeline.md)
42 | - [readCommandOutput](functions/readCommandOutput.md)
43 | - [resolveWebViewContext](functions/resolveWebViewContext.md)
44 | - [sleep](functions/sleep.md)
45 | - [throwError](functions/throwError.md)
46 | - [writeFile](functions/writeFile.md)
47 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@onslip/automation",
3 | "version": "1.3.0",
4 | "description": "Remote-control any old Android or iOS WebView in a Playwright-inspired way",
5 | "license": "Apache-2.0",
6 | "author": "Martin Blom ",
7 | "repository": "https://github.com/Onslip/automation.git",
8 | "main": "build/src/index.js",
9 | "types": "build/src/index.d.ts",
10 | "exports": {
11 | ".": {
12 | "types": "./build/src/index.d.ts",
13 | "default": "./build/src/index.js"
14 | },
15 | "./test": {
16 | "types": "./build/src/test.d.ts",
17 | "default": "./build/src/test.js"
18 | },
19 | "./build/src/test": {
20 | "types": "./build/src/test.d.ts",
21 | "default": "./build/src/test.js"
22 | }
23 | },
24 | "files": [
25 | "CHANGELOG.md",
26 | "build/*.js",
27 | "build/src",
28 | "src"
29 | ],
30 | "dependencies": {
31 | "@divine/synchronization": "^1.2.1",
32 | "@types/chrome-remote-interface": "~0.31.14",
33 | "chrome-remote-interface": "~0.33.2",
34 | "jimp": "~0.22.12"
35 | },
36 | "devDependencies": {
37 | "@changesets/cli": "^2.27.7",
38 | "@types/node": "^22.5.0",
39 | "typedoc": "^0.26.6",
40 | "typedoc-plugin-markdown": "^4.2.6",
41 | "typescript": "~5.5.4"
42 | },
43 | "peerDependencies": {
44 | "@playwright/test": "*"
45 | },
46 | "peerDependenciesMeta": {
47 | "@playwright/test": {
48 | "optional": true
49 | }
50 | },
51 | "pnpm": {
52 | "onlyBuiltDependencies": []
53 | },
54 | "engines": {
55 | "node": ">= 12.4"
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/tests/testapp/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
32 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/tests/testapp/ios/App/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - Capacitor (6.1.2):
3 | - CapacitorCordova
4 | - CapacitorCamera (6.0.2):
5 | - Capacitor
6 | - CapacitorCordova (6.1.2)
7 | - CapacitorSplashScreen (6.0.2):
8 | - Capacitor
9 |
10 | DEPENDENCIES:
11 | - "Capacitor (from `../../node_modules/.pnpm/@capacitor+ios@6.1.2_@capacitor+core@6.1.2/node_modules/@capacitor/ios`)"
12 | - "CapacitorCamera (from `../../node_modules/.pnpm/@capacitor+camera@6.0.2_@capacitor+core@6.1.2/node_modules/@capacitor/camera`)"
13 | - "CapacitorCordova (from `../../node_modules/.pnpm/@capacitor+ios@6.1.2_@capacitor+core@6.1.2/node_modules/@capacitor/ios`)"
14 | - "CapacitorSplashScreen (from `../../node_modules/.pnpm/@capacitor+splash-screen@6.0.2_@capacitor+core@6.1.2/node_modules/@capacitor/splash-screen`)"
15 |
16 | EXTERNAL SOURCES:
17 | Capacitor:
18 | :path: "../../node_modules/.pnpm/@capacitor+ios@6.1.2_@capacitor+core@6.1.2/node_modules/@capacitor/ios"
19 | CapacitorCamera:
20 | :path: "../../node_modules/.pnpm/@capacitor+camera@6.0.2_@capacitor+core@6.1.2/node_modules/@capacitor/camera"
21 | CapacitorCordova:
22 | :path: "../../node_modules/.pnpm/@capacitor+ios@6.1.2_@capacitor+core@6.1.2/node_modules/@capacitor/ios"
23 | CapacitorSplashScreen:
24 | :path: "../../node_modules/.pnpm/@capacitor+splash-screen@6.0.2_@capacitor+core@6.1.2/node_modules/@capacitor/splash-screen"
25 |
26 | SPEC CHECKSUMS:
27 | Capacitor: 679f9673fdf30597493a6362a5d5bf233d46abc2
28 | CapacitorCamera: ed022171dbf3853e68eec877b4d78995378af6b7
29 | CapacitorCordova: f48c89f96c319101cd2f0ce8a2b7449b5fb8b3dd
30 | CapacitorSplashScreen: 250df9ef8014fac5c7c1fd231f0f8b1d8f0b5624
31 |
32 | PODFILE CHECKSUM: 514e1875ca374c9581d383aa30449ad30eb0d78b
33 |
34 | COCOAPODS: 1.15.2
35 |
--------------------------------------------------------------------------------
/tests/testapp/ios/App/App/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | testapp
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | $(MARKETING_VERSION)
21 | CFBundleVersion
22 | $(CURRENT_PROJECT_VERSION)
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UIRequiredDeviceCapabilities
30 |
31 | armv7
32 |
33 | UISupportedInterfaceOrientations
34 |
35 | UIInterfaceOrientationPortrait
36 | UIInterfaceOrientationLandscapeLeft
37 | UIInterfaceOrientationLandscapeRight
38 |
39 | UISupportedInterfaceOrientations~ipad
40 |
41 | UIInterfaceOrientationPortrait
42 | UIInterfaceOrientationPortraitUpsideDown
43 | UIInterfaceOrientationLandscapeLeft
44 | UIInterfaceOrientationLandscapeRight
45 |
46 | UIViewControllerBasedStatusBarAppearance
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/docs/test/interfaces/DeviceWorkerLauncherOptions.md:
--------------------------------------------------------------------------------
1 | [**@onslip/automation**](../../README.md) • **Docs**
2 |
3 | ***
4 |
5 | [@onslip/automation](../../README.md) / [test](../README.md) / DeviceWorkerLauncherOptions
6 |
7 | # Interface: DeviceWorkerLauncherOptions
8 |
9 | [DeviceWorkerLauncher](../classes/DeviceWorkerLauncher.md) options.
10 |
11 | ## Properties
12 |
13 | ### app?
14 |
15 | > `optional` **app**: `string`
16 |
17 | If specified, the package/activity identifier or application bundle identifier to launch.
18 |
19 | #### Defined in
20 |
21 | [src/test.ts:37](https://github.com/Onslip/automation/blob/13befc40996d96bb2935315b372b921212adc8b4/src/test.ts#L37)
22 |
23 | ***
24 |
25 | ### archive?
26 |
27 | > `optional` **archive**: `string`
28 |
29 | If specified, path to the application to install on the device
30 |
31 | #### Defined in
32 |
33 | [src/test.ts:34](https://github.com/Onslip/automation/blob/13befc40996d96bb2935315b372b921212adc8b4/src/test.ts#L34)
34 |
35 | ***
36 |
37 | ### args?
38 |
39 | > `optional` **args**: `string`[]
40 |
41 | Optional application arguments.
42 |
43 | #### Defined in
44 |
45 | [src/test.ts:40](https://github.com/Onslip/automation/blob/13befc40996d96bb2935315b372b921212adc8b4/src/test.ts#L40)
46 |
47 | ***
48 |
49 | ### context?
50 |
51 | > `optional` **context**: `string`
52 |
53 | The web view/context to connect to (the actual device ID will be prepended to form a proper context path).
54 |
55 | #### Defined in
56 |
57 | [src/test.ts:43](https://github.com/Onslip/automation/blob/13befc40996d96bb2935315b372b921212adc8b4/src/test.ts#L43)
58 |
59 | ***
60 |
61 | ### devices?
62 |
63 | > `optional` **devices**: `string`[]
64 |
65 | A static list of device IDs to use (must match the number or workers) by default.
66 |
67 | #### Defined in
68 |
69 | [src/test.ts:46](https://github.com/Onslip/automation/blob/13befc40996d96bb2935315b372b921212adc8b4/src/test.ts#L46)
70 |
--------------------------------------------------------------------------------
/docs/index/functions/readCommandOutput.md:
--------------------------------------------------------------------------------
1 | [**@onslip/automation**](../../README.md) • **Docs**
2 |
3 | ***
4 |
5 | [@onslip/automation](../../README.md) / [index](../README.md) / readCommandOutput
6 |
7 | # Function: readCommandOutput()
8 |
9 | ## readCommandOutput(command, args, options)
10 |
11 | > **readCommandOutput**(`command`, `args`, `options`?): `AsyncGenerator`\<`string`\>
12 |
13 | Spawns a command and reads its standard output line by line.
14 |
15 | ### Parameters
16 |
17 | • **command**: `string`
18 |
19 | The command to executed.
20 |
21 | • **args**: `string`[]
22 |
23 | Command arguments.
24 |
25 | • **options?**: [`ReaderOptions`](../interfaces/ReaderOptions.md)
26 |
27 | Reader options.
28 |
29 | ### Returns
30 |
31 | `AsyncGenerator`\<`string`\>
32 |
33 | An async iterator generating one line at a time.
34 |
35 | ### Defined in
36 |
37 | [src/utils.ts:47](https://github.com/Onslip/automation/blob/13befc40996d96bb2935315b372b921212adc8b4/src/utils.ts#L47)
38 |
39 | ## readCommandOutput(command, args, options, timeout)
40 |
41 | > **readCommandOutput**(`command`, `args`, `options`?, `timeout`?): `AsyncGenerator`\<`string` \| `undefined`\>
42 |
43 | Spawns a command and reads its standard output line by line, with heartbeats.
44 |
45 | ### Parameters
46 |
47 | • **command**: `string`
48 |
49 | The command to executed.
50 |
51 | • **args**: `string`[]
52 |
53 | Command arguments.
54 |
55 | • **options?**: [`ReaderOptions`](../interfaces/ReaderOptions.md)
56 |
57 | Reader options.
58 |
59 | • **timeout?**: `number`
60 |
61 | The timeout, in milliseconds. If no lines has been produced within this time, an `undefined` value is
62 | generated instead of a string.
63 |
64 | ### Returns
65 |
66 | `AsyncGenerator`\<`string` \| `undefined`\>
67 |
68 | An async iterator generating one line at a time, or `undefined` on timeouts.
69 |
70 | ### Defined in
71 |
72 | [src/utils.ts:59](https://github.com/Onslip/automation/blob/13befc40996d96bb2935315b372b921212adc8b4/src/utils.ts#L59)
73 |
--------------------------------------------------------------------------------
/tests/testapp/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/tests/testapp/ios/App/App/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/demo.js:
--------------------------------------------------------------------------------
1 | const { Device, AndroidDevice, findWebViewContexts, openWebView, pipeline } = require('.' /* '@onslip/automation' */);
2 | const { createWriteStream } = require('fs');
3 | const { Readable } = require('stream');
4 |
5 | const PROXY_PORT = 8885;
6 |
7 | async function main(prog, deviceId) {
8 | const device = await Device.findDevice(deviceId);
9 |
10 | if (!device) {
11 | const devices = await Device.findDevices();
12 | throw `Usage: ${prog} ${devices.map((d) => d.id).join('|') || ''}`;
13 | }
14 |
15 | console.log(`Checking for debuggable web view on device ${device}`);
16 | const [ webviewId ] = await device.findWebViews();
17 |
18 | console.log(`Found web view ${webviewId}; opening proxy port ${PROXY_PORT}`);
19 | const options = await device.bindWebView(webviewId, PROXY_PORT);
20 |
21 | console.log(`Looking for contexts`);
22 | const [ context ] = await findWebViewContexts(options);
23 |
24 | console.info(`Opening CDP connection via port ${options.port} to context ${context.id} <${context.url}>`);
25 | const page = await openWebView({...options, ctxId: context.id });
26 |
27 | console.info(`Starting automation of ${webviewId} on device ${device}`);
28 | page.setDebug(true);
29 |
30 | try {
31 | const lollipop = parseInt(await device.osVersion()) >= 5;
32 | const logLines = device instanceof AndroidDevice
33 | ? await device.collectLogs({ clear: !lollipop, historic: !lollipop, filterspecs: ['*:D'] })
34 | : await device.collectLogs();
35 |
36 | const divs = await page.locator('div').count();
37 | console.log(`There are ${divs} DIV elements in the web view!`);
38 |
39 | await page.locator('body').screenshot({ path: `${deviceId}.png` });
40 | console.log(`Saved a screenshot to ${deviceId}.png`);
41 |
42 | await pipeline(Readable.from(await logLines()), createWriteStream(`${deviceId}.log`));
43 | console.log(`Saved device logs to ${deviceId}.log`);
44 | } finally {
45 | await page.close();
46 | }
47 | }
48 |
49 | main(...process.argv.slice(1)).catch((err) => (console.error(err), 70)).then(process.exit);
50 |
--------------------------------------------------------------------------------
/tests/testapp/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | namespace "com.onslip.automation.testapp"
5 | compileSdk rootProject.ext.compileSdkVersion
6 | defaultConfig {
7 | applicationId "com.onslip.automation.testapp"
8 | minSdkVersion rootProject.ext.minSdkVersion
9 | targetSdkVersion rootProject.ext.targetSdkVersion
10 | versionCode 1
11 | versionName "1.0"
12 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
13 | aaptOptions {
14 | // Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
15 | // Default: https://android.googlesource.com/platform/frameworks/base/+/282e181b58cf72b6ca770dc7ca5f91f135444502/tools/aapt/AaptAssets.cpp#61
16 | ignoreAssetsPattern '!.svn:!.git:!.ds_store:!*.scc:.*:!CVS:!thumbs.db:!picasa.ini:!*~'
17 | }
18 | }
19 | buildTypes {
20 | release {
21 | minifyEnabled false
22 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
23 | }
24 | }
25 | }
26 |
27 | repositories {
28 | flatDir{
29 | dirs '../capacitor-cordova-android-plugins/src/main/libs', 'libs'
30 | }
31 | }
32 |
33 | dependencies {
34 | implementation fileTree(include: ['*.jar'], dir: 'libs')
35 | implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
36 | implementation "androidx.coordinatorlayout:coordinatorlayout:$androidxCoordinatorLayoutVersion"
37 | implementation "androidx.core:core-splashscreen:$coreSplashScreenVersion"
38 | implementation project(':capacitor-android')
39 | testImplementation "junit:junit:$junitVersion"
40 | androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
41 | androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
42 | implementation project(':capacitor-cordova-android-plugins')
43 | }
44 |
45 | apply from: 'capacitor.build.gradle'
46 |
47 | try {
48 | def servicesJSON = file('google-services.json')
49 | if (servicesJSON.text) {
50 | apply plugin: 'com.google.gms.google-services'
51 | }
52 | } catch(Exception e) {
53 | logger.info("google-services.json not found, google-services plugin not applied. Push Notifications won't work")
54 | }
55 |
--------------------------------------------------------------------------------
/tests/testapp/android/.gitignore:
--------------------------------------------------------------------------------
1 | # Using Android gitignore template: https://github.com/github/gitignore/blob/HEAD/Android.gitignore
2 |
3 | # Built application files
4 | *.apk
5 | *.aar
6 | *.ap_
7 | *.aab
8 |
9 | # Files for the ART/Dalvik VM
10 | *.dex
11 |
12 | # Java class files
13 | *.class
14 |
15 | # Generated files
16 | bin/
17 | gen/
18 | out/
19 | # Uncomment the following line in case you need and you don't have the release build type files in your app
20 | # release/
21 |
22 | # Gradle files
23 | .gradle/
24 | build/
25 |
26 | # Local configuration file (sdk path, etc)
27 | local.properties
28 |
29 | # Proguard folder generated by Eclipse
30 | proguard/
31 |
32 | # Log Files
33 | *.log
34 |
35 | # Android Studio Navigation editor temp files
36 | .navigation/
37 |
38 | # Android Studio captures folder
39 | captures/
40 |
41 | # IntelliJ
42 | *.iml
43 | .idea/workspace.xml
44 | .idea/tasks.xml
45 | .idea/gradle.xml
46 | .idea/assetWizardSettings.xml
47 | .idea/dictionaries
48 | .idea/libraries
49 | # Android Studio 3 in .gitignore file.
50 | .idea/caches
51 | .idea/modules.xml
52 | # Comment next line if keeping position of elements in Navigation Editor is relevant for you
53 | .idea/navEditor.xml
54 |
55 | # Keystore files
56 | # Uncomment the following lines if you do not want to check your keystore files in.
57 | #*.jks
58 | #*.keystore
59 |
60 | # External native build folder generated in Android Studio 2.2 and later
61 | .externalNativeBuild
62 | .cxx/
63 |
64 | # Google Services (e.g. APIs or Firebase)
65 | # google-services.json
66 |
67 | # Freeline
68 | freeline.py
69 | freeline/
70 | freeline_project_description.json
71 |
72 | # fastlane
73 | fastlane/report.xml
74 | fastlane/Preview.html
75 | fastlane/screenshots
76 | fastlane/test_output
77 | fastlane/readme.md
78 |
79 | # Version control
80 | vcs.xml
81 |
82 | # lint
83 | lint/intermediates/
84 | lint/generated/
85 | lint/outputs/
86 | lint/tmp/
87 | # lint/reports/
88 |
89 | # Android Profiling
90 | *.hprof
91 |
92 | # Cordova plugins for Capacitor
93 | capacitor-cordova-android-plugins
94 |
95 | # Copied web assets
96 | app/src/main/assets/public
97 |
98 | # Generated Config files
99 | app/src/main/assets/capacitor.config.json
100 | app/src/main/assets/capacitor.plugins.json
101 | app/src/main/res/xml/config.xml
102 |
--------------------------------------------------------------------------------
/docs/index/interfaces/iOSLogOptions.md:
--------------------------------------------------------------------------------
1 | [**@onslip/automation**](../../README.md) • **Docs**
2 |
3 | ***
4 |
5 | [@onslip/automation](../../README.md) / [index](../README.md) / iOSLogOptions
6 |
7 | # Interface: iOSLogOptions
8 |
9 | ## Extends
10 |
11 | - [`ReaderOptions`](ReaderOptions.md)
12 |
13 | ## Properties
14 |
15 | ### exclude?
16 |
17 | > `optional` **exclude**: `string`[]
18 |
19 | Processes to exclude.
20 |
21 | #### Defined in
22 |
23 | [src/ios-device.ts:48](https://github.com/Onslip/automation/blob/13befc40996d96bb2935315b372b921212adc8b4/src/ios-device.ts#L48)
24 |
25 | ***
26 |
27 | ### include?
28 |
29 | > `optional` **include**: `string`[]
30 |
31 | Processes to include.
32 |
33 | #### Defined in
34 |
35 | [src/ios-device.ts:45](https://github.com/Onslip/automation/blob/13befc40996d96bb2935315b372b921212adc8b4/src/ios-device.ts#L45)
36 |
37 | ***
38 |
39 | ### match?
40 |
41 | > `optional` **match**: `string`
42 |
43 | Only include log lines that contain this string.
44 |
45 | #### Defined in
46 |
47 | [src/ios-device.ts:42](https://github.com/Onslip/automation/blob/13befc40996d96bb2935315b372b921212adc8b4/src/ios-device.ts#L42)
48 |
49 | ***
50 |
51 | ### quiet?
52 |
53 | > `optional` **quiet**: `boolean`
54 |
55 | If true, excludes a predefined set of noisy processes in addition to those specified via the [exclude](iOSLogOptions.md#exclude) option.
56 |
57 | #### Defined in
58 |
59 | [src/ios-device.ts:51](https://github.com/Onslip/automation/blob/13befc40996d96bb2935315b372b921212adc8b4/src/ios-device.ts#L51)
60 |
61 | ***
62 |
63 | ### separator?
64 |
65 | > `optional` **separator**: `string`
66 |
67 | A line separator to add to each generated line.
68 |
69 | #### Inherited from
70 |
71 | [`ReaderOptions`](ReaderOptions.md).[`separator`](ReaderOptions.md#separator)
72 |
73 | #### Defined in
74 |
75 | [src/utils.ts:36](https://github.com/Onslip/automation/blob/13befc40996d96bb2935315b372b921212adc8b4/src/utils.ts#L36)
76 |
77 | ***
78 |
79 | ### stopSignal?
80 |
81 | > `optional` **stopSignal**: `Signal`\<`boolean`\>
82 |
83 | A signal to check. Reading will stop when signal is `true.`
84 |
85 | #### Inherited from
86 |
87 | [`ReaderOptions`](ReaderOptions.md).[`stopSignal`](ReaderOptions.md#stopsignal)
88 |
89 | #### Defined in
90 |
91 | [src/utils.ts:33](https://github.com/Onslip/automation/blob/13befc40996d96bb2935315b372b921212adc8b4/src/utils.ts#L33)
92 |
--------------------------------------------------------------------------------
/docs/index/interfaces/DeviceOptions.md:
--------------------------------------------------------------------------------
1 | [**@onslip/automation**](../../README.md) • **Docs**
2 |
3 | ***
4 |
5 | [@onslip/automation](../../README.md) / [index](../README.md) / DeviceOptions
6 |
7 | # Interface: DeviceOptions
8 |
9 | Device manager configuration.
10 |
11 | ## Properties
12 |
13 | ### adb?
14 |
15 | > `optional` **adb**: `null` \| `string`
16 |
17 | Where to find the `adb` command. `undefined` means Android support is optional (enabled if `adb` is in the path) and
18 | `null` disables Android support completely.
19 |
20 | #### Defined in
21 |
22 | [src/device.ts:14](https://github.com/Onslip/automation/blob/13befc40996d96bb2935315b372b921212adc8b4/src/device.ts#L14)
23 |
24 | ***
25 |
26 | ### ideviceinstaller?
27 |
28 | > `optional` **ideviceinstaller**: `string`
29 |
30 | Where to find the `ideviceinstaller` command. Used by [iOSDevice.install](../classes/iOSDevice.md#install)/[iOSDevice.uninstall](../classes/iOSDevice.md#uninstall).
31 |
32 | #### Defined in
33 |
34 | [src/device.ts:17](https://github.com/Onslip/automation/blob/13befc40996d96bb2935315b372b921212adc8b4/src/device.ts#L17)
35 |
36 | ***
37 |
38 | ### idevicesyslog?
39 |
40 | > `optional` **idevicesyslog**: `string`
41 |
42 | Where to find the `idevicesyslog` command. Used by [iOSDevice.readLogs](../classes/iOSDevice.md#readlogs)/[iOSDevice.collectLogs](../classes/iOSDevice.md#collectlogs).
43 |
44 | #### Defined in
45 |
46 | [src/device.ts:20](https://github.com/Onslip/automation/blob/13befc40996d96bb2935315b372b921212adc8b4/src/device.ts#L20)
47 |
48 | ***
49 |
50 | ### ios\_instruments\_client?
51 |
52 | > `optional` **ios\_instruments\_client**: `string`
53 |
54 | Where to find the `ios_instruments_client` command. Used by [iOSDevice.start](../classes/iOSDevice.md#start)/[iOSDevice.stop](../classes/iOSDevice.md#stop).
55 |
56 | #### Defined in
57 |
58 | [src/device.ts:23](https://github.com/Onslip/automation/blob/13befc40996d96bb2935315b372b921212adc8b4/src/device.ts#L23)
59 |
60 | ***
61 |
62 | ### iwdpPort?
63 |
64 | > `optional` **iwdpPort**: `null` \| `number`
65 |
66 | What port `ios_webkit_debug_proxy` is listening on. `undefined` means iOS support is optional (enabled if
67 | `ios_webkit_debug_proxy` is listening on port 9221) and `null` disables iOS support completely.
68 |
69 | #### Defined in
70 |
71 | [src/device.ts:29](https://github.com/Onslip/automation/blob/13befc40996d96bb2935315b372b921212adc8b4/src/device.ts#L29)
72 |
--------------------------------------------------------------------------------
/docs/index/interfaces/AutomationOptions.md:
--------------------------------------------------------------------------------
1 | [**@onslip/automation**](../../README.md) • **Docs**
2 |
3 | ***
4 |
5 | [@onslip/automation](../../README.md) / [index](../README.md) / AutomationOptions
6 |
7 | # Interface: AutomationOptions
8 |
9 | ## Extends
10 |
11 | - `BaseOptions`
12 |
13 | ## Properties
14 |
15 | ### alterPath()?
16 |
17 | > `optional` **alterPath**: (`path`) => `string`
18 |
19 | #### Parameters
20 |
21 | • **path**: `string`
22 |
23 | #### Returns
24 |
25 | `string`
26 |
27 | #### Inherited from
28 |
29 | `BaseOptions.alterPath`
30 |
31 | #### Defined in
32 |
33 | node\_modules/.pnpm/@types+chrome-remote-interface@0.31.14/node\_modules/@types/chrome-remote-interface/index.d.ts:10
34 |
35 | ***
36 |
37 | ### appId?
38 |
39 | > `optional` **appId**: `string`
40 |
41 | Only include contexts owned by this iOS process.
42 |
43 | #### Defined in
44 |
45 | [src/automation.ts:71](https://github.com/Onslip/automation/blob/13befc40996d96bb2935315b372b921212adc8b4/src/automation.ts#L71)
46 |
47 | ***
48 |
49 | ### ctxId?
50 |
51 | > `optional` **ctxId**: `string`
52 |
53 | The [AutomationContext.id](AutomationContext.md#id) to use.
54 |
55 | #### Defined in
56 |
57 | [src/automation.ts:74](https://github.com/Onslip/automation/blob/13befc40996d96bb2935315b372b921212adc8b4/src/automation.ts#L74)
58 |
59 | ***
60 |
61 | ### host?
62 |
63 | > `optional` **host**: `string`
64 |
65 | #### Inherited from
66 |
67 | `BaseOptions.host`
68 |
69 | #### Defined in
70 |
71 | node\_modules/.pnpm/@types+chrome-remote-interface@0.31.14/node\_modules/@types/chrome-remote-interface/index.d.ts:6
72 |
73 | ***
74 |
75 | ### port?
76 |
77 | > `optional` **port**: `number`
78 |
79 | #### Inherited from
80 |
81 | `BaseOptions.port`
82 |
83 | #### Defined in
84 |
85 | node\_modules/.pnpm/@types+chrome-remote-interface@0.31.14/node\_modules/@types/chrome-remote-interface/index.d.ts:7
86 |
87 | ***
88 |
89 | ### secure?
90 |
91 | > `optional` **secure**: `boolean`
92 |
93 | #### Inherited from
94 |
95 | `BaseOptions.secure`
96 |
97 | #### Defined in
98 |
99 | node\_modules/.pnpm/@types+chrome-remote-interface@0.31.14/node\_modules/@types/chrome-remote-interface/index.d.ts:8
100 |
101 | ***
102 |
103 | ### useHostName?
104 |
105 | > `optional` **useHostName**: `boolean`
106 |
107 | #### Inherited from
108 |
109 | `BaseOptions.useHostName`
110 |
111 | #### Defined in
112 |
113 | node\_modules/.pnpm/@types+chrome-remote-interface@0.31.14/node\_modules/@types/chrome-remote-interface/index.d.ts:9
114 |
--------------------------------------------------------------------------------
/docs/index/functions/mkdir.md:
--------------------------------------------------------------------------------
1 | [**@onslip/automation**](../../README.md) • **Docs**
2 |
3 | ***
4 |
5 | [@onslip/automation](../../README.md) / [index](../README.md) / mkdir
6 |
7 | # Function: mkdir()
8 |
9 | ## mkdir(path, options)
10 |
11 | > **mkdir**(`path`, `options`): `Promise`\<`string` \| `undefined`\>
12 |
13 | Asynchronous mkdir(2) - create a directory.
14 |
15 | ### Parameters
16 |
17 | • **path**: `PathLike`
18 |
19 | A path to a file. If a URL is provided, it must use the `file:` protocol.
20 |
21 | • **options**: `MakeDirectoryOptions` & `object`
22 |
23 | Either the file mode, or an object optionally specifying the file mode and whether parent folders
24 | should be created. If a string is passed, it is parsed as an octal integer. If not specified, defaults to `0o777`.
25 |
26 | ### Returns
27 |
28 | `Promise`\<`string` \| `undefined`\>
29 |
30 | ### Defined in
31 |
32 | [src/utils.ts:8](https://github.com/Onslip/automation/blob/13befc40996d96bb2935315b372b921212adc8b4/src/utils.ts#L8)
33 |
34 | ## mkdir(path, options)
35 |
36 | > **mkdir**(`path`, `options`?): `Promise`\<`void`\>
37 |
38 | Asynchronous mkdir(2) - create a directory.
39 |
40 | ### Parameters
41 |
42 | • **path**: `PathLike`
43 |
44 | A path to a file. If a URL is provided, it must use the `file:` protocol.
45 |
46 | • **options?**: `null` \| `Mode` \| `MakeDirectoryOptions` & `object`
47 |
48 | Either the file mode, or an object optionally specifying the file mode and whether parent folders
49 | should be created. If a string is passed, it is parsed as an octal integer. If not specified, defaults to `0o777`.
50 |
51 | ### Returns
52 |
53 | `Promise`\<`void`\>
54 |
55 | ### Defined in
56 |
57 | [src/utils.ts:8](https://github.com/Onslip/automation/blob/13befc40996d96bb2935315b372b921212adc8b4/src/utils.ts#L8)
58 |
59 | ## mkdir(path, options)
60 |
61 | > **mkdir**(`path`, `options`?): `Promise`\<`string` \| `undefined`\>
62 |
63 | Asynchronous mkdir(2) - create a directory.
64 |
65 | ### Parameters
66 |
67 | • **path**: `PathLike`
68 |
69 | A path to a file. If a URL is provided, it must use the `file:` protocol.
70 |
71 | • **options?**: `null` \| `Mode` \| `MakeDirectoryOptions`
72 |
73 | Either the file mode, or an object optionally specifying the file mode and whether parent folders
74 | should be created. If a string is passed, it is parsed as an octal integer. If not specified, defaults to `0o777`.
75 |
76 | ### Returns
77 |
78 | `Promise`\<`string` \| `undefined`\>
79 |
80 | ### Defined in
81 |
82 | [src/utils.ts:8](https://github.com/Onslip/automation/blob/13befc40996d96bb2935315b372b921212adc8b4/src/utils.ts#L8)
83 |
--------------------------------------------------------------------------------
/docs/index/functions/resolveWebViewContext.md:
--------------------------------------------------------------------------------
1 | [**@onslip/automation**](../../README.md) • **Docs**
2 |
3 | ***
4 |
5 | [@onslip/automation](../../README.md) / [index](../README.md) / resolveWebViewContext
6 |
7 | # Function: resolveWebViewContext()
8 |
9 | > **resolveWebViewContext**(`contextPath`, `port`, `deviceOptions`?, `timeout`?): `Promise`\<[`ResolvedContextPath`](../interfaces/ResolvedContextPath.md)\>
10 |
11 | Binds a device's web view to a local port and then locates one of its contexts, based on a context path.
12 |
13 | The context path is a string that uniquely identifies the device, web view and context, using the following format:
14 | `[/[/]]`. The device ID is is required, but the web view name and context ID are
15 | optional. You may use `$n` to match the n:th web view or context found (starting at 1).
16 |
17 | This is a convenience function that uses [Device.findDevice](../classes/Device.md#finddevice) and [Device.findWebViews](../classes/Device.md#findwebviews) to find a web
18 | view, binds it to a local port with [Device.bindWebView](../classes/Device.md#bindwebview) and then finally calls [findWebViewContexts](findWebViewContexts.md) to
19 | resolve to a web view context.
20 |
21 | ## Parameters
22 |
23 | • **contextPath**: `string`
24 |
25 | Specify what device, web view and context to use.
26 |
27 | • **port**: `number`
28 |
29 | The port to bind the web view to. See [Device.bindWebView](../classes/Device.md#bindwebview).
30 |
31 | • **deviceOptions?**: [`DeviceOptions`](../interfaces/DeviceOptions.md)
32 |
33 | Additional options to pass to [Device.findDevice](../classes/Device.md#finddevice).
34 |
35 | • **timeout?**: `number`
36 |
37 | If specified, the method will wait for the device, web view and context to appear this many
38 | milliseconds (0 means forever).
39 |
40 | ## Returns
41 |
42 | `Promise`\<[`ResolvedContextPath`](../interfaces/ResolvedContextPath.md)\>
43 |
44 | A [ResolvedContextPath](../interfaces/ResolvedContextPath.md) object containing [AutomationOptions](../interfaces/AutomationOptions.md), ready to be passed to
45 | [openWebView](openWebView.md), the web view id and the [Device](../classes/Device.md) on which the web view is running.
46 |
47 | ## Throws
48 |
49 | TypeError If the context path or the port is invalid.
50 |
51 | ## Throws
52 |
53 | Error If the device, web view or context could not be found, or if the context path is ambiguous.
54 |
55 | ## Defined in
56 |
57 | [src/api.ts:61](https://github.com/Onslip/automation/blob/13befc40996d96bb2935315b372b921212adc8b4/src/api.ts#L61)
58 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | TESTAPPS := tests/testapp/dist/index.html \
2 | tests/testapp/android/app/build/outputs/apk/debug/app-debug.apk \
3 | tests/testapp/ios/App/DerivedData/App/Build/Products/Debug-iphonesimulator/App.app
4 |
5 | help: ## Show all public targets (this command).
6 | @awk -F ':.*## ' '/^[^\t]+:.*## / { printf "\033[1m%-16s\033[0m %s\n", $$1, $$2 }' $(MAKEFILE_LIST)
7 |
8 | all: build docs ## Build all artifacts.
9 |
10 | prepare: ## Build and install all dependencies.
11 | pnpm install --frozen-lockfile
12 | pnpm -C tests/testapp install --frozen-lockfile
13 |
14 | build: prepare ## Build package.
15 | pnpm exec tsc --build
16 |
17 | testapps: prepare ## Build test apps.
18 | $(MAKE) $(TESTAPPS)
19 |
20 | $(TESTAPPS):
21 | pnpm -C tests/testapp build
22 | pnpm -C tests/testapp exec cap sync
23 | cd tests/testapp/android && ./gradlew assembleDebug
24 | cd tests/testapp/ios/App && xcodebuild -workspace App.xcworkspace -scheme App -configuration Debug -sdk iphonesimulator -derivedDataPath DerivedData/App
25 |
26 | docs: prepare ## Build API documentation.
27 | rm -rf docs
28 | pnpm exec typedoc --entryPoints src --entryPoints src/test.ts --excludePrivate --excludeProtected --readme none --plugin typedoc-plugin-markdown
29 |
30 | test: build testapps ## Run Playwright tests.
31 | pnpm exec playwright test --config tests
32 |
33 | clean: ## Clean all build artifacts (but not dependencies).
34 | rm -rf build test-results
35 | rm -rf tests/testapp/dist
36 | rm -rf tests/testapp/android/{capacitor-cordova-android-plugins,app/{build,src/main/{assets/{capacitor.{config,plugins}.json,public},res/xml/config.xml}}}
37 | rm -rf tests/testapp/ios/{capacitor-cordova-ios-plugins,App/{build,DerivedData,Pods,App/{capacitor.config.json,config.xml,public}}}
38 |
39 | distclean: clean ## Like clean, but also remove all dependencies.
40 | rm -rf node_modules tests/testapp/node_modules tests/testapp/android/.gradle
41 |
42 | commit: prepare ## Commit a change and create a change-log entry for it.
43 | pnpm exec changeset
44 |
45 | release: pristine prepare ## Bump all package versions and generate changelog.
46 | $(MAKE) docs && git add -A docs
47 | pnpm exec changeset version
48 | pnpm install
49 | git commit --amend --reuse-message=HEAD pnpm-lock.yaml
50 |
51 | publish: pristine clean build ## Publish all new packages to NPM.
52 | pnpm publish --access public
53 | GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=tag.gpgSign GIT_CONFIG_VALUE_0=true pnpm exec changeset tag
54 |
55 | pristine:
56 | @[[ -z "$$(git status --porcelain)" ]] || (git status; false)
57 |
58 | .PHONY: help all prepare build testapps docs test clean distclean commit release publish pristine
59 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # @onslip/automation
2 |
3 | ## 1.3.0
4 |
5 | ### Minor Changes
6 |
7 | - 166124f: Handle fixture timeouts better and made them configurable.
8 |
9 | ### Patch Changes
10 |
11 | - 13befc4: Found a way to use same assertion names. Let's pretend 1.2.1 never happened.
12 |
13 | ## 1.2.1
14 |
15 | ### Patch Changes
16 |
17 | - 55b36c4: Trying to use the same assertion names as Playwright was a really bad idea.
18 |
19 | ## 1.2.0
20 |
21 | ### Minor Changes
22 |
23 | - 88f9c39: Support Shadow DOM-piercing. All nested XPath expressions should be relative.
24 | - 2e28f23: Added textContent, isChecked, isDisabled, isEnabled, isEditable, isHidden, isVisible to Locator.
25 | - 0e3d02e: Added a timeout parameter to Device.findDevice().
26 | - aed87d3: Allow arguments to be passed to started app.
27 | - 2da2b00: Added assertions/custom matchers for @playwright/test.
28 | - 883546e: Added resolveWebViewContext convenience function.
29 |
30 | ### Patch Changes
31 |
32 | - 57286e5: Pass device ID to adb/ideviceinstaller when installing/uninstalling apps.
33 | - 19f723e: Default page timeout should be 0 (disabled), like in Playwright.
34 |
35 | ## 1.1.0
36 |
37 | ### Minor Changes
38 |
39 | - 4cca30e: Bumped deps.
40 |
41 | ### Patch Changes
42 |
43 | - 65463c2: @types/chrome-remote-interface should be a regular dependency.
44 | - 2e0f02f: Don't leak on-exit handlers.
45 |
46 | ## 1.0.1
47 |
48 | ### Patch Changes
49 |
50 | - 7be0f9a: Ensure spawned processes (like idevicesyslog) are killed on exit.
51 | - 1fbabf1: iOS 9 (ES5) fixes.
52 |
53 | ## 1.0.0
54 |
55 | ### Major Changes
56 |
57 | - 16a19ed: Removed 'protocol' option since it's not used anyway.
58 | - b6606b0: Device management is now unified into a single API supporting both Android and iOS.
59 |
60 | ## 0.4.0
61 |
62 | ### Minor Changes
63 |
64 | - b4b6364: Added 'historic' logcat option, a better alternative to 'clear'.
65 | - 723f903: Added basic iOS support via ios_webkit_debug_proxy
66 | - b938d1d: Added fill() to input text.
67 |
68 | ## 0.3.0
69 |
70 | ### Minor Changes
71 |
72 | - 8cc5366: Added stopAndroidApplication and executeAndroidShellCommand helpers.
73 |
74 | ### Patch Changes
75 |
76 | - 9ff4353: Wait for DOM before initialization is complete.
77 | - b653813: Prettier debug output.
78 | - c93c858: Bumped deps.
79 |
80 | ## 0.2.0
81 |
82 | ### Minor Changes
83 |
84 | - bc3a9f2: Added Page.evaluate.
85 | - 6ba828f: Support shorthand text selectors.
86 | - f878fab: Added startAndroidActivity to lauch activities.
87 | - 8b6fce0: Added scrollIntoViewIfNeeded.
88 | - 8d24a46: Added evaluate and evaluateAll.
89 | - f150d66: Added partial Page.mouse.
90 | - 66003f5: Added RegExp text selectors.
91 | - 4dd5293: Added has/hasText locator options.
92 |
93 | ### Patch Changes
94 |
95 | - 755be74: Cannot fetch props from unconnected nodes.
96 | - dd95272: Fixed isTarget calculation.
97 |
98 | ## 0.1.0
99 |
100 | ### Minor Changes
101 |
102 | - 25301f4: Initial release.
103 |
--------------------------------------------------------------------------------
/docs/index/interfaces/AndroidLogOptions.md:
--------------------------------------------------------------------------------
1 | [**@onslip/automation**](../../README.md) • **Docs**
2 |
3 | ***
4 |
5 | [@onslip/automation](../../README.md) / [index](../README.md) / AndroidLogOptions
6 |
7 | # Interface: AndroidLogOptions
8 |
9 | ## Extends
10 |
11 | - [`ReaderOptions`](ReaderOptions.md)
12 |
13 | ## Properties
14 |
15 | ### buffers?
16 |
17 | > `optional` **buffers**: (`"main"` \| `"system"` \| `"radio"` \| `"events"` \| `"crash"`)[]
18 |
19 | #### Defined in
20 |
21 | [src/android-device.ts:10](https://github.com/Onslip/automation/blob/13befc40996d96bb2935315b372b921212adc8b4/src/android-device.ts#L10)
22 |
23 | ***
24 |
25 | ### clear?
26 |
27 | > `optional` **clear**: `boolean`
28 |
29 | #### Defined in
30 |
31 | [src/android-device.ts:6](https://github.com/Onslip/automation/blob/13befc40996d96bb2935315b372b921212adc8b4/src/android-device.ts#L6)
32 |
33 | ***
34 |
35 | ### filterspecs?
36 |
37 | > `optional` **filterspecs**: `string`[]
38 |
39 | #### Defined in
40 |
41 | [src/android-device.ts:11](https://github.com/Onslip/automation/blob/13befc40996d96bb2935315b372b921212adc8b4/src/android-device.ts#L11)
42 |
43 | ***
44 |
45 | ### format?
46 |
47 | > `optional` **format**: `"raw"` \| `"long"` \| `"time"` \| `"brief"` \| `"process"` \| `"tag"` \| `"thread"` \| `"threadtime"`
48 |
49 | #### Defined in
50 |
51 | [src/android-device.ts:8](https://github.com/Onslip/automation/blob/13befc40996d96bb2935315b372b921212adc8b4/src/android-device.ts#L8)
52 |
53 | ***
54 |
55 | ### historic?
56 |
57 | > `optional` **historic**: `boolean`
58 |
59 | #### Defined in
60 |
61 | [src/android-device.ts:7](https://github.com/Onslip/automation/blob/13befc40996d96bb2935315b372b921212adc8b4/src/android-device.ts#L7)
62 |
63 | ***
64 |
65 | ### modifiers?
66 |
67 | > `optional` **modifiers**: (`"color"` \| `"year"` \| `"descriptive"` \| `"epoch"` \| `"monotonic"` \| `"printable"` \| `"uid"` \| `"usec"` \| `"UTC"` \| `"zone"`)[]
68 |
69 | #### Defined in
70 |
71 | [src/android-device.ts:9](https://github.com/Onslip/automation/blob/13befc40996d96bb2935315b372b921212adc8b4/src/android-device.ts#L9)
72 |
73 | ***
74 |
75 | ### separator?
76 |
77 | > `optional` **separator**: `string`
78 |
79 | A line separator to add to each generated line.
80 |
81 | #### Inherited from
82 |
83 | [`ReaderOptions`](ReaderOptions.md).[`separator`](ReaderOptions.md#separator)
84 |
85 | #### Defined in
86 |
87 | [src/utils.ts:36](https://github.com/Onslip/automation/blob/13befc40996d96bb2935315b372b921212adc8b4/src/utils.ts#L36)
88 |
89 | ***
90 |
91 | ### stopSignal?
92 |
93 | > `optional` **stopSignal**: `Signal`\<`boolean`\>
94 |
95 | A signal to check. Reading will stop when signal is `true.`
96 |
97 | #### Inherited from
98 |
99 | [`ReaderOptions`](ReaderOptions.md).[`stopSignal`](ReaderOptions.md#stopsignal)
100 |
101 | #### Defined in
102 |
103 | [src/utils.ts:33](https://github.com/Onslip/automation/blob/13befc40996d96bb2935315b372b921212adc8b4/src/utils.ts#L33)
104 |
--------------------------------------------------------------------------------
/docs/index/interfaces/AutomationContext.md:
--------------------------------------------------------------------------------
1 | [**@onslip/automation**](../../README.md) • **Docs**
2 |
3 | ***
4 |
5 | [@onslip/automation](../../README.md) / [index](../README.md) / AutomationContext
6 |
7 | # Interface: AutomationContext
8 |
9 | ## Properties
10 |
11 | ### appId?
12 |
13 | > `optional` **appId**: `string`
14 |
15 | iOS process owning this context.
16 |
17 | #### Defined in
18 |
19 | [src/automation.ts:79](https://github.com/Onslip/automation/blob/13befc40996d96bb2935315b372b921212adc8b4/src/automation.ts#L79)
20 |
21 | ***
22 |
23 | ### description?
24 |
25 | > `optional` **description**: `string`
26 |
27 | Context description
28 |
29 | #### Defined in
30 |
31 | [src/automation.ts:82](https://github.com/Onslip/automation/blob/13befc40996d96bb2935315b372b921212adc8b4/src/automation.ts#L82)
32 |
33 | ***
34 |
35 | ### devtoolsFrontendUrl
36 |
37 | > **devtoolsFrontendUrl**: `string`
38 |
39 | URL to a DevTools session for this context.
40 |
41 | #### Defined in
42 |
43 | [src/automation.ts:85](https://github.com/Onslip/automation/blob/13befc40996d96bb2935315b372b921212adc8b4/src/automation.ts#L85)
44 |
45 | ***
46 |
47 | ### faviconUrl?
48 |
49 | > `optional` **faviconUrl**: `string`
50 |
51 | URL of favicon image.
52 |
53 | #### Defined in
54 |
55 | [src/automation.ts:88](https://github.com/Onslip/automation/blob/13befc40996d96bb2935315b372b921212adc8b4/src/automation.ts#L88)
56 |
57 | ***
58 |
59 | ### id
60 |
61 | > **id**: `string`
62 |
63 | Context ID.
64 |
65 | #### Defined in
66 |
67 | [src/automation.ts:91](https://github.com/Onslip/automation/blob/13befc40996d96bb2935315b372b921212adc8b4/src/automation.ts#L91)
68 |
69 | ***
70 |
71 | ### thumbnailUrl?
72 |
73 | > `optional` **thumbnailUrl**: `string`
74 |
75 | URL of thumbnail image.
76 |
77 | #### Defined in
78 |
79 | [src/automation.ts:94](https://github.com/Onslip/automation/blob/13befc40996d96bb2935315b372b921212adc8b4/src/automation.ts#L94)
80 |
81 | ***
82 |
83 | ### title
84 |
85 | > **title**: `string`
86 |
87 | The title of the page loaded in this context.
88 |
89 | #### Defined in
90 |
91 | [src/automation.ts:97](https://github.com/Onslip/automation/blob/13befc40996d96bb2935315b372b921212adc8b4/src/automation.ts#L97)
92 |
93 | ***
94 |
95 | ### type?
96 |
97 | > `optional` **type**: `string`
98 |
99 | Type of context.
100 |
101 | #### Defined in
102 |
103 | [src/automation.ts:100](https://github.com/Onslip/automation/blob/13befc40996d96bb2935315b372b921212adc8b4/src/automation.ts#L100)
104 |
105 | ***
106 |
107 | ### url
108 |
109 | > **url**: `string`
110 |
111 | The URL of the page loaded in this context.
112 |
113 | #### Defined in
114 |
115 | [src/automation.ts:103](https://github.com/Onslip/automation/blob/13befc40996d96bb2935315b372b921212adc8b4/src/automation.ts#L103)
116 |
117 | ***
118 |
119 | ### webSocketDebuggerUrl
120 |
121 | > **webSocketDebuggerUrl**: `string`
122 |
123 | The URL where the debugger is running.
124 |
125 | #### Defined in
126 |
127 | [src/automation.ts:106](https://github.com/Onslip/automation/blob/13befc40996d96bb2935315b372b921212adc8b4/src/automation.ts#L106)
128 |
--------------------------------------------------------------------------------
/tests/testapp/ios/App/App/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import Capacitor
3 |
4 | @UIApplicationMain
5 | class AppDelegate: UIResponder, UIApplicationDelegate {
6 |
7 | var window: UIWindow?
8 |
9 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
10 | // Override point for customization after application launch.
11 | return true
12 | }
13 |
14 | func applicationWillResignActive(_ application: UIApplication) {
15 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
16 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
17 | }
18 |
19 | func applicationDidEnterBackground(_ application: UIApplication) {
20 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
21 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
22 | }
23 |
24 | func applicationWillEnterForeground(_ application: UIApplication) {
25 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
26 | }
27 |
28 | func applicationDidBecomeActive(_ application: UIApplication) {
29 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
30 | }
31 |
32 | func applicationWillTerminate(_ application: UIApplication) {
33 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
34 | }
35 |
36 | func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
37 | // Called when the app was launched with a url. Feel free to add additional processing here,
38 | // but if you want the App API to support tracking app url opens, make sure to keep this call
39 | return ApplicationDelegateProxy.shared.application(app, open: url, options: options)
40 | }
41 |
42 | func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
43 | // Called when the app was launched with an activity, including Universal Links.
44 | // Feel free to add additional processing here, but if you want the App API to support
45 | // tracking app url opens, make sure to keep this call
46 | return ApplicationDelegateProxy.shared.application(application, continue: userActivity, restorationHandler: restorationHandler)
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/tests/testapp/android/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%"=="" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%"=="" set DIRNAME=.
29 | @rem This is normally unused
30 | set APP_BASE_NAME=%~n0
31 | set APP_HOME=%DIRNAME%
32 |
33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
35 |
36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
38 |
39 | @rem Find java.exe
40 | if defined JAVA_HOME goto findJavaFromJavaHome
41 |
42 | set JAVA_EXE=java.exe
43 | %JAVA_EXE% -version >NUL 2>&1
44 | if %ERRORLEVEL% equ 0 goto execute
45 |
46 | echo.
47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
48 | echo.
49 | echo Please set the JAVA_HOME variable in your environment to match the
50 | echo location of your Java installation.
51 |
52 | goto fail
53 |
54 | :findJavaFromJavaHome
55 | set JAVA_HOME=%JAVA_HOME:"=%
56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
57 |
58 | if exist "%JAVA_EXE%" goto execute
59 |
60 | echo.
61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
62 | echo.
63 | echo Please set the JAVA_HOME variable in your environment to match the
64 | echo location of your Java installation.
65 |
66 | goto fail
67 |
68 | :execute
69 | @rem Setup the command line
70 |
71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
72 |
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if %ERRORLEVEL% equ 0 goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | set EXIT_CODE=%ERRORLEVEL%
85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
87 | exit /b %EXIT_CODE%
88 |
89 | :mainEnd
90 | if "%OS%"=="Windows_NT" endlocal
91 |
92 | :omega
93 |
--------------------------------------------------------------------------------
/tests/playwright.android.ts:
--------------------------------------------------------------------------------
1 | import { WorkerInfo } from '@playwright/test';
2 | import { execFile, sleep } from '../src';
3 | import { DeviceWorkerLauncher, DeviceWorkerLauncherOptions } from '../src/test';
4 |
5 | export interface AndroidLauncherOptions extends DeviceWorkerLauncherOptions {
6 | emulator?: {
7 | name: (worker: number) => string,
8 | system: string,
9 | device: string,
10 | ports?: number[],
11 | },
12 | }
13 |
14 | export class AndroidLauncher extends DeviceWorkerLauncher {
15 | private emulators: (ReturnType | undefined)[] = [];
16 |
17 | override async setup(worker: number, headless: boolean, workerInfo: WorkerInfo) {
18 | const dwConfig = this.deviceWorkerConfig(workerInfo);
19 |
20 | if (this.options?.emulator) {
21 | const emulatorPort = 5300 + worker * 2;
22 | const deviceId = `emulator-${emulatorPort}`;
23 |
24 | dwConfig?.console?.info?.(`⚙️ Creating AVD ${this.options.emulator.name(worker)}`);
25 | await execFile('avdmanager', ['--silent', 'create', 'avd',
26 | '--force',
27 | '--name', this.options.emulator.name(worker),
28 | '--package', this.options.emulator.system,
29 | '--device', this.options.emulator.device,
30 | ]);
31 |
32 | // Ensure ADB server is running before starting emulator
33 | await execFile('adb', ['start-server']);
34 |
35 | dwConfig?.console?.info?.(`⚙️ Starting ${headless ? 'headless ' : ''}emulator ${this.options.emulator.name(worker)} as device ${deviceId}`);
36 | this.emulators[worker]?.child.kill();
37 | this.emulators[worker] = execFile('emulator', [
38 | '-avd', this.options.emulator.name(worker),
39 | '-port', `${emulatorPort}`,
40 | '-no-snapshot',
41 | ...headless ? [ '-no-window' ] : [],
42 | ]);
43 |
44 | for (let i = 0; (await execFile('adb', ['-s', deviceId, 'wait-for-device', 'shell', 'getprop', 'sys.boot_completed'])).stdout.trim() !== '1'; ++i) {
45 | i % 10 || dwConfig?.console?.debug?.(`🐢 Waiting for device ${deviceId} to boot ...`);
46 | await sleep(1000);
47 | }
48 |
49 | for (const port of this.options.emulator.ports ?? []) {
50 | dwConfig?.console?.info?.(`⚙️ Forwarding localhost port ${port} from device ${deviceId} to host`);
51 | await execFile('adb', ['-s', deviceId, 'reverse', `tcp:${port}`, `tcp:${port}`]);
52 | }
53 |
54 | return deviceId;
55 | } else {
56 | return super.setup(worker, headless, workerInfo);
57 | }
58 | }
59 |
60 | override async teardown(worker: number, workerInfo: WorkerInfo) {
61 | const dwConfig = this.deviceWorkerConfig(workerInfo);
62 |
63 | if (this.options?.emulator) {
64 | dwConfig?.console?.info?.(`⚙️ Stopping emulator ${this.options.emulator.name(worker)}`);
65 | this.emulators[worker]?.child.kill();
66 | await this.emulators[worker]?.catch(dwConfig?.console?.error ?? console.error);
67 |
68 | dwConfig?.console?.info?.(`⚙️ Deleting AVD ${this.options.emulator.name(worker)}`);
69 | await execFile('avdmanager', ['delete', 'avd', '--name', this.options.emulator.name(worker)]);
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/docs/index/classes/Page.md:
--------------------------------------------------------------------------------
1 | [**@onslip/automation**](../../README.md) • **Docs**
2 |
3 | ***
4 |
5 | [@onslip/automation](../../README.md) / [index](../README.md) / Page
6 |
7 | # Class: Page
8 |
9 | The Page object represents the connection to a remote web view.
10 |
11 | ## Properties
12 |
13 | ### mouse
14 |
15 | > `readonly` **mouse**: [`Mouse`](Mouse.md)
16 |
17 | A reference to Mouse instance.
18 |
19 | #### Defined in
20 |
21 | [src/api.ts:145](https://github.com/Onslip/automation/blob/13befc40996d96bb2935315b372b921212adc8b4/src/api.ts#L145)
22 |
23 | ***
24 |
25 | ### touchscreen
26 |
27 | > `readonly` **touchscreen**: [`Touchscreen`](Touchscreen.md)
28 |
29 | A reference to Touchscreen instance.
30 |
31 | #### Defined in
32 |
33 | [src/api.ts:148](https://github.com/Onslip/automation/blob/13befc40996d96bb2935315b372b921212adc8b4/src/api.ts#L148)
34 |
35 | ## Methods
36 |
37 | ### close()
38 |
39 | > **close**(): `Promise`\<`void`\>
40 |
41 | Closes the connection to the web view and deallocates all held resources.
42 |
43 | #### Returns
44 |
45 | `Promise`\<`void`\>
46 |
47 | #### Defined in
48 |
49 | [src/api.ts:202](https://github.com/Onslip/automation/blob/13befc40996d96bb2935315b372b921212adc8b4/src/api.ts#L202)
50 |
51 | ***
52 |
53 | ### evaluate()
54 |
55 | > **evaluate**(`pageFunction`, `arg`?): `Promise`\<`unknown`\>
56 |
57 | Invokes a custom JavaScript function inside the web view context.
58 |
59 | #### Parameters
60 |
61 | • **pageFunction**: `string` \| `Function`
62 |
63 | A function to call inside the web view context.
64 |
65 | • **arg?**: `unknown`
66 |
67 | An optional argument to pass to the function. Must be JSON serializable.
68 |
69 | #### Returns
70 |
71 | `Promise`\<`unknown`\>
72 |
73 | The value returned from the function. Must be JSON serializable.
74 |
75 | #### Defined in
76 |
77 | [src/api.ts:177](https://github.com/Onslip/automation/blob/13befc40996d96bb2935315b372b921212adc8b4/src/api.ts#L177)
78 |
79 | ***
80 |
81 | ### locator()
82 |
83 | > **locator**(`selector`, `options`?): [`Locator`](Locator.md)
84 |
85 | Creates and returns a new Locator.
86 |
87 | #### Parameters
88 |
89 | • **selector**: `string`
90 |
91 | The selectors to match.
92 |
93 | • **options?**: [`LocatorOptions`](../interfaces/LocatorOptions.md)
94 |
95 | Additional options.
96 |
97 | #### Returns
98 |
99 | [`Locator`](Locator.md)
100 |
101 | A new Locator.
102 |
103 | #### Defined in
104 |
105 | [src/api.ts:166](https://github.com/Onslip/automation/blob/13befc40996d96bb2935315b372b921212adc8b4/src/api.ts#L166)
106 |
107 | ***
108 |
109 | ### setDebug()
110 |
111 | > **setDebug**(`debug`): `void`
112 |
113 | Enables to disables debug output from this library.
114 |
115 | #### Parameters
116 |
117 | • **debug**: `null` \| `boolean` \| `Partial`\<`Console`\>
118 |
119 | Use this `Console` for debugging, or set to `true` to use `console` and `false`/`null` to disable,
120 |
121 | #### Returns
122 |
123 | `void`
124 |
125 | #### Defined in
126 |
127 | [src/api.ts:186](https://github.com/Onslip/automation/blob/13befc40996d96bb2935315b372b921212adc8b4/src/api.ts#L186)
128 |
129 | ***
130 |
131 | ### setDefaultTimeout()
132 |
133 | > **setDefaultTimeout**(`timeout`): `void`
134 |
135 | Specifies the default timeout when waiting for Locators to be actionable.
136 |
137 | #### Parameters
138 |
139 | • **timeout**: `number`
140 |
141 | The new default timeout, in milliseconds.
142 |
143 | #### Returns
144 |
145 | `void`
146 |
147 | #### Defined in
148 |
149 | [src/api.ts:195](https://github.com/Onslip/automation/blob/13befc40996d96bb2935315b372b921212adc8b4/src/api.ts#L195)
150 |
--------------------------------------------------------------------------------
/tests/playwright.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig, devices } from '@playwright/test';
2 | import { DeviceWorkerLauncher, DeviceWorkerOptions, setFixtureTimeout } from '../src/test';
3 | import { AndroidLauncher } from './playwright.android.ts';
4 | import { iOSLauncher } from './playwright.ios.ts';
5 |
6 | // Launch an Android emulator and install a test app on it
7 | const androidLauncher = new AndroidLauncher({
8 | archive: __dirname + '/testapp/android/app/build/outputs/apk/debug/app-debug.apk',
9 | app: 'com.onslip.automation.testapp/.MainActivity',
10 |
11 | emulator: {
12 | name: (worker) => `automation-${worker}`,
13 | system: 'system-images;android-34;google_apis;arm64-v8a',
14 | device: '10.1in WXGA (Tablet)',
15 | },
16 | });
17 |
18 | // Launch an iOS simulator and install a test app on it (except that it doesn't work)
19 | const iosLauncher = new iOSLauncher({
20 | archive: __dirname + '/testapp/ios/App/DerivedData/App/Build/Products/Debug-iphonesimulator/App.app',
21 | app: 'com.onslip.automation.testapp',
22 |
23 | emulator: {
24 | name: (worker) => `automation-${worker}`,
25 | system: 'com.apple.CoreSimulator.SimRuntime.iOS-17-5',
26 | device: 'iPad Pro 11-inch (M4)',
27 | },
28 | });
29 |
30 | // Test an app that is already running on an external device (this works with both Android and iOS)
31 | const externalLauncher = new DeviceWorkerLauncher({
32 | devices: [ 'feba19fc98c93945dc42dd7f9940d658b5dd211b' ],
33 | })
34 |
35 | // Set fixture timeouts so that the launchers have enough time to start up
36 | setFixtureTimeout('device', 120_000);
37 | setFixtureTimeout('webApp', 60_000);
38 |
39 | export default defineConfig<{}, DeviceWorkerOptions>({
40 | webServer: {
41 | command: 'pnpm -C testapp run preview --port 3000 --strictPort',
42 | url: 'http://localhost:3000/',
43 | },
44 | use: {
45 | baseURL: 'http://localhost:3000/',
46 | headless: false,
47 | },
48 | projects: [{
49 | name: 'Desktop Chrome',
50 | use: { ...devices['Desktop Chrome'] },
51 | }, {
52 | name: 'Desktop Firefox',
53 | use: { ...devices['Desktop Firefox'] },
54 | }, {
55 | name: 'Desktop Safari',
56 | use: { ...devices['Desktop Safari'] },
57 | }, {
58 | name: 'Android WebApp',
59 | testMatch: '**/*.e2e.ts',
60 | use: { ...devices['Galaxy Tab S4 landscape'],
61 | deviceWorkerConfig: {
62 | console, // Debug launcher & automation
63 | launcher: androidLauncher,
64 | },
65 | },
66 | }, {
67 | name: 'iPad WebApp',
68 | testMatch: '**/*.e2e.ts',
69 | testIgnore: '*', // Project disabled!
70 | use: { ...devices['iPad Pro 11 landscape'],
71 | deviceWorkerConfig: {
72 | console, // Debug launcher & automation
73 | launcher: iosLauncher,
74 | }
75 | },
76 | }, {
77 | name: 'External WebApp',
78 | testMatch: '**/*.e2e.ts',
79 | testIgnore: '*', // Project disabled!
80 | use: { ...devices['Nexus 10'],
81 | deviceWorkerConfig: {
82 | console, // Debug launcher & automation
83 | launcher: externalLauncher,
84 | }
85 | },
86 | }],
87 | });
88 |
--------------------------------------------------------------------------------
/tests/playwright.ios.ts:
--------------------------------------------------------------------------------
1 | import { WorkerInfo } from '@playwright/test';
2 | import { execFile } from '../src';
3 | import { DeviceWorkerLauncher, DeviceWorkerLauncherOptions } from '../src/test';
4 |
5 | export interface iOSLauncherOptions extends DeviceWorkerLauncherOptions {
6 | emulator?: {
7 | name: (worker: number) => string,
8 | system: string,
9 | device: string,
10 | },
11 | }
12 |
13 | export class iOSLauncher extends DeviceWorkerLauncher {
14 | private simulators: (string | undefined)[] = [];
15 |
16 | override async setup(worker: number, headless: boolean, workerInfo: WorkerInfo) {
17 | const dwConfig = this.deviceWorkerConfig(workerInfo);
18 |
19 | if (this.options?.emulator) {
20 | dwConfig?.console?.info?.(`⚙️ Creating simulator ${this.options.emulator.name(worker)}`);
21 | const deviceId = this.simulators[worker] = (await execFile('xcrun', [
22 | 'simctl', 'create',
23 | this.options.emulator.name(worker),
24 | this.options.emulator.device,
25 | this.options.emulator.system
26 | ])).stdout.trim();
27 |
28 | dwConfig?.console?.info?.(`⚙️ Starting simulator ${this.options.emulator.name(worker)} as device ${deviceId}`);
29 | await execFile('xcrun', ['simctl', 'boot', deviceId]);
30 |
31 | if (!headless) {
32 | dwConfig?.console?.info?.(`⚙️ Opening simulator UI for device ${deviceId}`);
33 | await execFile('open', ['-a', 'Simulator', `--args`, `-CurrentDeviceUDID`, deviceId]).catch(dwConfig?.console?.error ?? console.error);
34 | }
35 |
36 | dwConfig?.console?.error?.(`⚙️ The web inspector socket is at ${await this.simulatorSocket(deviceId)},`);
37 | dwConfig?.console?.error?.(`⚙️ but since ios_webkit_debug_proxy doesn't detects iOS simulators properly, this is not really going to work ...`);
38 |
39 | return deviceId;
40 | } else {
41 | return super.setup(worker, headless, workerInfo);
42 | }
43 | }
44 |
45 | override async teardown(worker: number, workerInfo: WorkerInfo) {
46 | const dwConfig = this.deviceWorkerConfig(workerInfo);
47 |
48 | if (this.options?.emulator) {
49 | const deviceId = this.simulators[worker] ?? this.options.emulator.name(worker);
50 |
51 | dwConfig?.console?.info?.(`⚙️ Stopping simulator ${deviceId}`);
52 | await execFile('xcrun', ['simctl', 'shutdown', deviceId]).catch(dwConfig?.console?.error ?? console.error);
53 |
54 | dwConfig?.console?.info?.(`⚙️ Deleting simulator ${deviceId}`);
55 | await execFile('xcrun', ['simctl', 'delete', deviceId]);
56 | }
57 | }
58 |
59 | private async simulatorSocket(deviceId: string) {
60 | const lsof = (await execFile('lsof', ['-aUc', 'launchd_sim'])).stdout.split('\n');
61 | const proc = lsof.find((line) => RegExp(`com\.apple\.CoreSimulator\.SimDevice\.${deviceId}`).test(line))?.split(/\s+/)[1];
62 | const sock = lsof.find((line) => RegExp(`^[^ ]+\\s+${proc}\\s.*com\.apple\.webinspectord_sim\.socket`).test(line))?.split(/\s+/)[7];
63 |
64 | // Example lsof output:
65 | // launchd_s 88333 mb 4u unix 0xda3c088bc3fdbaf0 0t0 /tmp/com.apple.CoreSimulator.SimDevice.5B7EBC7F-6778-42E7-8CCC-56ACDC3C9ED0/syslogsock
66 | // launchd_s 88333 mb 5u unix 0x360627c6ecf0e28 0t0 /private/tmp/com.apple.launchd.VNtrfJseJr/com.apple.webinspectord_sim.socket
67 |
68 | if (!sock) {
69 | throw new Error(`Failed to find web inspector socket for simulator ${deviceId}`);
70 | }
71 |
72 | return sock;
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/tests/testapp/src/js/capacitor-welcome.js:
--------------------------------------------------------------------------------
1 | import { SplashScreen } from '@capacitor/splash-screen';
2 | import { Camera } from '@capacitor/camera';
3 |
4 | window.customElements.define(
5 | 'capacitor-welcome',
6 | class extends HTMLElement {
7 | constructor() {
8 | super();
9 |
10 | SplashScreen.hide();
11 |
12 | const root = this.attachShadow({ mode: 'open' });
13 |
14 | root.innerHTML = `
15 |
58 |
59 |
60 |
Capacitor
61 |
62 |
63 |
64 | Capacitor makes it easy to build powerful apps for the app stores, mobile web (Progressive Web Apps), and desktop, all
65 | with a single code base.
66 |
67 |
Getting Started
68 |
69 | You'll probably need a UI framework to build a full-featured app. Might we recommend
70 | Ionic?
71 |
72 |
73 | Visit capacitorjs.com for information
74 | on using native features, building plugins, and more.
75 |