appium 3.2.2 → 3.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/lib/appium.d.ts +147 -205
- package/build/lib/appium.d.ts.map +1 -1
- package/build/lib/appium.js +169 -282
- package/build/lib/appium.js.map +1 -1
- package/build/lib/bidi-commands.d.ts.map +1 -1
- package/build/lib/bidi-commands.js +11 -11
- package/build/lib/bidi-commands.js.map +1 -1
- package/build/lib/bootstrap/appium-initializer.d.ts +21 -0
- package/build/lib/bootstrap/appium-initializer.d.ts.map +1 -0
- package/build/lib/bootstrap/appium-initializer.js +146 -0
- package/build/lib/bootstrap/appium-initializer.js.map +1 -0
- package/build/lib/bootstrap/appium-main-runner.d.ts +22 -0
- package/build/lib/bootstrap/appium-main-runner.d.ts.map +1 -0
- package/build/lib/bootstrap/appium-main-runner.js +109 -0
- package/build/lib/bootstrap/appium-main-runner.js.map +1 -0
- package/build/lib/bootstrap/config-file.d.ts +37 -0
- package/build/lib/bootstrap/config-file.d.ts.map +1 -0
- package/build/lib/{config-file.js → bootstrap/config-file.js} +62 -138
- package/build/lib/bootstrap/config-file.js.map +1 -0
- package/build/lib/bootstrap/grid-v3-register.d.ts +20 -0
- package/build/lib/bootstrap/grid-v3-register.d.ts.map +1 -0
- package/build/lib/bootstrap/grid-v3-register.js +185 -0
- package/build/lib/bootstrap/grid-v3-register.js.map +1 -0
- package/build/lib/bootstrap/init-types.d.ts +16 -0
- package/build/lib/bootstrap/init-types.d.ts.map +1 -0
- package/build/lib/bootstrap/init-types.js +3 -0
- package/build/lib/bootstrap/init-types.js.map +1 -0
- package/build/lib/bootstrap/main-helpers.d.ts +55 -0
- package/build/lib/bootstrap/main-helpers.d.ts.map +1 -0
- package/build/lib/bootstrap/main-helpers.js +187 -0
- package/build/lib/bootstrap/main-helpers.js.map +1 -0
- package/build/lib/bootstrap/node-helpers.d.ts +32 -0
- package/build/lib/bootstrap/node-helpers.d.ts.map +1 -0
- package/build/lib/bootstrap/node-helpers.js +201 -0
- package/build/lib/bootstrap/node-helpers.js.map +1 -0
- package/build/lib/bootstrap/startup-config.d.ts +22 -0
- package/build/lib/bootstrap/startup-config.d.ts.map +1 -0
- package/build/lib/bootstrap/startup-config.js +111 -0
- package/build/lib/bootstrap/startup-config.js.map +1 -0
- package/build/lib/cli/args.d.ts +16 -12
- package/build/lib/cli/args.d.ts.map +1 -1
- package/build/lib/cli/args.js +20 -40
- package/build/lib/cli/args.js.map +1 -1
- package/build/lib/cli/driver-command.d.ts +51 -93
- package/build/lib/cli/driver-command.d.ts.map +1 -1
- package/build/lib/cli/driver-command.js +11 -66
- package/build/lib/cli/driver-command.js.map +1 -1
- package/build/lib/cli/extension-command.d.ts +173 -377
- package/build/lib/cli/extension-command.d.ts.map +1 -1
- package/build/lib/cli/extension-command.js +387 -656
- package/build/lib/cli/extension-command.js.map +1 -1
- package/build/lib/cli/extension.d.ts +10 -15
- package/build/lib/cli/extension.d.ts.map +1 -1
- package/build/lib/cli/extension.js +15 -33
- package/build/lib/cli/extension.js.map +1 -1
- package/build/lib/cli/parser.d.ts +37 -66
- package/build/lib/cli/parser.d.ts.map +1 -1
- package/build/lib/cli/parser.js +69 -104
- package/build/lib/cli/parser.js.map +1 -1
- package/build/lib/cli/plugin-command.d.ts +50 -90
- package/build/lib/cli/plugin-command.d.ts.map +1 -1
- package/build/lib/cli/plugin-command.js +11 -63
- package/build/lib/cli/plugin-command.js.map +1 -1
- package/build/lib/cli/setup-command.d.ts +21 -26
- package/build/lib/cli/setup-command.d.ts.map +1 -1
- package/build/lib/cli/setup-command.js +19 -61
- package/build/lib/cli/setup-command.js.map +1 -1
- package/build/lib/cli/utils.d.ts +33 -35
- package/build/lib/cli/utils.d.ts.map +1 -1
- package/build/lib/cli/utils.js +48 -50
- package/build/lib/cli/utils.js.map +1 -1
- package/build/lib/constants.d.ts +23 -23
- package/build/lib/constants.d.ts.map +1 -1
- package/build/lib/constants.js +10 -15
- package/build/lib/constants.js.map +1 -1
- package/build/lib/doctor/doctor.d.ts +40 -57
- package/build/lib/doctor/doctor.d.ts.map +1 -1
- package/build/lib/doctor/doctor.js +31 -62
- package/build/lib/doctor/doctor.js.map +1 -1
- package/build/lib/extension/driver-config.d.ts +18 -77
- package/build/lib/extension/driver-config.d.ts.map +1 -1
- package/build/lib/extension/driver-config.js +37 -125
- package/build/lib/extension/driver-config.js.map +1 -1
- package/build/lib/extension/extension-config.d.ts +103 -210
- package/build/lib/extension/extension-config.d.ts.map +1 -1
- package/build/lib/extension/extension-config.js +180 -342
- package/build/lib/extension/extension-config.js.map +1 -1
- package/build/lib/extension/index.d.ts +12 -29
- package/build/lib/extension/index.d.ts.map +1 -1
- package/build/lib/extension/index.js +33 -75
- package/build/lib/extension/index.js.map +1 -1
- package/build/lib/extension/manifest-migrations.d.ts +3 -20
- package/build/lib/extension/manifest-migrations.d.ts.map +1 -1
- package/build/lib/extension/manifest-migrations.js +20 -101
- package/build/lib/extension/manifest-migrations.js.map +1 -1
- package/build/lib/extension/manifest.d.ts +61 -107
- package/build/lib/extension/manifest.d.ts.map +1 -1
- package/build/lib/extension/manifest.js +181 -356
- package/build/lib/extension/manifest.js.map +1 -1
- package/build/lib/extension/package-changed.d.ts +1 -3
- package/build/lib/extension/package-changed.d.ts.map +1 -1
- package/build/lib/extension/package-changed.js +8 -15
- package/build/lib/extension/package-changed.js.map +1 -1
- package/build/lib/extension/plugin-config.d.ts +10 -52
- package/build/lib/extension/plugin-config.d.ts.map +1 -1
- package/build/lib/extension/plugin-config.js +11 -63
- package/build/lib/extension/plugin-config.js.map +1 -1
- package/build/lib/helpers/build.d.ts +22 -0
- package/build/lib/helpers/build.d.ts.map +1 -0
- package/build/lib/helpers/build.js +109 -0
- package/build/lib/helpers/build.js.map +1 -0
- package/build/lib/helpers/capability.d.ts +38 -0
- package/build/lib/helpers/capability.d.ts.map +1 -0
- package/build/lib/helpers/capability.js +128 -0
- package/build/lib/helpers/capability.js.map +1 -0
- package/build/lib/helpers/network.d.ts +14 -0
- package/build/lib/helpers/network.d.ts.map +1 -0
- package/build/lib/helpers/network.js +35 -0
- package/build/lib/helpers/network.js.map +1 -0
- package/build/lib/insecure-features.js +6 -6
- package/build/lib/insecure-features.js.map +1 -1
- package/build/lib/inspector-commands.d.ts +6 -0
- package/build/lib/inspector-commands.d.ts.map +1 -1
- package/build/lib/inspector-commands.js +6 -0
- package/build/lib/inspector-commands.js.map +1 -1
- package/build/lib/logger.d.ts +2 -3
- package/build/lib/logger.d.ts.map +1 -1
- package/build/lib/logger.js +2 -3
- package/build/lib/logger.js.map +1 -1
- package/build/lib/logsink.d.ts +13 -22
- package/build/lib/logsink.d.ts.map +1 -1
- package/build/lib/logsink.js +48 -103
- package/build/lib/logsink.js.map +1 -1
- package/build/lib/main.d.ts +15 -58
- package/build/lib/main.d.ts.map +1 -1
- package/build/lib/main.js +25 -425
- package/build/lib/main.js.map +1 -1
- package/build/lib/schema/arg-spec.d.ts +32 -107
- package/build/lib/schema/arg-spec.d.ts.map +1 -1
- package/build/lib/schema/arg-spec.js +11 -107
- package/build/lib/schema/arg-spec.js.map +1 -1
- package/build/lib/schema/cli-args-guards.d.ts +34 -0
- package/build/lib/schema/cli-args-guards.d.ts.map +1 -0
- package/build/lib/schema/cli-args-guards.js +49 -0
- package/build/lib/schema/cli-args-guards.js.map +1 -0
- package/build/lib/schema/cli-args.d.ts +3 -15
- package/build/lib/schema/cli-args.d.ts.map +1 -1
- package/build/lib/schema/cli-args.js +17 -107
- package/build/lib/schema/cli-args.js.map +1 -1
- package/build/lib/schema/cli-transformers.d.ts +15 -12
- package/build/lib/schema/cli-transformers.d.ts.map +1 -1
- package/build/lib/schema/cli-transformers.js +15 -45
- package/build/lib/schema/cli-transformers.js.map +1 -1
- package/build/lib/schema/format-errors.d.ts +28 -0
- package/build/lib/schema/format-errors.d.ts.map +1 -0
- package/build/lib/schema/format-errors.js +29 -0
- package/build/lib/schema/format-errors.js.map +1 -0
- package/build/lib/schema/index.d.ts +4 -2
- package/build/lib/schema/index.d.ts.map +1 -1
- package/build/lib/schema/index.js +2 -0
- package/build/lib/schema/index.js.map +1 -1
- package/build/lib/schema/keywords.d.ts +12 -20
- package/build/lib/schema/keywords.d.ts.map +1 -1
- package/build/lib/schema/keywords.js +6 -51
- package/build/lib/schema/keywords.js.map +1 -1
- package/build/lib/schema/schema.d.ts +106 -231
- package/build/lib/schema/schema.d.ts.map +1 -1
- package/build/lib/schema/schema.js +88 -358
- package/build/lib/schema/schema.js.map +1 -1
- package/build/lib/utils.d.ts +7 -267
- package/build/lib/utils.d.ts.map +1 -1
- package/build/lib/utils.js +10 -409
- package/build/lib/utils.js.map +1 -1
- package/lib/{appium.js → appium.ts} +297 -341
- package/lib/bidi-commands.ts +10 -14
- package/lib/bootstrap/appium-initializer.ts +212 -0
- package/lib/bootstrap/appium-main-runner.ts +172 -0
- package/lib/bootstrap/config-file.ts +178 -0
- package/lib/bootstrap/grid-v3-register.ts +250 -0
- package/lib/bootstrap/init-types.ts +31 -0
- package/lib/bootstrap/main-helpers.ts +223 -0
- package/lib/bootstrap/node-helpers.ts +180 -0
- package/lib/bootstrap/startup-config.ts +143 -0
- package/lib/cli/{args.js → args.ts} +45 -56
- package/lib/cli/driver-command.ts +122 -0
- package/lib/cli/{extension-command.js → extension-command.ts} +827 -906
- package/lib/cli/extension.ts +65 -0
- package/lib/cli/{parser.js → parser.ts} +93 -116
- package/lib/cli/plugin-command.ts +117 -0
- package/lib/cli/{setup-command.js → setup-command.ts} +59 -74
- package/lib/cli/utils.ts +97 -0
- package/lib/{constants.js → constants.ts} +30 -41
- package/lib/doctor/{doctor.js → doctor.ts} +82 -92
- package/lib/extension/driver-config.ts +165 -0
- package/lib/extension/{extension-config.js → extension-config.ts} +291 -405
- package/lib/extension/index.ts +143 -0
- package/lib/extension/manifest-migrations.ts +57 -0
- package/lib/extension/manifest.ts +369 -0
- package/lib/extension/{package-changed.js → package-changed.ts} +9 -18
- package/lib/extension/plugin-config.ts +62 -0
- package/lib/helpers/build.ts +111 -0
- package/lib/helpers/capability.ts +171 -0
- package/lib/helpers/network.ts +30 -0
- package/lib/insecure-features.ts +1 -1
- package/lib/inspector-commands.ts +6 -1
- package/lib/{logger.js → logger.ts} +1 -2
- package/lib/{logsink.js → logsink.ts} +91 -137
- package/lib/main.ts +60 -0
- package/lib/schema/arg-spec.ts +131 -0
- package/lib/schema/cli-args-guards.ts +67 -0
- package/lib/schema/cli-args.ts +171 -0
- package/lib/schema/cli-transformers.ts +83 -0
- package/lib/schema/format-errors.ts +43 -0
- package/lib/schema/index.ts +4 -0
- package/lib/schema/keywords.ts +96 -0
- package/lib/schema/schema.ts +448 -0
- package/lib/utils.ts +73 -0
- package/package.json +17 -18
- package/scripts/autoinstall-extensions.js +3 -0
- package/build/lib/config-file.d.ts +0 -100
- package/build/lib/config-file.d.ts.map +0 -1
- package/build/lib/config-file.js.map +0 -1
- package/build/lib/config.d.ts +0 -70
- package/build/lib/config.d.ts.map +0 -1
- package/build/lib/config.js +0 -390
- package/build/lib/config.js.map +0 -1
- package/build/lib/grid-register.d.ts +0 -10
- package/build/lib/grid-register.d.ts.map +0 -1
- package/build/lib/grid-register.js +0 -134
- package/build/lib/grid-register.js.map +0 -1
- package/lib/cli/driver-command.js +0 -174
- package/lib/cli/extension.js +0 -74
- package/lib/cli/plugin-command.js +0 -164
- package/lib/cli/utils.js +0 -91
- package/lib/config-file.js +0 -228
- package/lib/config.js +0 -389
- package/lib/extension/driver-config.js +0 -245
- package/lib/extension/index.js +0 -169
- package/lib/extension/manifest-migrations.js +0 -136
- package/lib/extension/manifest.js +0 -550
- package/lib/extension/plugin-config.js +0 -112
- package/lib/grid-register.js +0 -146
- package/lib/main.js +0 -545
- package/lib/schema/arg-spec.js +0 -229
- package/lib/schema/cli-args.js +0 -254
- package/lib/schema/cli-transformers.js +0 -113
- package/lib/schema/index.js +0 -2
- package/lib/schema/keywords.js +0 -136
- package/lib/schema/schema.js +0 -725
- package/lib/utils.js +0 -512
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import _ from 'lodash';
|
|
2
|
+
import B from 'bluebird';
|
|
3
|
+
import type {DriverClass, ExtensionType, PluginClass} from '@appium/types';
|
|
4
|
+
import type {ExtClass} from 'appium/types';
|
|
5
|
+
import {USE_ALL_PLUGINS} from '../constants';
|
|
6
|
+
import {log} from '../logger';
|
|
7
|
+
import {DriverConfig} from './driver-config';
|
|
8
|
+
import {Manifest} from './manifest';
|
|
9
|
+
import {timing} from '@appium/support';
|
|
10
|
+
import {PluginConfig} from './plugin-config';
|
|
11
|
+
|
|
12
|
+
export type ExtensionConfigs = {
|
|
13
|
+
driverConfig: DriverConfig;
|
|
14
|
+
pluginConfig: PluginConfig;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type PluginNameMap = Map<PluginClass, string>;
|
|
18
|
+
export type DriverNameMap = Map<DriverClass, string>;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Loads extensions and creates `ExtensionConfig` instances.
|
|
22
|
+
*
|
|
23
|
+
* - Reads the manifest file, creating if necessary
|
|
24
|
+
* - Using the parsed extension data, creates/gets the `ExtensionConfig` subclass instances
|
|
25
|
+
* - Returns these instances
|
|
26
|
+
*
|
|
27
|
+
* If `appiumHome` is needed, use `resolveAppiumHome` from the `env` module in `@appium/support`.
|
|
28
|
+
*/
|
|
29
|
+
export async function loadExtensions(appiumHome: string): Promise<ExtensionConfigs> {
|
|
30
|
+
const manifest = Manifest.getInstance(appiumHome);
|
|
31
|
+
await manifest.read();
|
|
32
|
+
const driverConfig = DriverConfig.getInstance(manifest) ?? DriverConfig.create(manifest);
|
|
33
|
+
const pluginConfig = PluginConfig.getInstance(manifest) ?? PluginConfig.create(manifest);
|
|
34
|
+
|
|
35
|
+
await B.all([driverConfig.validate(), pluginConfig.validate()]);
|
|
36
|
+
return {driverConfig, pluginConfig};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Find any plugin name which has been installed, and which has been requested for activation by
|
|
41
|
+
* using the --use-plugins flag, and turn each one into its class, so we can send them as objects
|
|
42
|
+
* to the server init. We also want to send/assign them to the umbrella driver so it can use them
|
|
43
|
+
* to wrap command execution
|
|
44
|
+
*/
|
|
45
|
+
export async function getActivePlugins(
|
|
46
|
+
pluginConfig: PluginConfig,
|
|
47
|
+
maxParallelImports: number,
|
|
48
|
+
usePlugins: string[] = []
|
|
49
|
+
): Promise<PluginNameMap> {
|
|
50
|
+
if (_.isEmpty(usePlugins)) {
|
|
51
|
+
return new Map();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
let filteredPluginNames: string[] = [];
|
|
55
|
+
if (usePlugins.length === 1 && usePlugins[0] === USE_ALL_PLUGINS) {
|
|
56
|
+
filteredPluginNames = _.keys(pluginConfig.installedExtensions);
|
|
57
|
+
} else {
|
|
58
|
+
for (const pluginName of usePlugins) {
|
|
59
|
+
if (pluginName in pluginConfig.installedExtensions) {
|
|
60
|
+
filteredPluginNames.push(pluginName);
|
|
61
|
+
} else if (pluginName === USE_ALL_PLUGINS) {
|
|
62
|
+
throw new Error(`The reserved plugin name '${pluginName}' cannot be combined with other names.`);
|
|
63
|
+
} else {
|
|
64
|
+
const suffix = _.isEmpty(pluginConfig.installedExtensions)
|
|
65
|
+
? `You don't have any plugins installed yet.`
|
|
66
|
+
: `Only the following ${_.size(pluginConfig.installedExtensions) === 1 ? `plugin is` : `plugins are`} ` +
|
|
67
|
+
`available: ${_.keys(pluginConfig.installedExtensions)}`;
|
|
68
|
+
throw new Error(`Could not load the plugin '${pluginName}' because it is not installed. ${suffix}`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const pairs = await importExtensions('plugin', pluginConfig, filteredPluginNames, maxParallelImports);
|
|
73
|
+
return new Map(pairs as Array<[PluginClass, string]>);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Find any driver name which has been installed, and turn each one into its class, so we can send
|
|
78
|
+
* them as objects to the server init in case they need to add methods/routes or update the server.
|
|
79
|
+
* If the --drivers flag was given, this method only loads the given drivers.
|
|
80
|
+
*/
|
|
81
|
+
export async function getActiveDrivers(
|
|
82
|
+
driverConfig: DriverConfig,
|
|
83
|
+
maxParallelImports: number,
|
|
84
|
+
useDrivers: string[] = []
|
|
85
|
+
): Promise<DriverNameMap> {
|
|
86
|
+
let filteredDriverNames: string[] = [];
|
|
87
|
+
if (_.isEmpty(useDrivers)) {
|
|
88
|
+
filteredDriverNames = _.keys(driverConfig.installedExtensions);
|
|
89
|
+
} else {
|
|
90
|
+
for (const driverName of useDrivers) {
|
|
91
|
+
if (driverName in driverConfig.installedExtensions) {
|
|
92
|
+
filteredDriverNames.push(driverName);
|
|
93
|
+
} else {
|
|
94
|
+
const suffix = _.isEmpty(driverConfig.installedExtensions)
|
|
95
|
+
? `You don't have any drivers installed yet.`
|
|
96
|
+
: `Only the following ${_.size(driverConfig.installedExtensions) === 1 ? `driver is` : `drivers are`} ` +
|
|
97
|
+
`available: ${_.keys(driverConfig.installedExtensions)}`;
|
|
98
|
+
throw new Error(`Could not load the driver '${driverName}' because it is not installed. ${suffix}`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
const pairs = await importExtensions('driver', driverConfig, filteredDriverNames, maxParallelImports);
|
|
103
|
+
return new Map(pairs as Array<[DriverClass, string]>);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
async function importExtensions(
|
|
108
|
+
extType: 'driver' | 'plugin',
|
|
109
|
+
config: DriverConfig | PluginConfig,
|
|
110
|
+
extNames: string[],
|
|
111
|
+
asyncImportChunkSize: number
|
|
112
|
+
): Promise<Array<[ExtClass<ExtensionType>, string]>> {
|
|
113
|
+
const allPromises: Array<B<ExtClass<ExtensionType> | undefined>> = [];
|
|
114
|
+
const activePromisesChunk: Array<B<ExtClass<ExtensionType> | undefined>> = [];
|
|
115
|
+
for (const extName of extNames) {
|
|
116
|
+
_.remove(activePromisesChunk, (p) => p.isFulfilled());
|
|
117
|
+
if (activePromisesChunk.length >= asyncImportChunkSize) {
|
|
118
|
+
await B.any(activePromisesChunk);
|
|
119
|
+
}
|
|
120
|
+
const promise = B.resolve(
|
|
121
|
+
(async () => {
|
|
122
|
+
log.info(`Attempting to load ${extType} ${extName}...`);
|
|
123
|
+
const timer = new timing.Timer().start();
|
|
124
|
+
try {
|
|
125
|
+
const extClass = await config.requireAsync(extName as never);
|
|
126
|
+
log.debug(`${extClass.name} has been successfully loaded in ${timer.getDuration().asSeconds.toFixed(3)}s`);
|
|
127
|
+
return extClass as ExtClass<ExtensionType>;
|
|
128
|
+
} catch (err: any) {
|
|
129
|
+
log.error(
|
|
130
|
+
`Could not load ${extType} '${extName}', so it will not be available. Error ` +
|
|
131
|
+
`in loading the ${extType} was: ${err.message}`
|
|
132
|
+
);
|
|
133
|
+
log.debug(err.stack);
|
|
134
|
+
}
|
|
135
|
+
})()
|
|
136
|
+
);
|
|
137
|
+
activePromisesChunk.push(promise);
|
|
138
|
+
allPromises.push(promise);
|
|
139
|
+
}
|
|
140
|
+
return _.zip(await B.all(allPromises), extNames).filter(([extClass]) => Boolean(extClass)) as Array<
|
|
141
|
+
[ExtClass<ExtensionType>, string]
|
|
142
|
+
>;
|
|
143
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type {ManifestDataVersions} from 'appium/types';
|
|
2
|
+
import {CURRENT_SCHEMA_REV, DRIVER_TYPE, PLUGIN_TYPE} from '../constants';
|
|
3
|
+
import {log} from '../logger';
|
|
4
|
+
import type {Manifest} from './manifest';
|
|
5
|
+
|
|
6
|
+
const SCHEMA_REV_3 = 3;
|
|
7
|
+
const SCHEMA_REV_4 = 4;
|
|
8
|
+
|
|
9
|
+
type Migration = (manifest: Manifest) => boolean | Promise<boolean>;
|
|
10
|
+
|
|
11
|
+
const Migrations: Partial<Record<keyof ManifestDataVersions, Migration>> = {
|
|
12
|
+
[SCHEMA_REV_3]: (manifest) => {
|
|
13
|
+
const drivers = manifest.getExtensionData(DRIVER_TYPE);
|
|
14
|
+
const plugins = manifest.getExtensionData(PLUGIN_TYPE);
|
|
15
|
+
const allExtData = [...Object.values(drivers), ...Object.values(plugins)];
|
|
16
|
+
return allExtData.some((metadata) => !('installPath' in metadata));
|
|
17
|
+
},
|
|
18
|
+
[SCHEMA_REV_4]: (manifest) => {
|
|
19
|
+
if (manifest.schemaRev < SCHEMA_REV_4) {
|
|
20
|
+
const drivers = manifest.getExtensionData(DRIVER_TYPE);
|
|
21
|
+
const plugins = manifest.getExtensionData(PLUGIN_TYPE);
|
|
22
|
+
const allExtData = [...Object.values(drivers), ...Object.values(plugins)];
|
|
23
|
+
return allExtData.some((metadata) => metadata.installType === 'npm');
|
|
24
|
+
}
|
|
25
|
+
return false;
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Applies a series of migration functions to a manifest to update its manifest schema version.
|
|
31
|
+
*
|
|
32
|
+
* `data` is modified in-place.
|
|
33
|
+
*
|
|
34
|
+
* @returns If `true` existing packages should be synced from disk and the manifest should be persisted.
|
|
35
|
+
*/
|
|
36
|
+
export async function migrate(manifest: Manifest): Promise<boolean> {
|
|
37
|
+
let didChange = false;
|
|
38
|
+
for (const migration of Object.values(Migrations)) {
|
|
39
|
+
if (migration) {
|
|
40
|
+
didChange = (await migration(manifest)) || didChange;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
didChange = setSchemaRev(manifest, CURRENT_SCHEMA_REV) || didChange;
|
|
44
|
+
if (didChange) {
|
|
45
|
+
log.debug(`Upgraded extension manifest to schema v${manifest.schemaRev}`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return didChange;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function setSchemaRev(manifest: Manifest, version: number): boolean {
|
|
52
|
+
if ((manifest.schemaRev ?? 0) < version) {
|
|
53
|
+
manifest.setSchemaRev(version);
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
import B from 'bluebird';
|
|
2
|
+
import {env, fs} from '@appium/support';
|
|
3
|
+
import _ from 'lodash';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import * as YAML from 'yaml';
|
|
6
|
+
import type {DriverType, ExtensionType, PluginType} from '@appium/types';
|
|
7
|
+
import type {ExtManifest, ExtPackageJson, ExtRecord, InternalMetadata, ManifestData} from 'appium/types';
|
|
8
|
+
import {CURRENT_SCHEMA_REV, DRIVER_TYPE, PLUGIN_TYPE} from '../constants';
|
|
9
|
+
import {INSTALL_TYPE_DEV, INSTALL_TYPE_NPM} from './extension-config';
|
|
10
|
+
import {packageDidChange} from './package-changed';
|
|
11
|
+
import {migrate} from './manifest-migrations';
|
|
12
|
+
|
|
13
|
+
const CONFIG_DATA_DRIVER_KEY = `${DRIVER_TYPE}s` as const;
|
|
14
|
+
const CONFIG_DATA_PLUGIN_KEY = `${PLUGIN_TYPE}s` as const;
|
|
15
|
+
|
|
16
|
+
const INITIAL_MANIFEST_DATA: Readonly<ManifestData> = Object.freeze({
|
|
17
|
+
[CONFIG_DATA_DRIVER_KEY]: Object.freeze({}),
|
|
18
|
+
[CONFIG_DATA_PLUGIN_KEY]: Object.freeze({}),
|
|
19
|
+
schemaRev: CURRENT_SCHEMA_REV,
|
|
20
|
+
}) as Readonly<ManifestData>;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Handles reading & writing of extension config files.
|
|
24
|
+
*
|
|
25
|
+
* Only one instance of this class exists per value of `APPIUM_HOME`.
|
|
26
|
+
*/
|
|
27
|
+
export class Manifest {
|
|
28
|
+
/**
|
|
29
|
+
* Returns the memoized manifest for an `APPIUM_HOME` directory (one instance per home).
|
|
30
|
+
*
|
|
31
|
+
* @param appiumHome - `APPIUM_HOME` path used as the cache key
|
|
32
|
+
*/
|
|
33
|
+
static getInstance = _.memoize((appiumHome: string): Manifest => new Manifest(appiumHome));
|
|
34
|
+
|
|
35
|
+
#data!: ManifestData;
|
|
36
|
+
readonly #appiumHome: string;
|
|
37
|
+
#manifestPath: string | undefined = undefined;
|
|
38
|
+
#writing: Promise<boolean> | undefined;
|
|
39
|
+
#reading: Promise<void> | undefined;
|
|
40
|
+
|
|
41
|
+
private constructor(appiumHome: string) {
|
|
42
|
+
this.#appiumHome = appiumHome;
|
|
43
|
+
this.#data = _.cloneDeep(INITIAL_MANIFEST_DATA) as ManifestData;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** `APPIUM_HOME` directory this manifest is tied to. */
|
|
47
|
+
get appiumHome(): string {
|
|
48
|
+
return this.#appiumHome;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Absolute path to the extension manifest file after {@link Manifest.read} or {@link Manifest.write} has resolved it.
|
|
53
|
+
* Before that, this is `undefined`.
|
|
54
|
+
*/
|
|
55
|
+
get manifestPath(): string | undefined {
|
|
56
|
+
return this.#manifestPath;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Schema revision of the in-memory manifest data (from YAML `schemaRev`). */
|
|
60
|
+
get schemaRev(): number {
|
|
61
|
+
return this.#data.schemaRev;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Returns the live installed-extension map for drivers or plugins (same object as stored in memory).
|
|
66
|
+
* Mutations affect the manifest until replaced by a new object (e.g. via `read()`); `setExtension` / `deleteExtension` update this record.
|
|
67
|
+
*
|
|
68
|
+
* @param extType - `"driver"` or `"plugin"`
|
|
69
|
+
*/
|
|
70
|
+
getExtensionData<ExtType extends ExtensionType>(extType: ExtType): ExtRecord<ExtType> {
|
|
71
|
+
const record = extType === DRIVER_TYPE ? this.#data.drivers : this.#data.plugins;
|
|
72
|
+
return record as ExtRecord<ExtType>;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Whether a driver with the given manifest key is present.
|
|
77
|
+
*
|
|
78
|
+
* @param name - Driver name as stored under `drivers` in the manifest
|
|
79
|
+
*/
|
|
80
|
+
hasDriver(name: string): boolean {
|
|
81
|
+
return Boolean(this.#data.drivers[name]);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Whether a plugin with the given manifest key is present.
|
|
86
|
+
*
|
|
87
|
+
* @param name - Plugin name as stored under `plugins` in the manifest
|
|
88
|
+
*/
|
|
89
|
+
hasPlugin(name: string): boolean {
|
|
90
|
+
return Boolean(this.#data.plugins[name]);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Loads manifest YAML from disk into memory, runs migration when needed, may sync with installed packages, and writes back if required.
|
|
95
|
+
* Concurrent calls while a read is in flight share the same in-flight work.
|
|
96
|
+
*
|
|
97
|
+
* @returns The parsed in-memory manifest data
|
|
98
|
+
*/
|
|
99
|
+
async read(): Promise<ManifestData> {
|
|
100
|
+
if (this.#reading) {
|
|
101
|
+
await this.#reading;
|
|
102
|
+
return this.#data;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
this.#reading = (async () => {
|
|
106
|
+
let data: ManifestData;
|
|
107
|
+
let shouldWrite = false;
|
|
108
|
+
const manifestPathResolved = await this.#setManifestPath();
|
|
109
|
+
try {
|
|
110
|
+
const yaml = await fs.readFile(manifestPathResolved, 'utf8');
|
|
111
|
+
data = YAML.parse(yaml) as ManifestData;
|
|
112
|
+
} catch (err: any) {
|
|
113
|
+
if (err.code === 'ENOENT') {
|
|
114
|
+
data = _.cloneDeep(INITIAL_MANIFEST_DATA) as ManifestData;
|
|
115
|
+
shouldWrite = true;
|
|
116
|
+
} else {
|
|
117
|
+
throw new Error(
|
|
118
|
+
`Appium had trouble loading the extension installation ` +
|
|
119
|
+
`cache file (${manifestPathResolved}). It may be invalid YAML. Specific error: ${
|
|
120
|
+
err.message
|
|
121
|
+
}`
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
this.#data = data;
|
|
127
|
+
|
|
128
|
+
if (!shouldWrite && (data.schemaRev ?? 0) < CURRENT_SCHEMA_REV) {
|
|
129
|
+
shouldWrite = await migrate(this);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const hasAppiumDependency = await env.hasAppiumDependency(this.appiumHome);
|
|
133
|
+
|
|
134
|
+
if (shouldWrite || (hasAppiumDependency && (await packageDidChange(this.appiumHome)))) {
|
|
135
|
+
shouldWrite = (await this.syncWithInstalledExtensions(hasAppiumDependency)) || shouldWrite;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (shouldWrite) {
|
|
139
|
+
await this.write();
|
|
140
|
+
}
|
|
141
|
+
})();
|
|
142
|
+
|
|
143
|
+
try {
|
|
144
|
+
await this.#reading;
|
|
145
|
+
return this.#data;
|
|
146
|
+
} finally {
|
|
147
|
+
this.#reading = undefined;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Serializes the current in-memory manifest to the resolved manifest path.
|
|
153
|
+
* Concurrent calls while a write is in flight share the same in-flight work.
|
|
154
|
+
*
|
|
155
|
+
* @returns `true` when the file was written successfully
|
|
156
|
+
*/
|
|
157
|
+
async write(): Promise<boolean> {
|
|
158
|
+
if (this.#writing) {
|
|
159
|
+
return this.#writing;
|
|
160
|
+
}
|
|
161
|
+
this.#writing = (async () => {
|
|
162
|
+
const manifestPathResolved = await this.#setManifestPath();
|
|
163
|
+
try {
|
|
164
|
+
await fs.mkdirp(path.dirname(manifestPathResolved));
|
|
165
|
+
} catch (err: any) {
|
|
166
|
+
throw new Error(
|
|
167
|
+
`Appium could not create the directory for the manifest file: ${path.dirname(
|
|
168
|
+
manifestPathResolved
|
|
169
|
+
)}. Original error: ${err.message}`
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
try {
|
|
173
|
+
await fs.writeFile(manifestPathResolved, YAML.stringify(this.#data), 'utf8');
|
|
174
|
+
return true;
|
|
175
|
+
} catch (err: any) {
|
|
176
|
+
throw new Error(
|
|
177
|
+
`Appium could not write to manifest at ${manifestPathResolved} using APPIUM_HOME ${
|
|
178
|
+
this.#appiumHome
|
|
179
|
+
}. Please ensure it is writable. Original error: ${err.message}`
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
})();
|
|
183
|
+
try {
|
|
184
|
+
return await this.#writing;
|
|
185
|
+
} finally {
|
|
186
|
+
this.#writing = undefined;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Scans `APPIUM_HOME` (root and `node_modules`) for Appium extension packages and merges them into the manifest.
|
|
192
|
+
*
|
|
193
|
+
* @param hasAppiumDependency - When true and the root `package.json` depends on Appium, matching extensions use the `"dev"` install type
|
|
194
|
+
* @returns `true` if any extension entries changed, `false` otherwise
|
|
195
|
+
*/
|
|
196
|
+
async syncWithInstalledExtensions(hasAppiumDependency = false): Promise<boolean> {
|
|
197
|
+
let didChange = false;
|
|
198
|
+
|
|
199
|
+
const onMatch = async (filepath: string, devType = false): Promise<void> => {
|
|
200
|
+
try {
|
|
201
|
+
const pkg = JSON.parse(await fs.readFile(filepath, 'utf8')) as unknown;
|
|
202
|
+
if (isExtension(pkg)) {
|
|
203
|
+
const installType = devType && hasAppiumDependency ? INSTALL_TYPE_DEV : INSTALL_TYPE_NPM;
|
|
204
|
+
const changed = this.addExtensionFromPackage(pkg, filepath, installType);
|
|
205
|
+
didChange = didChange || changed;
|
|
206
|
+
}
|
|
207
|
+
} catch {
|
|
208
|
+
// ignore invalid package.json
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
const queue: Promise<void>[] = [
|
|
213
|
+
onMatch(path.join(this.#appiumHome, 'package.json'), true),
|
|
214
|
+
];
|
|
215
|
+
|
|
216
|
+
const filepaths = await fs.glob('node_modules/{*,@*/*}/package.json', {
|
|
217
|
+
cwd: this.#appiumHome,
|
|
218
|
+
absolute: true,
|
|
219
|
+
});
|
|
220
|
+
for (const filepath of filepaths) {
|
|
221
|
+
queue.push(onMatch(filepath));
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
await B.all(queue);
|
|
225
|
+
|
|
226
|
+
return didChange;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Builds manifest metadata from a `package.json` and registers it if it is a driver or plugin and the entry changed.
|
|
231
|
+
*
|
|
232
|
+
* @param pkgJson - Parsed extension `package.json`
|
|
233
|
+
* @param pkgPath - Path to that `package.json` (install path is derived from its directory)
|
|
234
|
+
* @param installType - How the package was discovered (`npm` vs `dev`)
|
|
235
|
+
* @returns `true` if the manifest was updated, `false` if unchanged or already matched
|
|
236
|
+
* @throws TypeError if the package is not a valid driver or plugin extension
|
|
237
|
+
*/
|
|
238
|
+
addExtensionFromPackage(
|
|
239
|
+
pkgJson: ExtPackageJson<ExtensionType>,
|
|
240
|
+
pkgPath: string,
|
|
241
|
+
installType: typeof INSTALL_TYPE_NPM | typeof INSTALL_TYPE_DEV = INSTALL_TYPE_NPM
|
|
242
|
+
): boolean {
|
|
243
|
+
const extensionPath = path.dirname(pkgPath);
|
|
244
|
+
|
|
245
|
+
const internal: InternalMetadata = {
|
|
246
|
+
pkgName: pkgJson.name,
|
|
247
|
+
version: pkgJson.version,
|
|
248
|
+
appiumVersion: pkgJson.peerDependencies?.appium,
|
|
249
|
+
installType,
|
|
250
|
+
installSpec: `${pkgJson.name}@${pkgJson.version}`,
|
|
251
|
+
installPath: extensionPath,
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
if (isDriver(pkgJson)) {
|
|
255
|
+
const driverName = pkgJson.appium.driverName;
|
|
256
|
+
const value = {
|
|
257
|
+
..._.omit(pkgJson.appium, 'driverName'),
|
|
258
|
+
...internal,
|
|
259
|
+
};
|
|
260
|
+
if (!_.isEqual(value, this.#data.drivers[driverName])) {
|
|
261
|
+
this.setExtension(DRIVER_TYPE, driverName, value);
|
|
262
|
+
return true;
|
|
263
|
+
}
|
|
264
|
+
return false;
|
|
265
|
+
}
|
|
266
|
+
if (isPlugin(pkgJson)) {
|
|
267
|
+
const pluginName = pkgJson.appium.pluginName;
|
|
268
|
+
const value = {
|
|
269
|
+
..._.omit(pkgJson.appium, 'pluginName'),
|
|
270
|
+
...internal,
|
|
271
|
+
};
|
|
272
|
+
if (!_.isEqual(value, this.#data.plugins[pluginName])) {
|
|
273
|
+
this.setExtension(PLUGIN_TYPE, pluginName, value);
|
|
274
|
+
return true;
|
|
275
|
+
}
|
|
276
|
+
return false;
|
|
277
|
+
}
|
|
278
|
+
throw new TypeError(
|
|
279
|
+
`The extension in ${extensionPath} is neither a valid ${DRIVER_TYPE} nor a valid ${PLUGIN_TYPE}.`
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Stores a deep-cloned copy of extension metadata under the given type and name.
|
|
285
|
+
*
|
|
286
|
+
* @param extType - `"driver"` or `"plugin"`
|
|
287
|
+
* @param extName - Manifest key for the extension
|
|
288
|
+
* @param extData - Full extension entry to persist in memory
|
|
289
|
+
* @returns The cloned data now held in the manifest
|
|
290
|
+
*/
|
|
291
|
+
setExtension<ExtType extends ExtensionType>(
|
|
292
|
+
extType: ExtType,
|
|
293
|
+
extName: string,
|
|
294
|
+
extData: ExtManifest<ExtType>
|
|
295
|
+
): ExtManifest<ExtType> {
|
|
296
|
+
const data = _.cloneDeep(extData) as ExtManifest<ExtType>;
|
|
297
|
+
if (extType === DRIVER_TYPE) {
|
|
298
|
+
this.#data.drivers[extName] = data as unknown as ExtManifest<DriverType>;
|
|
299
|
+
} else {
|
|
300
|
+
this.#data.plugins[extName] = data as unknown as ExtManifest<PluginType>;
|
|
301
|
+
}
|
|
302
|
+
return data;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Updates the in-memory manifest schema revision (typically during migration).
|
|
307
|
+
*
|
|
308
|
+
* @param rev - New `schemaRev` value
|
|
309
|
+
*/
|
|
310
|
+
setSchemaRev(rev: number): void {
|
|
311
|
+
this.#data.schemaRev = rev;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Removes an extension entry from the manifest data in memory (does not write to disk by itself).
|
|
316
|
+
*
|
|
317
|
+
* @param extType - `"driver"` or `"plugin"`
|
|
318
|
+
* @param extName - Manifest key to remove
|
|
319
|
+
*/
|
|
320
|
+
deleteExtension(extType: ExtensionType, extName: string): void {
|
|
321
|
+
if (extType === DRIVER_TYPE) {
|
|
322
|
+
delete this.#data.drivers[extName];
|
|
323
|
+
} else {
|
|
324
|
+
delete this.#data.plugins[extName];
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
async #setManifestPath(): Promise<string> {
|
|
329
|
+
if (!this.#manifestPath) {
|
|
330
|
+
const resolved = await env.resolveManifestPath(this.#appiumHome);
|
|
331
|
+
this.#manifestPath = resolved;
|
|
332
|
+
|
|
333
|
+
if (path.relative(this.#appiumHome, resolved).startsWith('.')) {
|
|
334
|
+
throw new Error(
|
|
335
|
+
`Mismatch between location of APPIUM_HOME and manifest file. APPIUM_HOME: ${
|
|
336
|
+
this.appiumHome
|
|
337
|
+
}, manifest file: ${resolved}`
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
return this.#manifestPath;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
function isExtension(value: unknown): value is ExtPackageJson<ExtensionType> {
|
|
347
|
+
return (
|
|
348
|
+
_.isPlainObject(value) &&
|
|
349
|
+
_.isPlainObject((value as {appium?: unknown}).appium) &&
|
|
350
|
+
_.isString((value as {name?: unknown}).name) &&
|
|
351
|
+
_.isString((value as {version?: unknown}).version)
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
function isDriver(value: unknown): value is ExtPackageJson<DriverType> {
|
|
356
|
+
return (
|
|
357
|
+
isExtension(value) &&
|
|
358
|
+
'driverName' in value.appium &&
|
|
359
|
+
_.isString((value.appium as {driverName?: unknown}).driverName)
|
|
360
|
+
);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
function isPlugin(value: unknown): value is ExtPackageJson<PluginType> {
|
|
364
|
+
return (
|
|
365
|
+
isExtension(value) &&
|
|
366
|
+
'pluginName' in value.appium &&
|
|
367
|
+
_.isString((value.appium as {pluginName?: unknown}).pluginName)
|
|
368
|
+
);
|
|
369
|
+
}
|
|
@@ -2,7 +2,7 @@ import {fs} from '@appium/support';
|
|
|
2
2
|
import {isPackageChanged} from 'package-changed';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import {PKG_HASHFILE_RELATIVE_PATH} from '../constants';
|
|
5
|
-
import log from '../logger';
|
|
5
|
+
import {log} from '../logger';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Determines if extensions have changed, and updates a hash the `package.json` in `appiumHome` if so.
|
|
@@ -10,40 +10,31 @@ import log from '../logger';
|
|
|
10
10
|
* If they have, we need to sync them with the `extensions.yaml` manifest.
|
|
11
11
|
*
|
|
12
12
|
* _Warning: this makes a blocking call to `writeFileSync`._
|
|
13
|
-
* @param {string} appiumHome
|
|
14
|
-
* @returns {Promise<boolean>} `true` if `package.json` `appiumHome` changed
|
|
15
13
|
*/
|
|
16
|
-
export async function packageDidChange(appiumHome) {
|
|
14
|
+
export async function packageDidChange(appiumHome: string): Promise<boolean> {
|
|
17
15
|
const hashFilename = path.join(appiumHome, PKG_HASHFILE_RELATIVE_PATH);
|
|
18
16
|
|
|
19
|
-
// XXX: the types in `package-changed` seem to be wrong.
|
|
20
|
-
|
|
21
|
-
/** @type {boolean} */
|
|
22
|
-
let isChanged;
|
|
23
|
-
/** @type {() => void} */
|
|
24
|
-
let writeHash;
|
|
25
|
-
/** @type {string} */
|
|
26
|
-
let hash;
|
|
27
|
-
/** @type {string|undefined} */
|
|
28
|
-
let oldHash;
|
|
29
|
-
|
|
30
|
-
// first mkdirp the target dir.
|
|
31
17
|
const hashFilenameDir = path.dirname(hashFilename);
|
|
32
18
|
log.debug(`Creating hash file directory: ${hashFilenameDir}`);
|
|
33
19
|
try {
|
|
34
20
|
await fs.mkdirp(hashFilenameDir);
|
|
35
|
-
} catch (err) {
|
|
21
|
+
} catch (err: any) {
|
|
36
22
|
throw new Error(
|
|
37
23
|
`Appium could not create the directory for hash file: ${hashFilenameDir}. Original error: ${err.message}`
|
|
38
24
|
);
|
|
39
25
|
}
|
|
40
26
|
|
|
27
|
+
let isChanged: boolean;
|
|
28
|
+
let writeHash: () => void;
|
|
29
|
+
let oldHash: string | undefined;
|
|
30
|
+
let hash: string;
|
|
41
31
|
try {
|
|
42
32
|
({isChanged, writeHash, oldHash, hash} = await isPackageChanged({
|
|
43
33
|
cwd: appiumHome,
|
|
44
34
|
hashFilename: PKG_HASHFILE_RELATIVE_PATH,
|
|
45
35
|
}));
|
|
46
36
|
} catch {
|
|
37
|
+
// If the library fails, assume the manifest may be stale and should be refreshed.
|
|
47
38
|
return true;
|
|
48
39
|
}
|
|
49
40
|
|
|
@@ -53,7 +44,7 @@ export async function packageDidChange(appiumHome) {
|
|
|
53
44
|
log.debug(
|
|
54
45
|
`Updated hash of ${appiumHome}/package.json from: ${oldHash ?? '(none)'} to: ${hash}`
|
|
55
46
|
);
|
|
56
|
-
} catch (err) {
|
|
47
|
+
} catch (err: any) {
|
|
57
48
|
throw new Error(
|
|
58
49
|
`Appium could not write hash file: ${hashFilenameDir}. Original error: ${err.message}`
|
|
59
50
|
);
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import _ from 'lodash';
|
|
2
|
+
import type {PluginType} from '@appium/types';
|
|
3
|
+
import type {ExtManifest, ExtName, ExtRecord} from 'appium/types';
|
|
4
|
+
import {PLUGIN_TYPE} from '../constants';
|
|
5
|
+
import {log} from '../logger';
|
|
6
|
+
import {ExtensionConfig} from './extension-config';
|
|
7
|
+
import type {Manifest} from './manifest';
|
|
8
|
+
|
|
9
|
+
export class PluginConfig extends ExtensionConfig<PluginType> {
|
|
10
|
+
private static readonly _instances = new WeakMap<Manifest, PluginConfig>();
|
|
11
|
+
|
|
12
|
+
private constructor(manifest: Manifest) {
|
|
13
|
+
super(PLUGIN_TYPE, manifest);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
static create(manifest: Manifest): PluginConfig {
|
|
17
|
+
const instance = new PluginConfig(manifest);
|
|
18
|
+
if (PluginConfig.getInstance(manifest)) {
|
|
19
|
+
throw new Error(
|
|
20
|
+
`Manifest with APPIUM_HOME ${manifest.appiumHome} already has a PluginConfig; use PluginConfig.getInstance() to retrieve it.`
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
PluginConfig._instances.set(manifest, instance);
|
|
24
|
+
return instance;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
static getInstance(manifest: Manifest): PluginConfig | undefined {
|
|
28
|
+
return PluginConfig._instances.get(manifest);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async validate(): Promise<ExtRecord<PluginType>> {
|
|
32
|
+
return await super._validate(this.manifest.getExtensionData(PLUGIN_TYPE));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
public override extensionDesc(
|
|
36
|
+
pluginName: ExtName<PluginType>,
|
|
37
|
+
{version}: ExtManifest<PluginType>
|
|
38
|
+
): string {
|
|
39
|
+
return `${String(pluginName)}@${version}`;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
override print(activeNames: ExtName<PluginType>[] = []): void {
|
|
43
|
+
const pluginNames = Object.keys(this.installedExtensions);
|
|
44
|
+
|
|
45
|
+
if (_.isEmpty(pluginNames)) {
|
|
46
|
+
log.info(
|
|
47
|
+
`No plugins have been installed. Use the "appium plugin" ` + 'command to install the one(s) you want to use.'
|
|
48
|
+
);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
log.info(`Available plugins:`);
|
|
53
|
+
for (const [pluginName, pluginData] of _.toPairs(this.installedExtensions)) {
|
|
54
|
+
const activeTxt = _.includes(activeNames, pluginName as ExtName<PluginType>) ? ' (ACTIVE)' : '';
|
|
55
|
+
log.info(` - ${this.extensionDesc(pluginName as ExtName<PluginType>, pluginData)}${activeTxt}`);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (_.isEmpty(activeNames)) {
|
|
59
|
+
log.info('No plugins activated. Use the --use-plugins flag with names of plugins to activate');
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|