Compare commits

..
Author SHA1 Message Date
kbrandwijk dd02e8be4a chore: initial setup for ESM 2022-04-24 20:43:37 +02:00
118 changed files with 2173 additions and 2595 deletions

No files matched your search

-8
View File
@@ -12,14 +12,6 @@ This is the log of notable changes to EAS CLI and related packages.
### 🧹 Chores
- Update dependencies. ([#1095](https://github.com/expo/eas-cli/pull/1095) by [@dsokal](https://github.com/dsokal))
## [0.52.0](https://github.com/expo/eas-cli/releases/tag/v0.52.0) - 2022-04-26
### 🎉 New features
- Support target specific entitlements. ([#1078](https://github.com/expo/eas-cli/pull/1078) by [@wkozyra95](https://github.com/wkozyra95))
## [0.51.0](https://github.com/expo/eas-cli/releases/tag/v0.51.0) - 2022-04-19
### 🎉 New features
+5 -5
View File
@@ -1,7 +1,7 @@
module.exports = {
projects: [
require('./packages/eas-cli/jest.config.js'),
require('./packages/eas-json/jest.config.js'),
],
import easCliConfig from './packages/eas-cli/jest.config.js';
import easJsonConfig from './packages/eas-json/jest.config.js';
export default {
projects: [easCliConfig, easJsonConfig],
testPathIgnorePatterns: ['.*'],
};
+2 -1
View File
@@ -1,7 +1,8 @@
module.exports = {
export default {
preset: 'ts-jest',
testEnvironment: 'node',
testRegex: '/__tests__/.*(test|spec)\\.[jt]sx?$',
watchPlugins: ['jest-watch-typeahead/filename', 'jest-watch-typeahead/testname'],
collectCoverageFrom: ['<rootDir>/src/**/*.{ts,tsx,js,jsx}'],
extensionsToTreatAsEsm: ['.ts'],
};
+1 -1
View File
@@ -1,5 +1,5 @@
{
"npmClient": "yarn",
"useWorkspaces": true,
"version": "0.52.0"
"version": "0.51.0"
}
+10 -9
View File
@@ -1,5 +1,6 @@
{
"private": true,
"type":"module",
"workspaces": [
"packages/*"
],
@@ -17,19 +18,19 @@
"clean": "lerna run clean && rm -rf node_modules coverage"
},
"devDependencies": {
"@types/jest": "27.5.0",
"@types/node": "17.0.32",
"eslint": "8.15.0",
"eslint-config-universe": "11.0.0",
"@types/jest": "27.4.1",
"@types/node": "16.7.8",
"eslint": "8.12.0",
"eslint-config-universe": "10.0.0",
"eslint-plugin-async-protect": "1.1.0",
"eslint-plugin-node": "11.1.0",
"jest": "28.1.0",
"jest-watch-typeahead": "1.1.0",
"jest": "27.5.1",
"jest-watch-typeahead": "1.0.0",
"lerna": "4.0.0",
"oclif": "3.0.1",
"prettier": "2.6.2",
"ts-jest": "28.0.2",
"typescript": "4.6.4"
"prettier": "2.6.1",
"ts-jest": "27.1.4",
"typescript": "4.6.3"
},
"volta": {
"node": "16.10.0",
+42 -42
View File
@@ -98,7 +98,7 @@ ALIASES
$ eas login
```
_See code: [src/commands/account/login.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/account/login.ts)_
_See code: [src/commands/account/login.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/account/login.ts)_
## `eas account:logout`
@@ -115,7 +115,7 @@ ALIASES
$ eas logout
```
_See code: [src/commands/account/logout.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/account/logout.ts)_
_See code: [src/commands/account/logout.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/account/logout.ts)_
## `eas account:view`
@@ -132,7 +132,7 @@ ALIASES
$ eas whoami
```
_See code: [src/commands/account/view.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/account/view.ts)_
_See code: [src/commands/account/view.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/account/view.ts)_
## `eas analytics [STATUS]`
@@ -146,7 +146,7 @@ DESCRIPTION
display or change analytics settings
```
_See code: [src/commands/analytics.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/analytics.ts)_
_See code: [src/commands/analytics.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/analytics.ts)_
## `eas autocomplete [SHELL]`
@@ -195,7 +195,7 @@ DESCRIPTION
create a branch
```
_See code: [src/commands/branch/create.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/branch/create.ts)_
_See code: [src/commands/branch/create.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/branch/create.ts)_
## `eas branch:delete [NAME]`
@@ -215,7 +215,7 @@ DESCRIPTION
delete a branch
```
_See code: [src/commands/branch/delete.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/branch/delete.ts)_
_See code: [src/commands/branch/delete.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/branch/delete.ts)_
## `eas branch:list`
@@ -232,7 +232,7 @@ DESCRIPTION
list all branches
```
_See code: [src/commands/branch/list.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/branch/list.ts)_
_See code: [src/commands/branch/list.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/branch/list.ts)_
## `eas branch:rename`
@@ -251,7 +251,7 @@ DESCRIPTION
rename a branch
```
_See code: [src/commands/branch/rename.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/branch/rename.ts)_
_See code: [src/commands/branch/rename.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/branch/rename.ts)_
## `eas branch:view [NAME]`
@@ -271,7 +271,7 @@ DESCRIPTION
view a branch
```
_See code: [src/commands/branch/view.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/branch/view.ts)_
_See code: [src/commands/branch/view.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/branch/view.ts)_
## `eas build`
@@ -300,7 +300,7 @@ DESCRIPTION
start a build
```
_See code: [src/commands/build/index.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/build/index.ts)_
_See code: [src/commands/build/index.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/build/index.ts)_
## `eas build:cancel [BUILD_ID]`
@@ -314,7 +314,7 @@ DESCRIPTION
cancel a build
```
_See code: [src/commands/build/cancel.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/build/cancel.ts)_
_See code: [src/commands/build/cancel.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/build/cancel.ts)_
## `eas build:configure`
@@ -331,7 +331,7 @@ DESCRIPTION
configure the project to support EAS Build
```
_See code: [src/commands/build/configure.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/build/configure.ts)_
_See code: [src/commands/build/configure.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/build/configure.ts)_
## `eas build:inspect`
@@ -366,7 +366,7 @@ DESCRIPTION
inspect the state of the project at specific build stages, useful for troubleshooting
```
_See code: [src/commands/build/inspect.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/build/inspect.ts)_
_See code: [src/commands/build/inspect.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/build/inspect.ts)_
## `eas build:list`
@@ -399,7 +399,7 @@ DESCRIPTION
list all builds for your project
```
_See code: [src/commands/build/list.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/build/list.ts)_
_See code: [src/commands/build/list.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/build/list.ts)_
## `eas build:submit`
@@ -444,7 +444,7 @@ DESCRIPTION
view a build for your project
```
_See code: [src/commands/build/view.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/build/view.ts)_
_See code: [src/commands/build/view.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/build/view.ts)_
## `eas channel:create [NAME]`
@@ -464,7 +464,7 @@ DESCRIPTION
create a channel
```
_See code: [src/commands/channel/create.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/channel/create.ts)_
_See code: [src/commands/channel/create.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/channel/create.ts)_
## `eas channel:edit [NAME]`
@@ -485,7 +485,7 @@ DESCRIPTION
point a channel at a new branch
```
_See code: [src/commands/channel/edit.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/channel/edit.ts)_
_See code: [src/commands/channel/edit.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/channel/edit.ts)_
## `eas channel:list`
@@ -502,7 +502,7 @@ DESCRIPTION
list all channels
```
_See code: [src/commands/channel/list.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/channel/list.ts)_
_See code: [src/commands/channel/list.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/channel/list.ts)_
## `eas channel:view [NAME]`
@@ -522,7 +522,7 @@ DESCRIPTION
view a channel
```
_See code: [src/commands/channel/view.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/channel/view.ts)_
_See code: [src/commands/channel/view.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/channel/view.ts)_
## `eas config`
@@ -540,7 +540,7 @@ DESCRIPTION
display project configuration (app.json + eas.json)
```
_See code: [src/commands/config.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/config.ts)_
_See code: [src/commands/config.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/config.ts)_
## `eas credentials`
@@ -554,7 +554,7 @@ DESCRIPTION
manage credentials
```
_See code: [src/commands/credentials.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/credentials.ts)_
_See code: [src/commands/credentials.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/credentials.ts)_
## `eas device:create`
@@ -568,7 +568,7 @@ DESCRIPTION
register new Apple Devices to use for internal distribution
```
_See code: [src/commands/device/create.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/device/create.ts)_
_See code: [src/commands/device/create.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/device/create.ts)_
## `eas device:delete`
@@ -586,7 +586,7 @@ DESCRIPTION
remove a registered device from your account
```
_See code: [src/commands/device/delete.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/device/delete.ts)_
_See code: [src/commands/device/delete.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/device/delete.ts)_
## `eas device:list`
@@ -603,7 +603,7 @@ DESCRIPTION
list all registered devices for your account
```
_See code: [src/commands/device/list.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/device/list.ts)_
_See code: [src/commands/device/list.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/device/list.ts)_
## `eas device:view [UDID]`
@@ -617,7 +617,7 @@ DESCRIPTION
view a device for your project
```
_See code: [src/commands/device/view.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/device/view.ts)_
_See code: [src/commands/device/view.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/device/view.ts)_
## `eas diagnostics`
@@ -631,7 +631,7 @@ DESCRIPTION
display environment info
```
_See code: [src/commands/diagnostics.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/diagnostics.ts)_
_See code: [src/commands/diagnostics.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/diagnostics.ts)_
## `eas help [COMMAND]`
@@ -710,7 +710,7 @@ DESCRIPTION
information about the current project
```
_See code: [src/commands/project/info.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/project/info.ts)_
_See code: [src/commands/project/info.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/project/info.ts)_
## `eas project:init`
@@ -727,7 +727,7 @@ ALIASES
$ eas init
```
_See code: [src/commands/project/init.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/project/init.ts)_
_See code: [src/commands/project/init.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/project/init.ts)_
## `eas secret:create`
@@ -747,7 +747,7 @@ DESCRIPTION
create an environment secret on the current project or owner account
```
_See code: [src/commands/secret/create.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/secret/create.ts)_
_See code: [src/commands/secret/create.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/secret/create.ts)_
## `eas secret:delete`
@@ -764,7 +764,7 @@ DESCRIPTION
delete an environment secret by ID
```
_See code: [src/commands/secret/delete.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/secret/delete.ts)_
_See code: [src/commands/secret/delete.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/secret/delete.ts)_
## `eas secret:list`
@@ -778,7 +778,7 @@ DESCRIPTION
list environment secrets available for your current app
```
_See code: [src/commands/secret/list.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/secret/list.ts)_
_See code: [src/commands/secret/list.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/secret/list.ts)_
## `eas submit`
@@ -808,7 +808,7 @@ ALIASES
$ eas build:submit
```
_See code: [src/commands/submit.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/submit.ts)_
_See code: [src/commands/submit.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/submit.ts)_
## `eas update`
@@ -838,7 +838,7 @@ DESCRIPTION
publish an update group
```
_See code: [src/commands/update/index.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/update/index.ts)_
_See code: [src/commands/update/index.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/update/index.ts)_
## `eas update:configure`
@@ -855,7 +855,7 @@ DESCRIPTION
configure the project to support EAS Update
```
_See code: [src/commands/update/configure.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/update/configure.ts)_
_See code: [src/commands/update/configure.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/update/configure.ts)_
## `eas update:delete GROUPID`
@@ -875,7 +875,7 @@ DESCRIPTION
delete all the updates in an update group
```
_See code: [src/commands/update/delete.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/update/delete.ts)_
_See code: [src/commands/update/delete.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/update/delete.ts)_
## `eas update:list`
@@ -894,7 +894,7 @@ DESCRIPTION
view the recent updates for a branch
```
_See code: [src/commands/update/list.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/update/list.ts)_
_See code: [src/commands/update/list.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/update/list.ts)_
## `eas update:view GROUPID`
@@ -914,7 +914,7 @@ DESCRIPTION
update group details
```
_See code: [src/commands/update/view.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/update/view.ts)_
_See code: [src/commands/update/view.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/update/view.ts)_
## `eas webhook:create`
@@ -934,7 +934,7 @@ DESCRIPTION
create a webhook
```
_See code: [src/commands/webhook/create.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/webhook/create.ts)_
_See code: [src/commands/webhook/create.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/webhook/create.ts)_
## `eas webhook:delete [ID]`
@@ -951,7 +951,7 @@ DESCRIPTION
delete a webhook
```
_See code: [src/commands/webhook/delete.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/webhook/delete.ts)_
_See code: [src/commands/webhook/delete.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/webhook/delete.ts)_
## `eas webhook:list`
@@ -968,7 +968,7 @@ DESCRIPTION
list webhooks
```
_See code: [src/commands/webhook/list.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/webhook/list.ts)_
_See code: [src/commands/webhook/list.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/webhook/list.ts)_
## `eas webhook:update`
@@ -989,7 +989,7 @@ DESCRIPTION
update a webhook
```
_See code: [src/commands/webhook/update.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/webhook/update.ts)_
_See code: [src/commands/webhook/update.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/webhook/update.ts)_
## `eas webhook:view ID`
@@ -1006,7 +1006,7 @@ DESCRIPTION
view a webhook
```
_See code: [src/commands/webhook/view.ts](https://github.com/expo/eas-cli/blob/v0.52.0/packages/eas-cli/src/commands/webhook/view.ts)_
_See code: [src/commands/webhook/view.ts](https://github.com/expo/eas-cli/blob/v0.51.0/packages/eas-cli/src/commands/webhook/view.ts)_
## `eas whoami`
File renamed without changes.
-6
View File
@@ -1,6 +0,0 @@
#!/usr/bin/env node
require('@oclif/core')
.run()
.then(require('@oclif/core/flush'))
.catch(require('@oclif/core/handle'));
+6
View File
@@ -0,0 +1,6 @@
#!/usr/bin/env node
import { Errors, flush, run } from '@oclif/core';
run(undefined, import.meta.url)
.then(flush)
.catch(Errors.handle);
+82 -551
View File
@@ -701,7 +701,7 @@
},
{
"name": "billing",
"description": "Billing information. Only visible to members with the ADMIN or OWNER role.",
"description": "Billing information",
"args": [],
"type": {
"kind": "OBJECT",
@@ -1702,7 +1702,7 @@
"deprecationReason": null
},
{
"name": "chargeID",
"name": "chargeIdentifier",
"description": null,
"type": {
"kind": "NON_NULL",
@@ -1716,30 +1716,6 @@
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "description",
"description": null,
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "reason",
"description": null,
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"type": {
@@ -2112,15 +2088,15 @@
"description": null,
"fields": [
{
"name": "id",
"name": "metric",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ID",
"kind": "ENUM",
"name": "EASServiceMetric",
"ofType": null
}
},
@@ -2143,22 +2119,6 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "serviceMetric",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "ENUM",
"name": "EASServiceMetric",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "timestamp",
"description": null,
@@ -2197,151 +2157,11 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "AccountUsageMetricAndCost",
"description": null,
"fields": [
{
"name": "id",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "limit",
"description": "The limit, in units, allowed by this plan",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Float",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "metricType",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "ENUM",
"name": "UsageMetricType",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "serviceMetric",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "ENUM",
"name": "EASServiceMetric",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "totalCost",
"description": "Total cost of this particular metric, in cents",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Float",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "value",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Float",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "AccountUsageMetrics",
"description": null,
"fields": [
{
"name": "byBillingPeriod",
"description": null,
"args": [
{
"name": "date",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "DateTime",
"ofType": null
}
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "UsageMetricTotal",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "metricsForServiceMetric",
"description": null,
@@ -2363,7 +2183,7 @@
"deprecationReason": null
},
{
"name": "serviceMetric",
"name": "metric",
"description": null,
"type": {
"kind": "NON_NULL",
@@ -6665,15 +6485,15 @@
"fields": [
{
"name": "colorPalette",
"description": null,
"description": "Nullable color palette of the app icon. If null, color palette couldn't be retrieved from external service (imgix)",
"args": [],
"type": {
"kind": "SCALAR",
"name": "JSON",
"ofType": null
},
"isDeprecated": true,
"deprecationReason": "No longer supported"
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "originalUrl",
@@ -6725,29 +6545,6 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "INPUT_OBJECT",
"name": "AppInfoInput",
"description": null,
"fields": null,
"inputFields": [
{
"name": "displayName",
"description": null,
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"interfaces": null,
"enumValues": null,
"possibleTypes": null
},
{
"kind": "INPUT_OBJECT",
"name": "AppInput",
@@ -6770,18 +6567,6 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "appInfo",
"description": null,
"type": {
"kind": "INPUT_OBJECT",
"name": "AppInfoInput",
"ofType": null
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "privacy",
"description": null,
@@ -6898,55 +6683,6 @@
"isDeprecated": true,
"deprecationReason": "No longer supported"
},
{
"name": "setAppInfo",
"description": "Set display info for app",
"args": [
{
"name": "appId",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
}
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "appInfo",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "INPUT_OBJECT",
"name": "AppInfoInput",
"ofType": null
}
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "App",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "setPushSecurityEnabled",
"description": "Require api token to send push notifs for experience",
@@ -13286,65 +13022,6 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "Concurrencies",
"description": null,
"fields": [
{
"name": "android",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "ios",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "total",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "INPUT_OBJECT",
"name": "CreateAccessTokenInput",
@@ -13449,18 +13126,6 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "archiveUrl",
"description": null,
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "config",
"description": null,
@@ -13599,16 +13264,59 @@
"deprecationReason": null
},
{
"name": "archiveUrl",
"name": "config",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "INPUT_OBJECT",
"name": "IosSubmissionConfigInput",
"ofType": null
}
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "submittedBuildId",
"description": null,
"type": {
"kind": "SCALAR",
"name": "String",
"name": "ID",
"ofType": null
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"interfaces": null,
"enumValues": null,
"possibleTypes": null
},
{
"kind": "INPUT_OBJECT",
"name": "CreateSubmissionInput",
"description": null,
"fields": null,
"inputFields": [
{
"name": "appId",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
}
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "config",
@@ -13617,8 +13325,24 @@
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "INPUT_OBJECT",
"name": "IosSubmissionConfigInput",
"kind": "SCALAR",
"name": "JSONObject",
"ofType": null
}
},
"defaultValue": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "platform",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "ENUM",
"name": "AppPlatform",
"ofType": null
}
},
@@ -14268,29 +13992,11 @@
"inputFields": null,
"interfaces": null,
"enumValues": [
{
"name": "ASSETS_REQUESTS",
"description": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "BANDWIDTH_USAGE",
"description": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "MANIFEST_REQUESTS",
"description": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "UNIQUE_USERS",
"description": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"possibleTypes": null
@@ -21383,34 +21089,6 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "archiveUrl",
"description": null,
"args": [],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "canRetry",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Boolean",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "cancelingActor",
"description": null,
@@ -21503,18 +21181,6 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "parentSubmission",
"description": null,
"args": [],
"type": {
"kind": "OBJECT",
"name": "Submission",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "platform",
"description": null,
@@ -21763,13 +21429,9 @@
"name": "submissionId",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
}
"kind": "SCALAR",
"name": "ID",
"ofType": null
},
"defaultValue": null,
"isDeprecated": false,
@@ -21855,18 +21517,18 @@
"deprecationReason": null
},
{
"name": "retrySubmission",
"description": "Retry an EAS Submit submission",
"name": "createSubmission",
"description": "Create an EAS Submit submission",
"args": [
{
"name": "parentSubmissionId",
"name": "input",
"description": null,
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ID",
"kind": "INPUT_OBJECT",
"name": "CreateSubmissionInput",
"ofType": null
}
},
@@ -21884,8 +21546,8 @@
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
"isDeprecated": true,
"deprecationReason": "Use createIosSubmission / createAndroidSubmission instead"
}
],
"inputFields": null,
@@ -22025,18 +21687,6 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "concurrencies",
"description": null,
"args": [],
"type": {
"kind": "OBJECT",
"name": "Concurrencies",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "endedAt",
"description": null,
@@ -23329,113 +22979,6 @@
],
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "UsageMetricTotal",
"description": null,
"fields": [
{
"name": "billingPeriod",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "BillingPeriod",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "id",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "overageMetrics",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "AccountUsageMetricAndCost",
"ofType": null
}
}
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "planMetrics",
"description": null,
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "AccountUsageMetricAndCost",
"ofType": null
}
}
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "totalCost",
"description": "Total cost of overages, in cents",
"args": [],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Float",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "ENUM",
"name": "UsageMetricType",
@@ -23444,23 +22987,11 @@
"inputFields": null,
"interfaces": null,
"enumValues": [
{
"name": "BANDWIDTH",
"description": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "REQUEST",
"description": null,
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "USER",
"description": null,
"isDeprecated": false,
"deprecationReason": null
}
],
"possibleTypes": null
+11 -2
View File
@@ -1,5 +1,14 @@
module.exports = {
...require('../../jest/jest.config.js'),
import { dirname } from 'path';
import { fileURLToPath } from 'url';
import baseConfig from '../../jest/jest.config.js';
const filename = fileURLToPath(import.meta.url);
// eslint-disable-next-line no-underscore-dangle
const __dirname = dirname(filename);
export default {
...baseConfig,
rootDir: __dirname,
roots: ['src', '__mocks__'],
globalSetup: './jest/global-setup.ts',
+34 -33
View File
@@ -1,38 +1,39 @@
{
"name": "eas-cli",
"description": "EAS command line tool",
"version": "0.52.0",
"version": "0.51.0",
"author": "Expo <[email protected]>",
"bin": {
"eas": "./bin/run"
"eas": "./bin/run.js"
},
"type": "module",
"bugs": "https://github.com/expo/eas-cli/issues",
"dependencies": {
"@expo/apple-utils": "0.0.0-alpha.31",
"@expo/code-signing-certificates": "0.0.2",
"@expo/config": "6.0.23",
"@expo/config-plugins": "4.1.4",
"@expo/config-types": "45.0.0",
"@expo/eas-build-job": "0.2.72",
"@expo/eas-json": "0.52.0",
"@expo/json-file": "8.2.36",
"@expo/config": "6.0.19",
"@expo/config-plugins": "4.1.0",
"@expo/config-types": "44.0.0",
"@expo/eas-build-job": "0.2.68",
"@expo/eas-json": "0.51.0",
"@expo/json-file": "8.2.34",
"@expo/multipart-body-parser": "1.1.0",
"@expo/pkcs12": "0.0.8",
"@expo/plist": "0.0.18",
"@expo/plugin-warn-if-update-available": "2.2.0",
"@expo/prebuild-config": "3.1.6",
"@expo/pkcs12": "0.0.7",
"@expo/plist": "0.0.17",
"@expo/plugin-warn-if-update-available": "2.1.0",
"@expo/prebuild-config": "3.1.0",
"@expo/results": "1.0.0",
"@expo/rudder-sdk-node": "1.1.1",
"@expo/spawn-async": "1.6.0",
"@expo/timeago.js": "1.0.0",
"@oclif/core": "1.8.1",
"@oclif/plugin-autocomplete": "1.3.0",
"@oclif/core": "1.6.4",
"@oclif/plugin-autocomplete": "1.2.0",
"@oclif/plugin-help": "5.1.12",
"@urql/core": "2.4.4",
"@urql/exchange-retry": "0.3.3",
"@urql/core": "2.4.3",
"@urql/exchange-retry": "0.3.2",
"chalk": "4.1.2",
"cli-progress": "3.11.0",
"cli-table3": "0.6.2",
"cli-progress": "3.10.0",
"cli-table3": "0.6.1",
"dateformat": "4.6.3",
"env-paths": "2.2.0",
"envinfo": "7.8.1",
@@ -40,12 +41,12 @@
"fast-glob": "3.2.11",
"figures": "3.2.0",
"form-data": "4.0.0",
"fs-extra": "10.1.0",
"fs-extra": "10.0.1",
"getenv": "1.0.0",
"gradle-to-js": "2.0.1",
"graphql": "16.5.0",
"gradle-to-js": "2.0.0",
"graphql": "16.3.0",
"graphql-tag": "2.12.6",
"https-proxy-agent": "5.0.1",
"https-proxy-agent": "5.0.0",
"ignore": "5.2.0",
"indent-string": "4.0.0",
"jks-js": "1.0.2",
@@ -53,8 +54,8 @@
"keychain": "1.3.0",
"log-symbols": "4.1.0",
"mime": "3.0.0",
"minimatch": "5.0.1",
"nanoid": "3.3.4",
"minimatch": "3.0.5",
"nanoid": "3.3.2",
"node-fetch": "2.6.7",
"node-forge": "1.3.1",
"nullthrows": "1.1.1",
@@ -63,12 +64,12 @@
"prompts": "2.4.2",
"qrcode-terminal": "0.12.0",
"resolve-from": "5.0.0",
"semver": "7.3.7",
"semver": "7.3.5",
"slash": "3.0.0",
"strip-ansi": "6.0.0",
"tar": "6.1.11",
"terminal-link": "2.1.1",
"tslib": "2.4.0",
"tslib": "2.3.1",
"turndown": "7.1.1",
"untildify": "4.0.0",
"uuid": "8.3.2",
@@ -77,8 +78,8 @@
"devDependencies": {
"@graphql-codegen/cli": "2.6.2",
"@graphql-codegen/introspection": "2.1.1",
"@graphql-codegen/typescript": "2.4.11",
"@graphql-codegen/typescript-operations": "2.4.0",
"@graphql-codegen/typescript": "2.4.8",
"@graphql-codegen/typescript-operations": "2.3.5",
"@types/cli-progress": "3.9.2",
"@types/dateformat": "3.0.1",
"@types/envinfo": "7.8.1",
@@ -86,14 +87,14 @@
"@types/getenv": "^1.0.0",
"@types/mime": "2.0.3",
"@types/node-fetch": "^2.5.7",
"@types/node-forge": "1.0.2",
"@types/node-forge": "1.0.1",
"@types/prompts": "2.0.14",
"@types/semver": "7.3.9",
"@types/tar": "6.1.1",
"@types/tough-cookie": "4.0.2",
"@types/tough-cookie": "4.0.1",
"@types/uuid": "8.3.4",
"@types/wrap-ansi": "3.0.0",
"axios": "0.27.2",
"axios": "0.26.1",
"eslint-plugin-graphql": "4.0.0",
"form-data": "4.0.0",
"memfs": "3.4.1",
@@ -101,10 +102,10 @@
"nock": "13.2.4",
"tempy": "0.7.0",
"ts-deepmerge": "2.0.1",
"typescript": "4.6.4"
"typescript": "4.6.3"
},
"engines": {
"node": ">=12.0.0"
"node": ">=14.16"
},
"files": [
"/bin",
+3
View File
@@ -0,0 +1,3 @@
export function asMock(fn: any): jest.Mock {
return fn as jest.Mock;
}
@@ -105,7 +105,7 @@ export async function maybeResolveVersionsAsync(
appBuildVersion:
resolveConfigValue(buildGradle, 'versionCode', parsedGradleCommand?.flavor) ?? '1',
};
} catch {
} catch (err: any) {
return {};
}
} else {
+7 -6
View File
@@ -36,12 +36,13 @@ export async function createIosContextAsync(
},
buildProfile
);
const targets = await resolveTargetsAsync({
projectDir: ctx.projectDir,
exp: ctx.exp,
xcodeBuildContext,
env: buildProfile.env,
});
const targets = await resolveTargetsAsync(
{
projectDir: ctx.projectDir,
exp: ctx.exp,
},
xcodeBuildContext
);
const applicationTarget = findApplicationTarget(targets);
const applicationTargetBuildSettings = resolveBuildSettings(ctx, applicationTarget);
@@ -2,6 +2,7 @@ import { Platform } from '@expo/eas-build-job';
import IosCredentialsProvider from '../../credentials/ios/IosCredentialsProvider';
import { getAppFromContext } from '../../credentials/ios/actions/BuildCredentialsUtils';
import { resolveEntitlementsJsonAsync } from '../../credentials/ios/appstore/entitlements';
import { IosCredentials, Target } from '../../credentials/ios/types';
import { CredentialsResult } from '../build';
import { BuildContext } from '../context';
@@ -18,6 +19,13 @@ export async function ensureIosCredentialsAsync(
const provider = new IosCredentialsProvider(buildCtx.credentialsCtx, {
app: getAppFromContext(buildCtx.credentialsCtx),
targets,
iosCapabilitiesOptions: {
entitlements: await resolveEntitlementsJsonAsync(
buildCtx.projectDir,
buildCtx.workflow,
buildCtx.buildProfile.env ?? {}
),
},
distribution: buildCtx.buildProfile.distribution ?? 'store',
enterpriseProvisioning: buildCtx.buildProfile.enterpriseProvisioning,
});
+1 -1
View File
@@ -6,7 +6,7 @@ import semver from 'semver';
import { ora } from '../ora';
const PLUGIN_PACKAGE_NAME = 'eas-cli-local-build-plugin';
const PLUGIN_PACKAGE_VERSION = '0.0.78';
const PLUGIN_PACKAGE_VERSION = '0.0.74';
export interface LocalBuildOptions {
enable: boolean;
@@ -1,8 +1,12 @@
import { asMock } from '../../__tests__/utils';
import { flushAsync, initAsync, logEvent } from '../../analytics/rudderstackClient';
import { jester as mockJester } from '../../credentials/__tests__/fixtures-constants';
import { getUserAsync } from '../../user/User';
import { ensureLoggedInAsync } from '../../user/actions';
import EasCommand from '../EasCommand';
import TestEasCommand from './TestEasCommand';
TestEasCommand.prototype.authValue = jest.fn().mockImplementation(() => true);
jest.mock('../../user/User');
jest.mock('../../user/actions', () => ({ ensureLoggedInAsync: jest.fn() }));
@@ -28,30 +32,17 @@ afterAll(() => {
});
beforeEach(() => {
jest
.mocked(getUserAsync)
asMock(getUserAsync)
.mockReset()
.mockImplementation(async () => mockJester);
jest
.mocked(ensureLoggedInAsync)
.mockImplementation(() => mockJester);
asMock(ensureLoggedInAsync)
.mockReset()
.mockImplementation(async () => mockJester);
jest.mocked(initAsync).mockReset();
jest.mocked(flushAsync).mockReset();
jest.mocked(logEvent).mockReset();
.mockImplementation(() => mockJester);
asMock(initAsync).mockReset();
asMock(flushAsync).mockReset();
asMock(logEvent).mockReset();
});
const createTestEasCommand = (authValue: boolean): typeof EasCommand => {
class TestEasCommand extends EasCommand {
requiresAuthentication = authValue;
async runAsync(): Promise<void> {}
}
TestEasCommand.id = 'TestEasCommand'; // normally oclif will assign ids, but b/c this is located outside the commands folder it will not
return TestEasCommand;
};
describe(EasCommand.name, () => {
describe('without exceptions', () => {
// The first test in this suite should have an increased timeout
@@ -62,36 +53,32 @@ describe(EasCommand.name, () => {
// See https://github.com/oclif/command/blob/master/src/command.ts#L80
// and look for "Config.load"
it('ensures the user is logged in', async () => {
const TestEasCommand = createTestEasCommand(true);
await TestEasCommand.run();
expect(ensureLoggedInAsync).toHaveBeenCalled();
}, 15_000);
it('ensures the user data is read from cache', async () => {
const TestEasCommand = createTestEasCommand(false);
asMock(TestEasCommand.prototype.authValue).mockImplementationOnce(() => false);
await TestEasCommand.run();
expect(jest.mocked(getUserAsync)).toBeCalledTimes(1);
expect(jest.mocked(ensureLoggedInAsync)).not.toBeCalled();
expect(getUserAsync).toHaveReturnedWith(mockJester);
});
it('initializes analytics', async () => {
const TestEasCommand = createTestEasCommand(true);
await TestEasCommand.run();
expect(initAsync).toHaveBeenCalled();
});
it('flushes analytics', async () => {
const TestEasCommand = createTestEasCommand(true);
await TestEasCommand.run();
expect(flushAsync).toHaveBeenCalled();
});
it('logs events', async () => {
const TestEasCommand = createTestEasCommand(true);
await TestEasCommand.run();
expect(logEvent).toHaveBeenCalledWith('action', {
@@ -102,12 +89,11 @@ describe(EasCommand.name, () => {
describe('after exceptions', () => {
it('flushes analytics', async () => {
const TestEasCommand = createTestEasCommand(true);
try {
await TestEasCommand.run().then(() => {
throw new Error('foo');
});
} catch {}
} catch (error) {}
expect(flushAsync).toHaveBeenCalled();
});
@@ -0,0 +1,13 @@
import EasCommand from '../EasCommand';
export default class TestEasCommand extends EasCommand {
requiresAuthentication = this.authValue();
authValue(): boolean {
return false;
}
async runAsync(): Promise<void> {}
}
TestEasCommand.id = 'TestEasCommand'; // normally oclif will assign ids, but b/c this is located outside the commands folder it will not
@@ -104,7 +104,7 @@ async function selectBuildToCancelAsync(
async function ensureBuildExistsAsync(buildId: string): Promise<void> {
try {
await BuildQuery.byIdAsync(buildId);
} catch {
} catch (err) {
throw new Error(`Couldn't find a build matching the id ${buildId}`);
}
}
@@ -102,7 +102,7 @@ export default class BuildInspect extends EasCommand {
if (!flags.verbose) {
Log.log(chalk.green('Build successful'));
}
} catch {
} catch (err) {
if (!flags.verbose) {
Log.error('Build failed');
Log.error(`Re-run this command with ${chalk.bold('--verbose')} flag to see the logs`);
@@ -45,7 +45,7 @@ export function getBranchMapping(branchMappingString?: string): {
let branchMapping: BranchMapping;
try {
branchMapping = JSON.parse(branchMappingString);
} catch {
} catch (e) {
throw new Error(`Could not parse branchMapping string into a JSON: "${branchMappingString}"`);
}
assert(branchMapping, 'Branch Mapping must be defined.');
@@ -76,9 +76,7 @@ export const testPushKey: ApplePushKeyFragment = {
iosAppCredentialsList: [],
};
export const testTargets: Target[] = [
{ targetName: 'testapp', bundleIdentifier: 'foo.bar.com', entitlements: {} },
];
export const testTargets: Target[] = [{ targetName: 'testapp', bundleIdentifier: 'foo.bar.com' }];
export const testProvisioningProfileFragment: AppleProvisioningProfileFragment = {
id: 'test-prov-prof-id-1',
@@ -1,3 +1,4 @@
import { asMock } from '../../../../__tests__/utils';
import { confirmAsync, promptAsync } from '../../../../prompts';
import {
getNewAndroidApiMock,
@@ -16,7 +17,7 @@ import {
jest.mock('../../../../ora');
jest.mock('../../../../prompts');
jest.mocked(confirmAsync).mockImplementation(async () => true);
asMock(confirmAsync).mockImplementation(() => true);
describe('BuildCredentialsUtils', () => {
describe(canCopyLegacyCredentialsAsync, () => {
@@ -83,9 +84,7 @@ describe('BuildCredentialsUtils', () => {
});
describe(promptUserAndCopyLegacyCredentialsAsync, () => {
it('copies all legacy credentials to EAS if the user is eligible', async () => {
jest
.mocked(promptAsync)
.mockImplementation(async () => ({ providedName: 'test-provided-name' }));
asMock(promptAsync).mockImplementation(() => ({ providedName: 'test-provided-name' }));
const ctx = createCtxMock({
nonInteractive: false,
android: {
@@ -1,10 +1,11 @@
import { asMock } from '../../../../__tests__/utils';
import { promptAsync } from '../../../../prompts';
import { createCtxMock } from '../../../__tests__/fixtures-context';
import { getAppLookupParamsFromContextAsync } from '../BuildCredentialsUtils';
import { CreateFcm } from '../CreateFcm';
jest.mock('../../../../prompts');
jest.mocked(promptAsync).mockImplementation(async () => ({ fcmApiKey: 'blah' }));
asMock(promptAsync).mockImplementation(() => ({ fcmApiKey: 'blah' }));
describe(CreateFcm, () => {
it('creates an fcm api key in Interactive Mode', async () => {
@@ -1,5 +1,6 @@
import { vol } from 'memfs';
import { asMock } from '../../../../__tests__/utils';
import { promptAsync } from '../../../../prompts';
import { createCtxMock } from '../../../__tests__/fixtures-context';
import { getAppLookupParamsFromContextAsync } from '../BuildCredentialsUtils';
@@ -7,7 +8,7 @@ import { CreateGoogleServiceAccountKey } from '../CreateGoogleServiceAccountKey'
jest.mock('../../../../prompts');
jest.mock('fs');
jest.mocked(promptAsync).mockImplementation(async () => ({
asMock(promptAsync).mockImplementation(() => ({
filePath: '/google-service-account-key.json',
}));
@@ -1,3 +1,4 @@
import { asMock } from '../../../../__tests__/utils';
import { jester as mockJester } from '../../../../credentials/__tests__/fixtures-constants';
import { testKeystore } from '../../../__tests__/fixtures-android';
import { createCtxMock } from '../../../__tests__/fixtures-context';
@@ -14,7 +15,7 @@ jest.mock('../../utils/keystore', () => ({ generateRandomKeystoreAsync: jest.fn(
describe('CreateKeystore', () => {
beforeEach(() => {
jest.mocked(generateRandomKeystoreAsync).mockReset();
asMock(generateRandomKeystoreAsync).mockReset();
});
it('creates a keystore in Interactive Mode', async () => {
const ctx = createCtxMock({ nonInteractive: false });
@@ -27,7 +28,7 @@ describe('CreateKeystore', () => {
expect(generateRandomKeystoreAsync).toHaveBeenCalled();
});
it('creates a keystore by uploading', async () => {
jest.mocked(askForUserProvidedAsync).mockImplementationOnce(async () => testKeystore);
asMock(askForUserProvidedAsync).mockImplementationOnce(() => testKeystore);
const ctx = createCtxMock({ nonInteractive: false });
const appLookupParams = await getAppLookupParamsFromContextAsync(ctx);
const createKeystoreAction = new CreateKeystore(appLookupParams.account);
@@ -1,3 +1,4 @@
import { asMock } from '../../../../__tests__/utils';
import { confirmAsync } from '../../../../prompts';
import {
getNewAndroidApiMock,
@@ -8,7 +9,7 @@ import { getAppLookupParamsFromContextAsync } from '../BuildCredentialsUtils';
import { RemoveFcm } from '../RemoveFcm';
jest.mock('../../../../prompts');
jest.mocked(confirmAsync).mockImplementation(async () => true);
asMock(confirmAsync).mockImplementation(() => true);
describe(RemoveFcm, () => {
it('removes an FCM API Key', async () => {
@@ -1,3 +1,4 @@
import { asMock } from '../../../../__tests__/utils';
import { confirmAsync } from '../../../../prompts';
import {
getNewAndroidApiMock,
@@ -7,7 +8,7 @@ import { createCtxMock } from '../../../__tests__/fixtures-context';
import { RemoveGoogleServiceAccountKey } from '../RemoveGoogleServiceAccountKey';
jest.mock('../../../../prompts');
jest.mocked(confirmAsync).mockImplementation(async () => true);
asMock(confirmAsync).mockImplementation(() => true);
describe(RemoveGoogleServiceAccountKey, () => {
it('removes an Google Service Account Key', async () => {
@@ -1,3 +1,4 @@
import { asMock } from '../../../../__tests__/utils';
import { confirmAsync } from '../../../../prompts';
import { testAndroidBuildCredentialsFragment } from '../../../__tests__/fixtures-android';
import { createCtxMock } from '../../../__tests__/fixtures-context';
@@ -6,7 +7,7 @@ import { RemoveKeystore } from '../RemoveKeystore';
jest.mock('fs-extra');
jest.mock('../../../../prompts');
jest.mocked(confirmAsync).mockImplementation(async () => true);
asMock(confirmAsync).mockImplementation(() => true);
describe(RemoveKeystore, () => {
it('removes a keystore', async () => {
@@ -1,3 +1,4 @@
import { asMock } from '../../../../__tests__/utils';
import {
getNewAndroidApiMock,
testAndroidBuildCredentialsFragment,
@@ -18,7 +19,7 @@ jest.mock('../../utils/keystore', () => ({ generateRandomKeystoreAsync: jest.fn(
describe('SetUpBuildCredentials', () => {
beforeEach(() => {
jest.mocked(generateRandomKeystoreAsync).mockReset();
asMock(generateRandomKeystoreAsync).mockReset();
});
it('skips setup when there are prior credentials', async () => {
@@ -1,5 +1,6 @@
import { vol } from 'memfs';
import { asMock } from '../../../../__tests__/utils';
import { AndroidKeystoreType } from '../../../../graphql/generated';
import { confirmAsync } from '../../../../prompts';
import {
@@ -12,14 +13,13 @@ import {
SelectAndroidBuildCredentials,
SelectAndroidBuildCredentialsResultType,
} from '../../../manager/SelectAndroidBuildCredentials';
import { AppLookupParams } from '../../api/GraphqlClient';
import { getAppLookupParamsFromContextAsync } from '../BuildCredentialsUtils';
import { SetUpBuildCredentialsFromCredentialsJson } from '../SetUpBuildCredentialsFromCredentialsJson';
jest.mock('fs');
jest.mock('../../../../prompts');
jest.mocked(confirmAsync).mockImplementation(async () => true);
asMock(confirmAsync).mockImplementation(() => true);
jest.mock('../../../manager/SelectAndroidBuildCredentials');
beforeEach(() => {
@@ -28,7 +28,7 @@ beforeEach(() => {
describe(SetUpBuildCredentialsFromCredentialsJson, () => {
it('sets up a new build configuration from credentials.json upon user request', async () => {
jest.mocked(SelectAndroidBuildCredentials).mockImplementation((() => {
asMock(SelectAndroidBuildCredentials).mockImplementation(() => {
return {
runAsync: () => {
return {
@@ -37,7 +37,7 @@ describe(SetUpBuildCredentialsFromCredentialsJson, () => {
};
},
};
}) as any as (app: AppLookupParams) => SelectAndroidBuildCredentials);
});
const ctx = createCtxMock({
nonInteractive: false,
android: {
@@ -78,7 +78,7 @@ describe(SetUpBuildCredentialsFromCredentialsJson, () => {
expect(ctx.android.createAndroidAppBuildCredentialsAsync).toHaveBeenCalledTimes(1);
});
it('uses an existing build configuration upon user request', async () => {
jest.mocked(SelectAndroidBuildCredentials).mockImplementation((() => {
asMock(SelectAndroidBuildCredentials).mockImplementation(() => {
return {
runAsync: () => {
return {
@@ -87,7 +87,7 @@ describe(SetUpBuildCredentialsFromCredentialsJson, () => {
};
},
};
}) as any as (app: AppLookupParams) => SelectAndroidBuildCredentials);
});
const ctx = createCtxMock({
nonInteractive: false,
android: {
@@ -1,5 +1,6 @@
import { vol } from 'memfs';
import { asMock } from '../../../../__tests__/utils';
import { promptAsync } from '../../../../prompts';
import {
getNewAndroidApiMock,
@@ -13,7 +14,7 @@ import { SetUpGoogleServiceAccountKey } from '../SetUpGoogleServiceAccountKey';
jest.mock('../../../../prompts');
jest.mock('fs');
jest.mocked(promptAsync).mockImplementation(async () => ({
asMock(promptAsync).mockImplementation(() => ({
filePath: '/google-service-account-key.json',
}));
@@ -1,3 +1,4 @@
import { asMock } from '../../../../__tests__/utils';
import { promptAsync } from '../../../../prompts';
import {
getNewAndroidApiMock,
@@ -8,9 +9,7 @@ import { getAppLookupParamsFromContextAsync } from '../BuildCredentialsUtils';
import { UseExistingGoogleServiceAccountKey } from '../UseExistingGoogleServiceAccountKey';
jest.mock('../../../../prompts');
jest
.mocked(promptAsync)
.mockImplementation(async () => ({ chosenKey: testGoogleServiceAccountKeyFragment }));
asMock(promptAsync).mockImplementation(() => ({ chosenKey: testGoogleServiceAccountKeyFragment }));
describe(UseExistingGoogleServiceAccountKey, () => {
it('uses an existing Google Service Account Key in Interactive Mode', async () => {
@@ -1,5 +1,6 @@
import { vol } from 'memfs';
import { asMock } from '../../../../__tests__/utils';
import { confirmAsync, promptAsync } from '../../../../prompts';
import { detectGoogleServiceAccountKeyPathAsync } from '../googleServiceAccountKey';
@@ -24,13 +25,13 @@ afterAll(() => {
});
afterEach(() => {
jest.mocked(promptAsync).mockClear();
jest.mocked(confirmAsync).mockClear();
asMock(promptAsync).mockClear();
asMock(confirmAsync).mockClear();
});
describe('Google Service Account Key path detection', () => {
it('detects a single google-services file and prompts for confirmation', async () => {
jest.mocked(confirmAsync).mockResolvedValueOnce(true);
asMock(confirmAsync).mockResolvedValueOnce(true);
const serviceAccountPath = await detectGoogleServiceAccountKeyPathAsync('/project_dir/subdir');
expect(confirmAsync).toHaveBeenCalledWith(
expect.objectContaining({ message: 'Would you like to use this file?' })
@@ -45,7 +46,7 @@ describe('Google Service Account Key path detection', () => {
});
it('returns null, when user rejects to use detected file', async () => {
jest.mocked(confirmAsync).mockResolvedValueOnce(false);
asMock(confirmAsync).mockResolvedValueOnce(false);
const serviceAccountPath = await detectGoogleServiceAccountKeyPathAsync('/project_dir/subdir');
expect(confirmAsync).toHaveBeenCalledTimes(1);
expect(confirmAsync).toHaveBeenCalledWith(
@@ -55,7 +56,7 @@ describe('Google Service Account Key path detection', () => {
});
it('displays a chooser, when multiple files are found', async () => {
jest.mocked(promptAsync).mockResolvedValueOnce({
asMock(promptAsync).mockResolvedValueOnce({
selectedPath: '/project_dir/another-service-account.json',
});
const serviceAccountPath = await detectGoogleServiceAccountKeyPathAsync('/project_dir'); // should find 2 files here
@@ -75,7 +76,7 @@ describe('Google Service Account Key path detection', () => {
});
it('returns null, when user selects the "None of above"', async () => {
jest.mocked(promptAsync).mockResolvedValueOnce({
asMock(promptAsync).mockResolvedValueOnce({
selectedPath: false,
});
@@ -1,5 +1,6 @@
import mockdate from 'mockdate';
import { asMock } from '../../../../__tests__/utils';
import Log from '../../../../log';
import {
testAndroidAppCredentialsFragment,
@@ -24,9 +25,7 @@ describe('print credentials', () => {
appLookupParams,
appCredentials: testAndroidAppCredentialsFragment,
});
const loggedSoFar = jest
.mocked(Log.log)
.mock.calls.reduce((acc, mockValue) => acc + mockValue.toString(), '');
const loggedSoFar = asMock(Log.log).mock.calls.reduce((acc, mockValue) => acc + mockValue);
expect(loggedSoFar).toMatchSnapshot();
});
});
@@ -20,7 +20,7 @@ function fileIsServiceAccountKey(keyJsonPath: string): boolean {
try {
readAndValidateServiceAccountKey(keyJsonPath);
return true;
} catch {
} catch (err: any) {
return false;
}
}
@@ -24,7 +24,7 @@ export async function keytoolCommandExistsAsync(): Promise<boolean> {
try {
await spawnAsync('keytool');
return true;
} catch {
} catch (error) {
return false;
}
}
@@ -24,7 +24,7 @@ function isPKCSKeystore(keystore: Keystore): boolean {
try {
parsePKCS12(keystore.keystore, keystore.keystorePassword);
return true;
} catch {
} catch (e) {
return false;
}
}
@@ -33,7 +33,7 @@ function isJKSKeystore(keystore: Keystore): boolean {
try {
jks.parseJks(Buffer.from(keystore.keystore, 'base64'), keystore.keystorePassword);
return true;
} catch {
} catch (e) {
return false;
}
}
+1 -1
View File
@@ -50,7 +50,7 @@ export class CredentialsContext {
): ExpoConfig | null {
try {
return getExpoConfig(projectDir, { env });
} catch {
} catch (error) {
// ignore error, context might be created outside of expo project
return null;
}
@@ -2,6 +2,7 @@ import fs from 'fs-extra';
import { vol } from 'memfs';
import prompts from 'prompts';
import { asMock } from '../../../__tests__/utils';
import { IosDistributionType } from '../../../graphql/generated';
import {
testKeystore,
@@ -24,8 +25,8 @@ jest.mock('prompts');
describe('update credentials.json', () => {
beforeEach(() => {
vol.reset();
jest.mocked(prompts).mockReset();
jest.mocked(prompts).mockImplementation(() => {
asMock(prompts).mockReset();
asMock(prompts).mockImplementation(() => {
throw new Error(`unhandled prompts call - this shouldn't happen - fix tests!`);
});
});
@@ -175,7 +176,6 @@ describe('update credentials.json', () => {
{
targetName: 'testapp',
bundleIdentifier: 'com.bundle.id',
entitlements: {},
},
];
@@ -114,7 +114,7 @@ export async function readRawAsync(
try {
const credentialsJSONContents = await fs.readFile(credentialsJsonFilePath, 'utf8');
return JSON.parse(credentialsJSONContents);
} catch {
} catch (err) {
throw new Error(
`credentials.json must exist in the project root directory and contain a valid JSON`
);
@@ -11,6 +11,7 @@ import { ensureAllTargetsAreConfigured } from '../credentialsJson/utils';
import { getAppFromContext } from './actions/BuildCredentialsUtils';
import { SetUpBuildCredentials } from './actions/SetUpBuildCredentials';
import { SetUpPushKey } from './actions/SetUpPushKey';
import { IosCapabilitiesOptions } from './appstore/ensureAppExists';
import { App, IosCredentials, Target } from './types';
import { isAdHocProfile } from './utils/provisioningProfile';
@@ -19,6 +20,7 @@ interface Options {
targets: Target[];
distribution: DistributionType;
enterpriseProvisioning?: IosEnterpriseProvisioning;
iosCapabilitiesOptions?: IosCapabilitiesOptions;
}
export default class IosCredentialsProvider {
@@ -61,6 +63,7 @@ export default class IosCredentialsProvider {
targets: this.options.targets,
distribution: this.options.distribution,
enterpriseProvisioning: this.options.enterpriseProvisioning,
iosCapabilitiesOptions: this.options.iosCapabilitiesOptions,
}).runAsync(this.ctx);
}
@@ -58,13 +58,7 @@ describe(IosCredentialsProvider, () => {
},
projectName: appLookupParams.projectName,
},
targets: [
{
targetName: 'testapp',
bundleIdentifier: appLookupParams.bundleIdentifier,
entitlements: {},
},
],
targets: [{ targetName: 'testapp', bundleIdentifier: appLookupParams.bundleIdentifier }],
distribution: 'store',
});
await expect(provider.getCredentialsAsync(CredentialsSource.REMOTE)).rejects.toThrowError(
@@ -101,13 +95,7 @@ describe(IosCredentialsProvider, () => {
},
projectName: appLookupParams.projectName,
},
targets: [
{
targetName: 'testapp',
bundleIdentifier: appLookupParams.bundleIdentifier,
entitlements: {},
},
],
targets: [{ targetName: 'testapp', bundleIdentifier: appLookupParams.bundleIdentifier }],
distribution: 'store',
});
@@ -4,6 +4,7 @@ import nullthrows from 'nullthrows';
import Log from '../../../log';
import { CredentialsContext } from '../../context';
import { IosCapabilitiesOptions } from '../appstore/ensureAppExists';
import { App, IosAppBuildCredentialsMap, IosCredentials, Target } from '../types';
import { displayProjectCredentials } from '../utils/printCredentials';
import { SetUpTargetBuildCredentials } from './SetUpTargetBuildCredentials';
@@ -13,6 +14,7 @@ interface Options {
targets: Target[];
distribution: DistributionType;
enterpriseProvisioning?: IosEnterpriseProvisioning;
iosCapabilitiesOptions?: IosCapabilitiesOptions;
}
export class SetUpBuildCredentials {
@@ -48,9 +50,7 @@ export class SetUpBuildCredentials {
Log.newLine();
}
const action = new SetUpTargetBuildCredentials({
enterpriseProvisioning: this.options.enterpriseProvisioning,
distribution: this.options.distribution,
entitlements: target.entitlements,
...this.options,
app: {
...this.options.app,
bundleIdentifier: target.bundleIdentifier,
@@ -1,5 +1,4 @@
import { DistributionType, IosEnterpriseProvisioning } from '@expo/eas-json';
import { JSONObject } from '@expo/json-file';
import {
IosDistributionType as GraphQLIosDistributionType,
@@ -8,6 +7,7 @@ import {
import Log from '../../../log';
import { CredentialsContext } from '../../context';
import { AppLookupParams as GraphQLAppLookupParams } from '../api/GraphqlClient';
import { IosCapabilitiesOptions } from '../appstore/ensureAppExists';
import { SetUpAdhocProvisioningProfile } from './SetUpAdhocProvisioningProfile';
import { SetUpInternalProvisioningProfile } from './SetUpInternalProvisioningProfile';
import { SetUpProvisioningProfile } from './SetUpProvisioningProfile';
@@ -16,13 +16,13 @@ interface Options {
app: GraphQLAppLookupParams;
distribution: DistributionType;
enterpriseProvisioning?: IosEnterpriseProvisioning;
entitlements: JSONObject;
iosCapabilitiesOptions?: IosCapabilitiesOptions;
}
export class SetUpTargetBuildCredentials {
constructor(private options: Options) {}
async runAsync(ctx: CredentialsContext): Promise<IosAppBuildCredentialsFragment> {
const { app, entitlements } = this.options;
const { app, iosCapabilitiesOptions } = this.options;
await ctx.bestEffortAppStoreAuthenticateAsync();
@@ -33,7 +33,7 @@ export class SetUpTargetBuildCredentials {
bundleIdentifier: app.bundleIdentifier,
projectName: app.projectName,
},
{ entitlements }
iosCapabilitiesOptions
);
}
try {
@@ -1,3 +1,4 @@
import { asMock } from '../../../../__tests__/utils';
import { promptAsync } from '../../../../prompts';
import { getAppstoreMock, testAuthCtx } from '../../../__tests__/fixtures-appstore';
import { createCtxMock } from '../../../__tests__/fixtures-context';
@@ -17,8 +18,8 @@ function enumKeys<O extends object, K extends keyof O = keyof O>(obj: O): K[] {
}
afterEach(() => {
jest.mocked(promptAsync).mockClear();
jest.mocked(getCredentialsFromUserAsync).mockClear();
asMock(promptAsync).mockClear();
asMock(getCredentialsFromUserAsync).mockClear();
});
describe(getAscApiKeyName, () => {
@@ -34,10 +35,10 @@ describe(getAscApiKeyName, () => {
describe(promptForAscApiKeyPathAsync, () => {
it('prompts for keyId, keyP8Path and issuerId when user is not authenticated to Apple', async () => {
jest.mocked(promptAsync).mockImplementationOnce(async () => ({
asMock(promptAsync).mockImplementationOnce(() => ({
keyP8Path: '/asc-api-key.p8',
}));
jest.mocked(getCredentialsFromUserAsync).mockImplementation(async () => ({
asMock(getCredentialsFromUserAsync).mockImplementation(() => ({
keyId: 'test-key-id',
issuerId: 'test-issuer-id',
}));
@@ -61,10 +62,10 @@ describe(promptForAscApiKeyPathAsync, () => {
expect(getCredentialsFromUserAsync).toHaveBeenCalledTimes(2); // keyId, issuerId
});
it('prompts for keyId, keyP8Path and detects issuerId when user is authenticated to Apple', async () => {
jest.mocked(promptAsync).mockImplementationOnce(async () => ({
asMock(promptAsync).mockImplementationOnce(() => ({
keyP8Path: '/asc-api-key.p8',
}));
jest.mocked(getCredentialsFromUserAsync).mockImplementation(async () => ({
asMock(getCredentialsFromUserAsync).mockImplementation(() => ({
keyId: 'test-key-id',
}));
const ctx = createCtxMock({
@@ -84,6 +85,6 @@ describe(promptForAscApiKeyPathAsync, () => {
});
expect(promptAsync).toHaveBeenCalledTimes(1); // keyP8Path
expect(getCredentialsFromUserAsync).toHaveBeenCalledTimes(1); // keyId
expect(jest.mocked(ctx.appStore.getAscApiKeyAsync).mock.calls.length).toBe(1); // issuerId
expect(asMock(ctx.appStore.getAscApiKeyAsync).mock.calls.length).toBe(1); // issuerId
});
});
@@ -1,3 +1,4 @@
import { asMock } from '../../../../__tests__/utils';
import { findApplicationTarget } from '../../../../project/ios/target';
import { confirmAsync } from '../../../../prompts';
import { getAppstoreMock, testAuthCtx } from '../../../__tests__/fixtures-appstore';
@@ -14,7 +15,7 @@ import { ConfigureProvisioningProfile } from '../ConfigureProvisioningProfile';
jest.mock('../../../../ora');
jest.mock('../../../../prompts');
jest.mocked(confirmAsync).mockImplementation(async () => true);
asMock(confirmAsync).mockImplementation(() => true);
describe('ConfigureProvisioningProfile', () => {
it('configures a Provisioning Profile in Interactive Mode', async () => {
@@ -42,9 +43,9 @@ describe('ConfigureProvisioningProfile', () => {
await provProfConfigurator.runAsync(ctx);
// expect provisioning profile not to be updated on expo servers
expect(jest.mocked(ctx.ios.updateProvisioningProfileAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.ios.updateProvisioningProfileAsync).mock.calls.length).toBe(1);
// expect provisioning profile not to be updated on apple portal
expect(jest.mocked(ctx.appStore.useExistingProvisioningProfileAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.appStore.useExistingProvisioningProfileAsync).mock.calls.length).toBe(1);
});
it('errors in Non Interactive Mode', async () => {
const ctx = createCtxMock({
@@ -61,8 +62,8 @@ describe('ConfigureProvisioningProfile', () => {
);
// expect provisioning profile not to be updated on expo servers
expect(jest.mocked(ctx.ios.updateProvisioningProfileAsync).mock.calls.length).toBe(0);
expect(asMock(ctx.ios.updateProvisioningProfileAsync).mock.calls.length).toBe(0);
// expect provisioning profile not to be updated on apple portal
expect(jest.mocked(ctx.appStore.useExistingProvisioningProfileAsync).mock.calls.length).toBe(0);
expect(asMock(ctx.appStore.useExistingProvisioningProfileAsync).mock.calls.length).toBe(0);
});
});
@@ -1,3 +1,4 @@
import { asMock } from '../../../../__tests__/utils';
import { findApplicationTarget } from '../../../../project/ios/target';
import { confirmAsync } from '../../../../prompts';
import { getAppstoreMock, testAuthCtx } from '../../../__tests__/fixtures-appstore';
@@ -8,7 +9,7 @@ import { getAppLookupParamsFromContext } from '../BuildCredentialsUtils';
import { CreateAscApiKey } from '../CreateAscApiKey';
jest.mock('../../../../prompts');
jest.mocked(confirmAsync).mockImplementation(async () => true);
asMock(confirmAsync).mockImplementation(() => true);
describe(CreateAscApiKey, () => {
it('creates a App Store API Key in Interactive Mode', async () => {
@@ -26,9 +27,9 @@ describe(CreateAscApiKey, () => {
await createAscApiKeyAction.runAsync(ctx, AppStoreApiKeyPurpose.SUBMISSION_SERVICE);
// expect api key to be created on expo servers
expect(jest.mocked(ctx.ios.createAscApiKeyAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.ios.createAscApiKeyAsync).mock.calls.length).toBe(1);
// expect api key to be created on apple portal
expect(jest.mocked(ctx.appStore.createAscApiKeyAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.appStore.createAscApiKeyAsync).mock.calls.length).toBe(1);
});
it('errors in Non Interactive Mode', async () => {
const ctx = createCtxMock({
@@ -1,3 +1,4 @@
import { asMock } from '../../../../__tests__/utils';
import { findApplicationTarget } from '../../../../project/ios/target';
import { confirmAsync } from '../../../../prompts';
import { getAppstoreMock, testAuthCtx } from '../../../__tests__/fixtures-appstore';
@@ -12,7 +13,7 @@ import { getAppLookupParamsFromContext } from '../BuildCredentialsUtils';
import { CreateProvisioningProfile } from '../CreateProvisioningProfile';
jest.mock('../../../../prompts');
jest.mocked(confirmAsync).mockImplementation(async () => true);
asMock(confirmAsync).mockImplementation(() => true);
describe('CreateProvisioningProfile', () => {
it('creates a Provisioning Profile in Interactive Mode', async () => {
@@ -33,9 +34,9 @@ describe('CreateProvisioningProfile', () => {
await createProvProfAction.runAsync(ctx);
// expect provisioning profile to be created on expo servers
expect(jest.mocked(ctx.ios.createProvisioningProfileAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.ios.createProvisioningProfileAsync).mock.calls.length).toBe(1);
// expect provisioning profile to be created on apple portal
expect(jest.mocked(ctx.appStore.createProvisioningProfileAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.appStore.createProvisioningProfileAsync).mock.calls.length).toBe(1);
});
it('errors in Non Interactive Mode', async () => {
const ctx = createCtxMock({
@@ -51,8 +52,8 @@ describe('CreateProvisioningProfile', () => {
);
// expect provisioning profile not to be created on expo servers
expect(jest.mocked(ctx.ios.createProvisioningProfileAsync).mock.calls.length).toBe(0);
expect(asMock(ctx.ios.createProvisioningProfileAsync).mock.calls.length).toBe(0);
// expect provisioning profile not to be created on apple portal
expect(jest.mocked(ctx.appStore.createProvisioningProfileAsync).mock.calls.length).toBe(0);
expect(asMock(ctx.appStore.createProvisioningProfileAsync).mock.calls.length).toBe(0);
});
});
@@ -1,3 +1,4 @@
import { asMock } from '../../../../__tests__/utils';
import { findApplicationTarget } from '../../../../project/ios/target';
import { confirmAsync } from '../../../../prompts';
import { getAppstoreMock, testAuthCtx } from '../../../__tests__/fixtures-appstore';
@@ -7,7 +8,7 @@ import { getAppLookupParamsFromContext } from '../BuildCredentialsUtils';
import { CreatePushKey } from '../CreatePushKey';
jest.mock('../../../../prompts');
jest.mocked(confirmAsync).mockImplementation(async () => true);
asMock(confirmAsync).mockImplementation(() => true);
describe(CreatePushKey, () => {
it('creates a Push Key in Interactive Mode', async () => {
@@ -24,9 +25,9 @@ describe(CreatePushKey, () => {
await createPushKeyAction.runAsync(ctx);
// expect push key to be created on expo servers
expect(jest.mocked(ctx.ios.createPushKeyAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.ios.createPushKeyAsync).mock.calls.length).toBe(1);
// expect push key to be created on apple portal
expect(jest.mocked(ctx.appStore.createPushKeyAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.appStore.createPushKeyAsync).mock.calls.length).toBe(1);
});
it('errors in Non Interactive Mode', async () => {
const ctx = createCtxMock({
@@ -1,10 +1,11 @@
import { asMock } from '../../../../__tests__/utils';
import { confirmAsync } from '../../../../prompts';
import { createCtxMock } from '../../../__tests__/fixtures-context';
import { testAscApiKeyFragment } from '../../../__tests__/fixtures-ios';
import { RemoveAscApiKey } from '../RemoveAscApiKey';
jest.mock('../../../../prompts');
jest.mocked(confirmAsync).mockImplementation(async () => true);
asMock(confirmAsync).mockImplementation(() => true);
describe(RemoveAscApiKey, () => {
it('removes an Asc API Key', async () => {
@@ -1,3 +1,4 @@
import { asMock } from '../../../../__tests__/utils';
import { findApplicationTarget } from '../../../../project/ios/target';
import { confirmAsync } from '../../../../prompts';
import { createCtxMock } from '../../../__tests__/fixtures-context';
@@ -10,7 +11,7 @@ import { getAppLookupParamsFromContext } from '../BuildCredentialsUtils';
import { RemoveDistributionCertificate } from '../RemoveDistributionCertificate';
jest.mock('../../../../prompts');
jest.mocked(confirmAsync).mockImplementation(async () => true);
asMock(confirmAsync).mockImplementation(() => true);
describe('RemoveDistributionCertificate', () => {
it('deletes the distribution certificate on Expo and Apple servers when there are no App Dependencies in Interactive Mode', async () => {
@@ -23,11 +24,11 @@ describe('RemoveDistributionCertificate', () => {
await removeDistCertAction.runAsync(ctx);
// expect dist cert to be deleted on expo servers
expect(jest.mocked(ctx.ios.deleteDistributionCertificateAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.ios.deleteDistributionCertificateAsync).mock.calls.length).toBe(1);
// expect dist cert to be deleted on apple portal
expect(jest.mocked(ctx.appStore.revokeDistributionCertificateAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.appStore.revokeDistributionCertificateAsync).mock.calls.length).toBe(1);
// expect provisioning profile deletion to be skipped because there arent any associated with the dist cert
expect(jest.mocked(ctx.ios.deleteProvisioningProfilesAsync).mock.calls.length).toBe(0);
expect(asMock(ctx.ios.deleteProvisioningProfilesAsync).mock.calls.length).toBe(0);
});
it('deletes the distribution certificate on Expo servers when there are no App Dependencies in Non-Interactive Mode', async () => {
const ctx = createCtxMock({ nonInteractive: true });
@@ -39,11 +40,11 @@ describe('RemoveDistributionCertificate', () => {
await removeDistCertAction.runAsync(ctx);
// expect dist cert to be deleted on expo servers
expect(jest.mocked(ctx.ios.deleteDistributionCertificateAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.ios.deleteDistributionCertificateAsync).mock.calls.length).toBe(1);
// not supported in non-interactive mode
expect(jest.mocked(ctx.appStore.revokeDistributionCertificateAsync).mock.calls.length).toBe(0);
expect(asMock(ctx.appStore.revokeDistributionCertificateAsync).mock.calls.length).toBe(0);
// expect provisioning profile deletion to be skipped because there arent any associated with the dist cert
expect(jest.mocked(ctx.ios.deleteProvisioningProfilesAsync).mock.calls.length).toBe(0);
expect(asMock(ctx.ios.deleteProvisioningProfilesAsync).mock.calls.length).toBe(0);
});
it('deletes the distribution certificate and its provisioning profile on Expo and Apple servers when there are App Dependencies in Interactive Mode', async () => {
const ctx = createCtxMock({ nonInteractive: false });
@@ -55,11 +56,11 @@ describe('RemoveDistributionCertificate', () => {
await removeDistCertAction.runAsync(ctx);
// expect dist cert to be deleted on expo servers
expect(jest.mocked(ctx.ios.deleteDistributionCertificateAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.ios.deleteDistributionCertificateAsync).mock.calls.length).toBe(1);
// expect dist cert to be deleted on apple portal
expect(jest.mocked(ctx.appStore.revokeDistributionCertificateAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.appStore.revokeDistributionCertificateAsync).mock.calls.length).toBe(1);
// expect provisioning profile deletion to be invoked on expo servers
expect(jest.mocked(ctx.ios.deleteProvisioningProfilesAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.ios.deleteProvisioningProfilesAsync).mock.calls.length).toBe(1);
});
it('errors when the distribution certificate has App Dependencies in Non-Interactive Mode', async () => {
const ctx = createCtxMock({ nonInteractive: true });
@@ -1,3 +1,4 @@
import { asMock } from '../../../../__tests__/utils';
import { findApplicationTarget } from '../../../../project/ios/target';
import { createCtxMock } from '../../../__tests__/fixtures-context';
import { testTargets } from '../../../__tests__/fixtures-ios';
@@ -19,6 +20,6 @@ describe('RemoveProvisioningProfile', () => {
await removeProfilesAction.runAsync(ctx);
// expect provisioning profile to be deleted from servers
expect(jest.mocked(ctx.ios.deleteProvisioningProfilesAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.ios.deleteProvisioningProfilesAsync).mock.calls.length).toBe(1);
});
});
@@ -1,10 +1,11 @@
import { asMock } from '../../../../__tests__/utils';
import { confirmAsync } from '../../../../prompts';
import { createCtxMock } from '../../../__tests__/fixtures-context';
import { testPushKey } from '../../../__tests__/fixtures-ios';
import { RemovePushKey } from '../RemovePushKey';
jest.mock('../../../../prompts');
jest.mocked(confirmAsync).mockImplementation(async () => true);
asMock(confirmAsync).mockImplementation(() => true);
describe(RemovePushKey, () => {
it('deletes the push key on Expo and Apple servers in Interactive Mode', async () => {
@@ -13,9 +14,9 @@ describe(RemovePushKey, () => {
await removePushKeyAction.runAsync(ctx);
// expect push key to be deleted on expo servers
expect(jest.mocked(ctx.ios.deletePushKeyAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.ios.deletePushKeyAsync).mock.calls.length).toBe(1);
// expect push key to be revoked on apple portal
expect(jest.mocked(ctx.appStore.revokePushKeyAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.appStore.revokePushKeyAsync).mock.calls.length).toBe(1);
});
it('errors in Non-Interactive Mode', async () => {
const ctx = createCtxMock({ nonInteractive: true });
@@ -1,3 +1,4 @@
import { asMock } from '../../../../__tests__/utils';
import { findApplicationTarget } from '../../../../project/ios/target';
import { confirmAsync, promptAsync } from '../../../../prompts';
import { getAppstoreMock, testAuthCtx } from '../../../__tests__/fixtures-appstore';
@@ -14,7 +15,7 @@ import { getAppLookupParamsFromContext } from '../BuildCredentialsUtils';
import { SetUpAscApiKey, SetupAscApiKeyChoice } from '../SetUpAscApiKey';
jest.mock('../../../../prompts');
jest.mocked(confirmAsync).mockImplementation(async () => true);
asMock(confirmAsync).mockImplementation(() => true);
describe(SetUpAscApiKey, () => {
it('skips setting up a ASC API Key if it is already configured', async () => {
@@ -35,9 +36,9 @@ describe(SetUpAscApiKey, () => {
await setupAscApiKeyAction.runAsync(ctx);
// expect not to create a new ASC API Key on expo servers
expect(jest.mocked(ctx.ios.createAscApiKeyAsync).mock.calls.length).toBe(0);
expect(asMock(ctx.ios.createAscApiKeyAsync).mock.calls.length).toBe(0);
// expect configuration not to be updated with a new ASC API Key
expect(jest.mocked(ctx.ios.updateIosAppCredentialsAsync).mock.calls.length).toBe(0);
expect(asMock(ctx.ios.updateIosAppCredentialsAsync).mock.calls.length).toBe(0);
});
it('sets up a ASC API Key, creating a new one if there are no existing ASC API Keys', async () => {
const ctx = createCtxMock({
@@ -60,9 +61,9 @@ describe(SetUpAscApiKey, () => {
await setupAscApiKeyAction.runAsync(ctx);
// expect to create a new ASC API Key on expo servers
expect(jest.mocked(ctx.ios.createAscApiKeyAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.ios.createAscApiKeyAsync).mock.calls.length).toBe(1);
// expect configuration to be updated with a new ASC API Key
expect(jest.mocked(ctx.ios.updateIosAppCredentialsAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.ios.updateIosAppCredentialsAsync).mock.calls.length).toBe(1);
});
it('sets up a ASC API Key, use autoselected ASC API Key when there are existing keys', async () => {
const ctx = createCtxMock({
@@ -86,9 +87,9 @@ describe(SetUpAscApiKey, () => {
await setupAscApiKeyAction.runAsync(ctx);
// expect not to create a new ASC API Key on expo servers, we are using the existing one
expect(jest.mocked(ctx.ios.createAscApiKeyAsync).mock.calls.length).toBe(0);
expect(asMock(ctx.ios.createAscApiKeyAsync).mock.calls.length).toBe(0);
// expect configuration to be updated with a new ASC API Key
expect(jest.mocked(ctx.ios.updateIosAppCredentialsAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.ios.updateIosAppCredentialsAsync).mock.calls.length).toBe(1);
});
it('sets up a ASC API Key, allowing user to choose existing keys', async () => {
const ctx = createCtxMock({
@@ -98,7 +99,7 @@ describe(SetUpAscApiKey, () => {
getAscApiKeysForAccountAsync: jest.fn(() => [testAscApiKeyFragment]),
},
});
jest.mocked(promptAsync).mockImplementation(async () => ({
asMock(promptAsync).mockImplementation(() => ({
choice: SetupAscApiKeyChoice.USE_EXISTING,
chosenAscApiKey: testAscApiKeyFragment,
}));
@@ -110,9 +111,9 @@ describe(SetUpAscApiKey, () => {
await setupAscApiKeyAction.runAsync(ctx);
// expect not to create a new ASC API Key on expo servers, we are using the existing one
expect(jest.mocked(ctx.ios.createAscApiKeyAsync).mock.calls.length).toBe(0);
expect(asMock(ctx.ios.createAscApiKeyAsync).mock.calls.length).toBe(0);
// expect configuration to be updated with a new ASC API Key
expect(jest.mocked(ctx.ios.updateIosAppCredentialsAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.ios.updateIosAppCredentialsAsync).mock.calls.length).toBe(1);
});
it('works in Non Interactive Mode if ASC Key is configured', async () => {
const ctx = createCtxMock({
@@ -1,3 +1,4 @@
import { asMock } from '../../../../__tests__/utils';
import { IosAppBuildCredentialsFragment, IosDistributionType } from '../../../../graphql/generated';
import { promptAsync } from '../../../../prompts';
import { getAppstoreMock, testAuthCtx } from '../../../__tests__/fixtures-appstore';
@@ -13,10 +14,10 @@ jest.mock('../SetUpProvisioningProfile');
jest.mock('../BuildCredentialsUtils', () => ({ getAllBuildCredentialsAsync: jest.fn() }));
beforeEach(() => {
jest.mocked(promptAsync).mockReset();
asMock(promptAsync).mockReset();
jest.mocked(getAllBuildCredentialsAsync).mockReset();
jest.mocked(getAllBuildCredentialsAsync).mockImplementation(() => {
asMock(getAllBuildCredentialsAsync).mockReset();
asMock(getAllBuildCredentialsAsync).mockImplementation(() => {
throw new Error(
`unhandled getAllBuildCredentialsAsync call - this shouldn't happen - fix tests!`
);
@@ -36,7 +37,7 @@ describe(SetUpInternalProvisioningProfile, () => {
describe('interactive mode', () => {
describe('when authenticated with apple', () => {
it('runs the SetUpAdhocProvisioningProfile action for non-enterprise team', async () => {
jest.mocked(getAllBuildCredentialsAsync).mockImplementationOnce(async () => {
asMock(getAllBuildCredentialsAsync).mockImplementationOnce(() => {
const buildCredentials: IosAppBuildCredentialsFragment[] = [];
return buildCredentials;
});
@@ -63,10 +64,10 @@ describe(SetUpInternalProvisioningProfile, () => {
});
it('asks the user for an action to run when they have access to an enterprise team', async () => {
jest.mocked(promptAsync).mockImplementationOnce(async () => ({
asMock(promptAsync).mockImplementationOnce(() => ({
distributionType: IosDistributionType.Enterprise,
}));
jest.mocked(getAllBuildCredentialsAsync).mockImplementationOnce(async () => {
asMock(getAllBuildCredentialsAsync).mockImplementationOnce(() => {
const buildCredentials: IosAppBuildCredentialsFragment[] = [];
return buildCredentials;
});
@@ -98,10 +99,10 @@ describe(SetUpInternalProvisioningProfile, () => {
});
describe('when not authenticated with apple', () => {
it('asks the user for an action to run when both adhoc and universal distribution credentials exist', async () => {
jest.mocked(promptAsync).mockImplementationOnce(async () => ({
asMock(promptAsync).mockImplementationOnce(() => ({
distributionType: IosDistributionType.Enterprise,
}));
jest.mocked(getAllBuildCredentialsAsync).mockImplementationOnce(async () => {
asMock(getAllBuildCredentialsAsync).mockImplementationOnce(() => {
const buildCredentials: IosAppBuildCredentialsFragment[] = [
testAdhocBuildCredentials,
testEnterpriseBuildCredentials,
@@ -132,10 +133,10 @@ describe(SetUpInternalProvisioningProfile, () => {
});
it('runs the SetUpAdhocProvisioningProfile action when adhoc credentials exist', async () => {
jest.mocked(promptAsync).mockImplementationOnce(async () => ({
asMock(promptAsync).mockImplementationOnce(() => ({
distributionType: IosDistributionType.Enterprise,
}));
jest.mocked(getAllBuildCredentialsAsync).mockImplementationOnce(async () => {
asMock(getAllBuildCredentialsAsync).mockImplementationOnce(() => {
const buildCredentials: IosAppBuildCredentialsFragment[] = [testAdhocBuildCredentials];
return buildCredentials;
});
@@ -163,10 +164,10 @@ describe(SetUpInternalProvisioningProfile, () => {
});
it('runs the SetUpProvisioningProfile action when enterprise credentials exist', async () => {
jest.mocked(promptAsync).mockImplementationOnce(async () => ({
asMock(promptAsync).mockImplementationOnce(() => ({
distributionType: IosDistributionType.Enterprise,
}));
jest.mocked(getAllBuildCredentialsAsync).mockImplementationOnce(async () => {
asMock(getAllBuildCredentialsAsync).mockImplementationOnce(() => {
const buildCredentials: IosAppBuildCredentialsFragment[] = [
testEnterpriseBuildCredentials,
];
@@ -196,10 +197,10 @@ describe(SetUpInternalProvisioningProfile, () => {
});
it('forces the apple authentication when neither adhoc nor enterprise credentials exist', async () => {
jest.mocked(promptAsync).mockImplementationOnce(async () => ({
asMock(promptAsync).mockImplementationOnce(() => ({
distributionType: IosDistributionType.Enterprise,
}));
jest.mocked(getAllBuildCredentialsAsync).mockImplementationOnce(async () => {
asMock(getAllBuildCredentialsAsync).mockImplementationOnce(() => {
const buildCredentials: IosAppBuildCredentialsFragment[] = [];
return buildCredentials;
});
@@ -227,7 +228,7 @@ describe(SetUpInternalProvisioningProfile, () => {
describe('non-interactive mode', () => {
it('throws an error when both adhoc and enterprise credentials are set up', async () => {
jest.mocked(getAllBuildCredentialsAsync).mockImplementationOnce(async () => {
asMock(getAllBuildCredentialsAsync).mockImplementationOnce(() => {
const buildCredentials: IosAppBuildCredentialsFragment[] = [
testAdhocBuildCredentials,
testEnterpriseBuildCredentials,
@@ -248,7 +249,7 @@ describe(SetUpInternalProvisioningProfile, () => {
});
it('throws an error when neither adhoc nor enterprise credentials are set up', async () => {
jest.mocked(getAllBuildCredentialsAsync).mockImplementationOnce(async () => {
asMock(getAllBuildCredentialsAsync).mockImplementationOnce(() => {
const buildCredentials: IosAppBuildCredentialsFragment[] = [];
return buildCredentials;
});
@@ -264,7 +265,7 @@ describe(SetUpInternalProvisioningProfile, () => {
});
it('runs the SetUpAdhocProvisioningProfile action when adhoc credentials exist', async () => {
jest.mocked(getAllBuildCredentialsAsync).mockImplementationOnce(async () => {
asMock(getAllBuildCredentialsAsync).mockImplementationOnce(() => {
const buildCredentials: IosAppBuildCredentialsFragment[] = [testAdhocBuildCredentials];
return buildCredentials;
});
@@ -286,7 +287,7 @@ describe(SetUpInternalProvisioningProfile, () => {
});
it('runs the SetUpProvisioningProfile action when enterprise credentials exist', async () => {
jest.mocked(getAllBuildCredentialsAsync).mockImplementationOnce(async () => {
asMock(getAllBuildCredentialsAsync).mockImplementationOnce(() => {
const buildCredentials: IosAppBuildCredentialsFragment[] = [testEnterpriseBuildCredentials];
return buildCredentials;
});
@@ -1,5 +1,6 @@
import nullthrows from 'nullthrows';
import { asMock } from '../../../../__tests__/utils';
import { IosDistributionType } from '../../../../graphql/generated';
import { findApplicationTarget } from '../../../../project/ios/target';
import { confirmAsync } from '../../../../prompts';
@@ -18,7 +19,7 @@ import { getAppLookupParamsFromContext } from '../BuildCredentialsUtils';
import { SetUpProvisioningProfile } from '../SetUpProvisioningProfile';
jest.mock('../../../../prompts');
jest.mocked(confirmAsync).mockImplementation(async () => true);
asMock(confirmAsync).mockImplementation(() => true);
jest.mock('../SetUpDistributionCertificate');
jest.mock('../ConfigureProvisioningProfile');
jest.mock('../CreateProvisioningProfile');
@@ -26,7 +27,7 @@ jest.mock('../../validators/validateProvisioningProfile');
describe('SetUpProvisioningProfile', () => {
it('repairs existing Provisioning Profile with bad build credentials in Interactive Mode', async () => {
jest.mocked(validateProvisioningProfileAsync).mockImplementation(async () => false);
asMock(validateProvisioningProfileAsync).mockImplementation(() => false);
const ctx = createCtxMock({
nonInteractive: false,
appStore: {
@@ -60,14 +61,12 @@ describe('SetUpProvisioningProfile', () => {
await setupProvisioningProfileAction.runAsync(ctx);
// expect build credentials to be created or updated on expo servers
expect(jest.mocked(ctx.ios.createOrUpdateIosAppBuildCredentialsAsync).mock.calls.length).toBe(
1
);
expect(asMock(ctx.ios.createOrUpdateIosAppBuildCredentialsAsync).mock.calls.length).toBe(1);
// expect provisioning profile not to be deleted on expo servers
expect(jest.mocked(ctx.ios.deleteProvisioningProfilesAsync).mock.calls.length).toBe(0);
expect(asMock(ctx.ios.deleteProvisioningProfilesAsync).mock.calls.length).toBe(0);
});
it('sets up a new Provisioning Profile with bad build credentials in Interactive Mode', async () => {
jest.mocked(validateProvisioningProfileAsync).mockImplementation(async () => false);
asMock(validateProvisioningProfileAsync).mockImplementation(() => false);
const ctx = createCtxMock({
nonInteractive: false,
appStore: {
@@ -92,14 +91,12 @@ describe('SetUpProvisioningProfile', () => {
await setupProvisioningProfileAction.runAsync(ctx);
// expect build credentials to be created or updated on expo servers
expect(jest.mocked(ctx.ios.createOrUpdateIosAppBuildCredentialsAsync).mock.calls.length).toBe(
1
);
expect(asMock(ctx.ios.createOrUpdateIosAppBuildCredentialsAsync).mock.calls.length).toBe(1);
// expect provisioning profile to be deleted on expo servers
expect(jest.mocked(ctx.ios.deleteProvisioningProfilesAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.ios.deleteProvisioningProfilesAsync).mock.calls.length).toBe(1);
});
it('skips setting up a Provisioning Profile with prior build credentials configured properly in Interactive Mode', async () => {
jest.mocked(validateProvisioningProfileAsync).mockImplementation(async () => true);
asMock(validateProvisioningProfileAsync).mockImplementation(() => true);
const ctx = createCtxMock({
nonInteractive: false,
appStore: {
@@ -124,14 +121,12 @@ describe('SetUpProvisioningProfile', () => {
await setupProvisioningProfileAction.runAsync(ctx);
// expect build credentials not to be created or updated on expo servers
expect(jest.mocked(ctx.ios.createOrUpdateIosAppBuildCredentialsAsync).mock.calls.length).toBe(
0
);
expect(asMock(ctx.ios.createOrUpdateIosAppBuildCredentialsAsync).mock.calls.length).toBe(0);
// expect provisioning profile not to be deleted on expo servers
expect(jest.mocked(ctx.ios.deleteProvisioningProfilesAsync).mock.calls.length).toBe(0);
expect(asMock(ctx.ios.deleteProvisioningProfilesAsync).mock.calls.length).toBe(0);
});
it('sets up a Provisioning Profile with no prior build credentials configured in Interactive Mode', async () => {
jest.mocked(validateProvisioningProfileAsync).mockImplementation(async () => false);
asMock(validateProvisioningProfileAsync).mockImplementation(() => false);
const ctx = createCtxMock({
nonInteractive: false,
appStore: {
@@ -152,14 +147,12 @@ describe('SetUpProvisioningProfile', () => {
await setupProvisioningProfileAction.runAsync(ctx);
// expect build credentials to be created or updated on expo servers
expect(jest.mocked(ctx.ios.createOrUpdateIosAppBuildCredentialsAsync).mock.calls.length).toBe(
1
);
expect(asMock(ctx.ios.createOrUpdateIosAppBuildCredentialsAsync).mock.calls.length).toBe(1);
// expect provisioning profile not to be deleted on expo servers
expect(jest.mocked(ctx.ios.deleteProvisioningProfilesAsync).mock.calls.length).toBe(0);
expect(asMock(ctx.ios.deleteProvisioningProfilesAsync).mock.calls.length).toBe(0);
});
it('errors in Non Interactive Mode', async () => {
jest.mocked(validateProvisioningProfileAsync).mockImplementation(async () => false);
asMock(validateProvisioningProfileAsync).mockImplementation(() => false);
const ctx = createCtxMock({
nonInteractive: true,
});
@@ -173,10 +166,8 @@ describe('SetUpProvisioningProfile', () => {
);
// expect build credentials not to be created or updated on expo servers
expect(jest.mocked(ctx.ios.createOrUpdateIosAppBuildCredentialsAsync).mock.calls.length).toBe(
0
);
expect(asMock(ctx.ios.createOrUpdateIosAppBuildCredentialsAsync).mock.calls.length).toBe(0);
// expect provisioning profile not to be deleted on expo servers
expect(jest.mocked(ctx.ios.deleteProvisioningProfilesAsync).mock.calls.length).toBe(0);
expect(asMock(ctx.ios.deleteProvisioningProfilesAsync).mock.calls.length).toBe(0);
});
});
@@ -1,3 +1,4 @@
import { asMock } from '../../../../__tests__/utils';
import { findApplicationTarget } from '../../../../project/ios/target';
import { confirmAsync } from '../../../../prompts';
import { getAppstoreMock, testAuthCtx } from '../../../__tests__/fixtures-appstore';
@@ -7,7 +8,7 @@ import { getAppLookupParamsFromContext } from '../BuildCredentialsUtils';
import { SetUpPushKey } from '../SetUpPushKey';
jest.mock('../../../../prompts');
jest.mocked(confirmAsync).mockImplementation(async () => true);
asMock(confirmAsync).mockImplementation(() => true);
describe(SetUpPushKey, () => {
it('skips setting up a Push Key if it is already configured', async () => {
@@ -23,9 +24,9 @@ describe(SetUpPushKey, () => {
await setupPushKeyAction.runAsync(ctx);
// expect not to create a new push key on expo servers
expect(jest.mocked(ctx.ios.createPushKeyAsync).mock.calls.length).toBe(0);
expect(asMock(ctx.ios.createPushKeyAsync).mock.calls.length).toBe(0);
// expect configuration not to be updated with a new push key
expect(jest.mocked(ctx.ios.updateIosAppCredentialsAsync).mock.calls.length).toBe(0);
expect(asMock(ctx.ios.updateIosAppCredentialsAsync).mock.calls.length).toBe(0);
});
it('sets up a Push Key, creating a new one if there are no existing push keys', async () => {
const ctx = createCtxMock({
@@ -41,9 +42,9 @@ describe(SetUpPushKey, () => {
await setupPushKeyAction.runAsync(ctx);
// expect to create a new push key on expo servers
expect(jest.mocked(ctx.ios.createPushKeyAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.ios.createPushKeyAsync).mock.calls.length).toBe(1);
// expect configuration to be updated with a new push key
expect(jest.mocked(ctx.ios.updateIosAppCredentialsAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.ios.updateIosAppCredentialsAsync).mock.calls.length).toBe(1);
});
it('sets up a Push Key, use autoselected push key when there are existing keys', async () => {
const ctx = createCtxMock({
@@ -65,9 +66,9 @@ describe(SetUpPushKey, () => {
await setupPushKeyAction.runAsync(ctx);
// expect not to create a new push key on expo servers, we are using the existing one
expect(jest.mocked(ctx.ios.createPushKeyAsync).mock.calls.length).toBe(0);
expect(asMock(ctx.ios.createPushKeyAsync).mock.calls.length).toBe(0);
// expect configuration not to be updated with a new push key
expect(jest.mocked(ctx.ios.updateIosAppCredentialsAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.ios.updateIosAppCredentialsAsync).mock.calls.length).toBe(1);
});
it('errors in Non Interactive Mode', async () => {
const ctx = createCtxMock({
@@ -1,3 +1,4 @@
import { asMock } from '../../../../__tests__/utils';
import { findApplicationTarget } from '../../../../project/ios/target';
import { confirmAsync, promptAsync } from '../../../../prompts';
import { createCtxMock } from '../../../__tests__/fixtures-context';
@@ -14,16 +15,15 @@ import {
} from '../SetUpSubmissionCredentials';
jest.mock('../../../../prompts');
jest.mocked(confirmAsync).mockImplementation(async () => true);
asMock(confirmAsync).mockImplementation(() => true);
describe(SetUpSubmissionCredentials, () => {
it('allows user to enter an App Specific Password', async () => {
jest
.mocked(promptAsync)
.mockImplementationOnce(async () => ({
asMock(promptAsync)
.mockImplementationOnce(() => ({
choice: PROMPT_FOR_APP_SPECIFIC_PASSWORD,
}))
.mockImplementationOnce(async () => ({
.mockImplementationOnce(() => ({
appSpecificPassword: 'super secret',
}));
const ctx = createCtxMock({
@@ -37,7 +37,7 @@ describe(SetUpSubmissionCredentials, () => {
const asp = await setupAscApiKeyAction.runAsync(ctx);
// prompt to choose ASP, then prompt to input ASP
expect(jest.mocked(promptAsync).mock.calls.length).toBe(2);
expect(asMock(promptAsync).mock.calls.length).toBe(2);
expect(asp).toBe('super secret');
});
it('returns an ASC API key', async () => {
@@ -1,3 +1,4 @@
import { asMock } from '../../../../__tests__/utils';
import { IosDistributionType } from '../../../../graphql/generated';
import { findApplicationTarget } from '../../../../project/ios/target';
import { confirmAsync } from '../../../../prompts';
@@ -20,8 +21,8 @@ jest.mock('../../../../prompts');
jest.mock('../../../credentialsJson/read');
beforeEach(() => {
jest.mocked(confirmAsync).mockReset();
jest.mocked(confirmAsync).mockImplementation(async () => true);
asMock(confirmAsync).mockReset();
asMock(confirmAsync).mockImplementation(() => true);
});
describe('SetUpTargetBuildCredentialsFromCredentialsJson', () => {
@@ -60,13 +61,11 @@ describe('SetUpTargetBuildCredentialsFromCredentialsJson', () => {
await setupBuildCredentialsFromCredentialsJsonAction.runAsync(ctx);
// expect build credentials not to be created or updated on expo servers
expect(jest.mocked(ctx.ios.createOrUpdateIosAppBuildCredentialsAsync).mock.calls.length).toBe(
0
);
expect(asMock(ctx.ios.createOrUpdateIosAppBuildCredentialsAsync).mock.calls.length).toBe(0);
// expect distribution certificate not to be uploaded on expo servers
expect(jest.mocked(ctx.ios.createDistributionCertificateAsync).mock.calls.length).toBe(0);
expect(asMock(ctx.ios.createDistributionCertificateAsync).mock.calls.length).toBe(0);
// expect provisioning profile not to be uploaded on expo servers
expect(jest.mocked(ctx.ios.createProvisioningProfileAsync).mock.calls.length).toBe(0);
expect(asMock(ctx.ios.createProvisioningProfileAsync).mock.calls.length).toBe(0);
});
it('sets up build credentials with no prior configuration in Interactive Mode', async () => {
const ctx = createCtxMock({
@@ -91,13 +90,11 @@ describe('SetUpTargetBuildCredentialsFromCredentialsJson', () => {
await setupBuildCredentialsFromCredentialsJsonAction.runAsync(ctx);
// expect build credentials to be created or updated on expo servers
expect(jest.mocked(ctx.ios.createOrUpdateIosAppBuildCredentialsAsync).mock.calls.length).toBe(
1
);
expect(asMock(ctx.ios.createOrUpdateIosAppBuildCredentialsAsync).mock.calls.length).toBe(1);
// expect distribution certificate to be uploaded on expo servers
expect(jest.mocked(ctx.ios.createDistributionCertificateAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.ios.createDistributionCertificateAsync).mock.calls.length).toBe(1);
// expect provisioning profile to be uploaded on expo servers
expect(jest.mocked(ctx.ios.createProvisioningProfileAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.ios.createProvisioningProfileAsync).mock.calls.length).toBe(1);
});
it('sets up build credentials with different prior configuration in Interactive Mode', async () => {
// create deep clone the quick and dirty way
@@ -126,13 +123,11 @@ describe('SetUpTargetBuildCredentialsFromCredentialsJson', () => {
await setupBuildCredentialsFromCredentialsJsonAction.runAsync(ctx);
// expect build credentials to be created or updated on expo servers
expect(jest.mocked(ctx.ios.createOrUpdateIosAppBuildCredentialsAsync).mock.calls.length).toBe(
1
);
expect(asMock(ctx.ios.createOrUpdateIosAppBuildCredentialsAsync).mock.calls.length).toBe(1);
// expect distribution certificate to be uploaded on expo servers
expect(jest.mocked(ctx.ios.createDistributionCertificateAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.ios.createDistributionCertificateAsync).mock.calls.length).toBe(1);
// expect provisioning profile to be uploaded on expo servers
expect(jest.mocked(ctx.ios.createProvisioningProfileAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.ios.createProvisioningProfileAsync).mock.calls.length).toBe(1);
});
it('works in Non Interactive Mode - sets up build credentials with different prior configuration', async () => {
// create deep clone the quick and dirty way
@@ -162,14 +157,12 @@ describe('SetUpTargetBuildCredentialsFromCredentialsJson', () => {
await setupBuildCredentialsFromCredentialsJsonAction.runAsync(ctx);
// expect no prompts to be called
expect(jest.mocked(confirmAsync).mock.calls.length).toBe(0);
expect(asMock(confirmAsync).mock.calls.length).toBe(0);
// expect build credentials to be created or updated on expo servers
expect(jest.mocked(ctx.ios.createOrUpdateIosAppBuildCredentialsAsync).mock.calls.length).toBe(
1
);
expect(asMock(ctx.ios.createOrUpdateIosAppBuildCredentialsAsync).mock.calls.length).toBe(1);
// expect distribution certificate to be uploaded on expo servers
expect(jest.mocked(ctx.ios.createDistributionCertificateAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.ios.createDistributionCertificateAsync).mock.calls.length).toBe(1);
// expect provisioning profile to be uploaded on expo servers
expect(jest.mocked(ctx.ios.createProvisioningProfileAsync).mock.calls.length).toBe(1);
expect(asMock(ctx.ios.createProvisioningProfileAsync).mock.calls.length).toBe(1);
});
});
@@ -1,5 +1,6 @@
import { ApiKey, ApiKeyType, UserRole } from '@expo/apple-utils';
import { asMock } from '../../../../__tests__/utils';
import {
createAscApiKeyAsync,
getAscApiKeyAsync,
@@ -56,7 +57,7 @@ beforeEach(() => {
test(`listAscApiKeysAsync`, async () => {
const mockRequestContext = {};
jest.spyOn(ApiKey, 'getAsync').mockImplementation(async () => []);
jest.mocked(getRequestContext).mockImplementation(() => mockRequestContext);
asMock(getRequestContext).mockImplementation(() => mockRequestContext);
const result = await listAscApiKeysAsync(mockAuthCtx);
expect(ApiKey.getAsync).toHaveBeenLastCalledWith(mockRequestContext);
expect(result).toEqual([]);
@@ -65,7 +66,7 @@ test(`listAscApiKeysAsync`, async () => {
test(`getAscApiKeyAsync`, async () => {
const mockRequestContext = {};
jest.spyOn(ApiKey, 'infoAsync').mockImplementation(async () => mockApiKey);
jest.mocked(getRequestContext).mockImplementation(() => mockRequestContext);
asMock(getRequestContext).mockImplementation(() => mockRequestContext);
const result = await getAscApiKeyAsync(mockAuthCtx, 'test-key-id');
expect(ApiKey.infoAsync).toHaveBeenLastCalledWith(mockRequestContext, { id: 'test-key-id' });
expect(result).toEqual(mockAscApiKeyInfo);
@@ -74,7 +75,7 @@ test(`getAscApiKeyAsync`, async () => {
test(`createAscApiKeyAsync`, async () => {
const mockRequestContext = {};
jest.spyOn(ApiKey, 'createAsync').mockImplementation(async () => mockApiKey);
jest.mocked(getRequestContext).mockImplementation(() => mockRequestContext);
asMock(getRequestContext).mockImplementation(() => mockRequestContext);
const result = await createAscApiKeyAsync(mockAuthCtx, { nickname: 'test-name' });
expect(ApiKey.createAsync).toHaveBeenLastCalledWith(mockRequestContext, {
nickname: 'test-name',
@@ -88,7 +89,7 @@ test(`createAscApiKeyAsync`, async () => {
test(`revokeAscApiKeyAsync`, async () => {
const mockRequestContext = {};
jest.spyOn(ApiKey, 'infoAsync').mockImplementation(async () => mockApiKey);
jest.mocked(getRequestContext).mockImplementation(() => mockRequestContext);
asMock(getRequestContext).mockImplementation(() => mockRequestContext);
const result = await revokeAscApiKeyAsync(mockAuthCtx, 'test-key-id');
expect(ApiKey.infoAsync).toHaveBeenLastCalledWith(mockRequestContext, {
id: 'test-key-id',
@@ -1,5 +1,6 @@
import { vol } from 'memfs';
import { asMock } from '../../../../__tests__/utils';
import { promptAsync } from '../../../../prompts';
import { AppleTeamType } from '../authenticateTypes';
import { resolveAppleTeamAsync, resolveAscApiKeyAsync } from '../resolveCredentials';
@@ -16,7 +17,7 @@ const testAscApiKey = {
beforeEach(() => {
vol.reset();
process.env = {};
jest.mocked(promptAsync).mockReset();
asMock(promptAsync).mockReset();
});
describe(resolveAscApiKeyAsync, () => {
@@ -43,7 +44,7 @@ describe(resolveAscApiKeyAsync, () => {
vol.fromJSON({
'/test-asc-key.p8': testAscApiKey.keyP8,
});
jest.mocked(promptAsync).mockImplementation(async () => ({
asMock(promptAsync).mockImplementation(() => ({
ascApiKeyPath: '/test-asc-key.p8',
ascIssuerId: testAscApiKey.issuerId,
ascApiKeyId: testAscApiKey.keyId,
@@ -77,7 +78,7 @@ describe(resolveAppleTeamAsync, () => {
});
});
it(`prompts the user if it can't find anything else`, async () => {
jest.mocked(promptAsync).mockImplementation(async () => ({
asMock(promptAsync).mockImplementation(() => ({
appleTeamId: testTeam.teamId,
appleTeamType: testTeam.teamType,
}));
@@ -1,14 +1,11 @@
import { IOSConfig, compileModsAsync } from '@expo/config-plugins';
import { Workflow } from '@expo/eas-build-job';
import { JSONObject } from '@expo/json-file';
import { getPrebuildConfigAsync } from '@expo/prebuild-config';
import { readPlistAsync } from '../../utils/plist';
import { readPlistAsync } from '../../../utils/plist';
interface Target {
buildConfiguration?: string;
targetName: string;
}
export async function getManagedApplicationTargetEntitlementsAsync(
export async function getManagedEntitlementsJsonAsync(
projectDir: string,
env: Record<string, string>
): Promise<JSONObject> {
@@ -31,11 +28,22 @@ export async function getManagedApplicationTargetEntitlementsAsync(
}
}
export async function getNativeTargetEntitlementsAsync(
export async function resolveEntitlementsJsonAsync(
projectDir: string,
target: Target
): Promise<JSONObject | null> {
const entitlementsPath = IOSConfig.Entitlements.getEntitlementsPath(projectDir, target);
workflow: Workflow,
env: Record<string, string>
): Promise<JSONObject> {
if (workflow === Workflow.GENERIC) {
return (await getEntitlementsJsonAsync(projectDir)) || {};
} else if (workflow === Workflow.MANAGED) {
return await getManagedEntitlementsJsonAsync(projectDir, env);
} else {
throw new Error(`Unknown workflow: ${workflow}`);
}
}
async function getEntitlementsJsonAsync(projectDir: string): Promise<JSONObject | null> {
const entitlementsPath = IOSConfig.Paths.getEntitlementsPath(projectDir);
if (entitlementsPath) {
const plist = await readPlistAsync(entitlementsPath);
return plist ? (plist as unknown as JSONObject) : null;
@@ -1,5 +1,3 @@
import { JSONObject } from '@expo/json-file';
import {
CommonIosAppCredentialsFragment,
IosAppBuildCredentialsFragment,
@@ -16,7 +14,6 @@ export interface Target {
buildConfiguration?: string;
bundleIdentifier: string;
parentBundleIdentifier?: string;
entitlements: JSONObject;
}
export interface TargetCredentials {
@@ -1,7 +1,8 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`print credentials prints the IosAppCredentials map 1`] = `
"iOS Credentials
Array [
"iOS Credentials
Project @quinlanj/test52
Bundle Identifier com.quinlanj.test52
Apple Team test-apple-identifier
@@ -50,5 +51,6 @@ Status active
Expiration Wed, 13 Oct 2021 18:28:44 UTC
Apple Team test-apple-identifier
Updated 5 months ago
"
",
]
`;
@@ -15,7 +15,7 @@ describe(getCertData, () => {
});
});
describe('forge.pkcs12.pkcs12FromAsn1', () => {
describe(forge.pkcs12.pkcs12FromAsn1, () => {
describe('when using p12 created from keychain', () => {
it('throws if empty string is passed', async () => {
const p12Der = forge.util.decode64(testDistributionCertificateEmptyPasswordBase64);
@@ -1,6 +1,7 @@
import mockdate from 'mockdate';
import nullthrows from 'nullthrows';
import { asMock } from '../../../../__tests__/utils';
import Log from '../../../../log';
import { IosAppCredentialsQuery } from '../../api/graphql/queries/IosAppCredentialsQuery';
import { App, Target } from '../../types';
@@ -28,13 +29,9 @@ describe('print credentials', () => {
const appCredentials = {
test52: nullthrows(testIosAppCredentialsData),
};
const targets: Target[] = [
{ targetName: 'test52', bundleIdentifier: 'com.quinlanj.test52', entitlements: {} },
];
const targets: Target[] = [{ targetName: 'test52', bundleIdentifier: 'com.quinlanj.test52' }];
displayIosCredentials(app, appCredentials, targets);
const loggedSoFar = jest
.mocked(Log.log)
.mock.calls.reduce((acc, mockValue) => acc + mockValue, '');
const loggedSoFar = asMock(Log.log).mock.calls.reduce((acc, mockValue) => acc + mockValue);
expect(loggedSoFar).toMatchSnapshot();
});
});
@@ -123,7 +123,7 @@ export function displayIosCredentials(
export function displayProjectCredentials(
app: App,
appBuildCredentials: IosAppBuildCredentialsMap,
targets: Omit<Target, 'entitlements'>[]
targets: Target[]
): void {
const projectFullName = `@${app.account.name}/${app.projectName}`;
const targetToBundleId = targets.reduce<Record<string, string>>((acc, target) => {
@@ -31,7 +31,7 @@ export function parse(dataBase64: string): PlistObject {
const buffer = Buffer.from(dataBase64, 'base64');
const profile = buffer.toString('utf8');
return plist.parse(profile) as PlistObject;
} catch {
} catch (error) {
throw new Error('Provisioning profile is malformed');
}
}
@@ -75,7 +75,7 @@ function validateProvisioningProfileWithoutApple(
Log.warn('Provisioning Profile has expired.');
return false;
}
} catch {
} catch (error) {
Log.warn('Provisioning Profile is malformed.');
return false;
}
@@ -43,6 +43,7 @@ import { SetUpProvisioningProfile } from '../ios/actions/SetUpProvisioningProfil
import { SetUpPushKey } from '../ios/actions/SetUpPushKey';
import { UpdateCredentialsJson } from '../ios/actions/UpdateCredentialsJson';
import { AppLookupParams } from '../ios/api/GraphqlClient';
import { getManagedEntitlementsJsonAsync } from '../ios/appstore/entitlements';
import { App, IosAppCredentialsMap, Target } from '../ios/types';
import { displayIosCredentials } from '../ios/utils/printCredentials';
import { ActionInfo, IosActionType, Scope } from './Actions';
@@ -191,12 +192,10 @@ export class ManageIos {
},
buildProfile
);
const targets = await resolveTargetsAsync({
exp: ctx.exp,
projectDir: ctx.projectDir,
xcodeBuildContext,
env: buildProfile.env,
});
const targets = await resolveTargetsAsync(
{ exp: ctx.exp, projectDir: ctx.projectDir },
xcodeBuildContext
);
return {
app,
targets,
@@ -236,6 +235,12 @@ export class ManageIos {
targets,
distribution: buildProfile.distribution,
enterpriseProvisioning: buildProfile.enterpriseProvisioning,
iosCapabilitiesOptions: {
entitlements: await getManagedEntitlementsJsonAsync(
ctx.projectDir,
buildProfile.env ?? {}
),
},
}).runAsync(ctx);
return;
}
@@ -1,3 +1,4 @@
import { asMock } from '../../../__tests__/utils';
import { promptAsync } from '../../../prompts';
import {
getNewAndroidApiMock,
@@ -14,12 +15,12 @@ const TEST_STRING = 'TEST_STRING';
jest.mock('../../../prompts');
beforeEach(() => {
jest.mocked(promptAsync).mockReset();
asMock(promptAsync).mockReset();
});
describe(SelectAndroidBuildCredentials, () => {
it('returns a request to make default build credentials when there are no credentials', async () => {
jest.mocked(promptAsync).mockImplementation(async () => ({
asMock(promptAsync).mockImplementation(() => ({
providedName: TEST_STRING,
}));
const ctx = createCtxMock({
@@ -37,7 +38,7 @@ describe(SelectAndroidBuildCredentials, () => {
});
});
it('returns a request to make build credentials when the user chooses to make a new one', async () => {
jest.mocked(promptAsync).mockImplementation(async () => ({
asMock(promptAsync).mockImplementation(() => ({
buildCredentialsResultOrRequestToCreateNew:
SelectAndroidBuildCredentialsResultType.CREATE_REQUEST,
providedName: TEST_STRING,
@@ -59,7 +60,7 @@ describe(SelectAndroidBuildCredentials, () => {
});
});
it('returns a request to make default build credentials when the user chooses to make a new one, and if they have no existing credentials', async () => {
jest.mocked(promptAsync).mockImplementation(async () => ({
asMock(promptAsync).mockImplementation(() => ({
buildCredentialsResultOrRequestToCreateNew:
SelectAndroidBuildCredentialsResultType.CREATE_REQUEST,
providedName: TEST_STRING,
@@ -79,7 +80,7 @@ describe(SelectAndroidBuildCredentials, () => {
});
});
it('returns buildCredentials of the users choice', async () => {
jest.mocked(promptAsync).mockImplementation(async () => ({
asMock(promptAsync).mockImplementation(() => ({
buildCredentialsResultOrRequestToCreateNew: testAndroidBuildCredentialsFragment,
}));
const ctx = createCtxMock({
@@ -1,5 +1,6 @@
import prompts from 'prompts';
import { asMock } from '../../__tests__/utils';
import Log from '../../log';
import { Actor } from '../../user/User';
import { AccountResolver } from '../manager';
@@ -14,8 +15,8 @@ jest.mock('../../project/projectUtils', () => {
jest.mock('../../credentials/ios/api/graphql/queries/AppleTeamQuery');
beforeEach(() => {
jest.mocked(prompts).mockReset();
jest.mocked(prompts).mockImplementation(() => {
asMock(prompts).mockReset();
asMock(prompts).mockImplementation(() => {
throw new Error(`unhandled prompts call - this shouldn't happen - fix tests!`);
});
});
@@ -37,7 +38,7 @@ describe(AccountResolver, () => {
const exp = {} as any;
it('returns the account defined in app.json/app.config.js if user confirms', async () => {
jest.mocked(prompts).mockImplementationOnce(async () => ({
asMock(prompts).mockImplementationOnce(() => ({
value: true,
}));
@@ -47,10 +48,10 @@ describe(AccountResolver, () => {
});
it('asks the user to choose the account from his account list if he rejects to use the one defined in app.json / app.config.js', async () => {
jest.mocked(prompts).mockImplementationOnce(async () => ({
asMock(prompts).mockImplementationOnce(() => ({
useProjectAccount: false,
}));
jest.mocked(prompts).mockImplementationOnce(async () => ({
asMock(prompts).mockImplementationOnce(() => ({
account: user.accounts[0],
}));
@@ -60,7 +61,7 @@ describe(AccountResolver, () => {
});
it(`asks the user to choose the account from his account list if he doesn't have access to the account defined in app.json / app.config.js`, async () => {
jest.mocked(prompts).mockImplementationOnce(async () => ({
asMock(prompts).mockImplementationOnce(() => ({
account: user.accounts[0],
}));
@@ -79,7 +80,7 @@ describe(AccountResolver, () => {
describe('when outside project dir', () => {
it('asks the user to choose the account from his account list', async () => {
jest.mocked(prompts).mockImplementationOnce(async () => ({
asMock(prompts).mockImplementationOnce(() => ({
account: user.accounts[0],
}));
@@ -1,5 +1,6 @@
import prompts from 'prompts';
import { asMock } from '../../../../__tests__/utils';
import { Account } from '../../../../user/Account';
import DeviceCreateAction, { RegistrationMethod } from '../action';
import { runInputMethodAsync } from '../inputMethod';
@@ -10,19 +11,19 @@ jest.mock('../registrationUrlMethod');
jest.mock('../inputMethod');
beforeEach(() => {
const promptsMock = jest.mocked(prompts);
const promptsMock = asMock(prompts);
promptsMock.mockReset();
promptsMock.mockImplementation(() => {
throw new Error(`unhandled prompts call - this shouldn't happen - fix tests!`);
});
jest.mocked(runRegistrationUrlMethodAsync).mockClear();
jest.mocked(runInputMethodAsync).mockClear();
asMock(runRegistrationUrlMethodAsync).mockClear();
asMock(runInputMethodAsync).mockClear();
});
describe(DeviceCreateAction, () => {
describe('#runAsync', () => {
it('calls runRegistrationUrlMethodAsync if user chooses the website method', async () => {
jest.mocked(prompts).mockImplementationOnce(async () => ({
asMock(prompts).mockImplementationOnce(() => ({
method: RegistrationMethod.WEBSITE,
}));
@@ -42,7 +43,7 @@ describe(DeviceCreateAction, () => {
});
it('calls runInputMethodAsync if user chooses the input method', async () => {
jest.mocked(prompts).mockImplementationOnce(async () => ({
asMock(prompts).mockImplementationOnce(() => ({
method: RegistrationMethod.INPUT,
}));
@@ -1,5 +1,6 @@
import prompts from 'prompts';
import { asMock } from '../../../../__tests__/utils';
import { AppleDeviceMutation } from '../../../../credentials/ios/api/graphql/mutations/AppleDeviceMutation';
import { AppleDeviceClass, AppleTeam } from '../../../../graphql/generated';
import { runInputMethodAsync } from '../inputMethod';
@@ -8,19 +9,19 @@ jest.mock('../../../../credentials/ios/api/graphql/mutations/AppleDeviceMutation
jest.mock('../../../../ora');
beforeEach(() => {
jest.mocked(prompts).mockReset();
jest.mocked(prompts).mockImplementation(() => {
asMock(prompts).mockReset();
asMock(prompts).mockImplementation(() => {
throw new Error(`unhandled prompts call - this shouldn't happen - fix tests!`);
});
jest.mocked(AppleDeviceMutation.createAppleDeviceAsync).mockClear();
asMock(AppleDeviceMutation.createAppleDeviceAsync).mockClear();
});
describe(runInputMethodAsync, () => {
it('should allow for multiple device registration', async () => {
mockDeviceData('00001111-001122334455662E', 'my iPhone', AppleDeviceClass.Iphone);
jest.mocked(prompts).mockImplementationOnce(async () => ({ value: true }));
asMock(prompts).mockImplementationOnce(() => ({ value: true }));
mockDeviceData('b12cba9856d89c932ab7a4b813c4d932534e1679', 'my iPad', AppleDeviceClass.Ipad);
jest.mocked(prompts).mockImplementationOnce(async () => ({ value: false }));
asMock(prompts).mockImplementationOnce(() => ({ value: false }));
const accountId = 'account-id';
// @ts-expect-error appleTeam is missing properties of AppleTeam GraphQL type
@@ -37,8 +38,8 @@ describe(runInputMethodAsync, () => {
});
function mockDeviceData(udid: string, name: string, deviceClass: AppleDeviceClass): void {
jest.mocked(prompts).mockImplementationOnce(async () => ({ udid }));
jest.mocked(prompts).mockImplementationOnce(async () => ({ name }));
jest.mocked(prompts).mockImplementationOnce(async () => ({ deviceClass }));
jest.mocked(prompts).mockImplementationOnce(async () => ({ value: true }));
asMock(prompts).mockImplementationOnce(() => ({ udid }));
asMock(prompts).mockImplementationOnce(() => ({ name }));
asMock(prompts).mockImplementationOnce(() => ({ deviceClass }));
asMock(prompts).mockImplementationOnce(() => ({ value: true }));
}
+22 -74
View File
@@ -94,7 +94,7 @@ export type Account = {
apps: Array<App>;
/** @deprecated Build packs are no longer supported */
availableBuilds?: Maybe<Scalars['Int']>;
/** Billing information. Only visible to members with the ADMIN or OWNER role. */
/** Billing information */
billing?: Maybe<Billing>;
billingPeriod: BillingPeriod;
/** Build Jobs associated with this account */
@@ -338,9 +338,7 @@ export type AccountMutationRenameArgs = {
export type AccountMutationRequestRefundArgs = {
accountID: Scalars['ID'];
chargeID: Scalars['ID'];
description?: InputMaybe<Scalars['String']>;
reason?: InputMaybe<Scalars['String']>;
chargeIdentifier: Scalars['ID'];
};
@@ -395,40 +393,21 @@ export type AccountQueryByNameArgs = {
export type AccountUsageMetric = {
__typename?: 'AccountUsageMetric';
id: Scalars['ID'];
metric: EasServiceMetric;
metricType: UsageMetricType;
serviceMetric: EasServiceMetric;
timestamp: Scalars['DateTime'];
value: Scalars['Float'];
};
export type AccountUsageMetricAndCost = {
__typename?: 'AccountUsageMetricAndCost';
id: Scalars['ID'];
/** The limit, in units, allowed by this plan */
limit: Scalars['Float'];
metricType: UsageMetricType;
serviceMetric: EasServiceMetric;
/** Total cost of this particular metric, in cents */
totalCost: Scalars['Float'];
value: Scalars['Float'];
};
export type AccountUsageMetrics = {
__typename?: 'AccountUsageMetrics';
byBillingPeriod: UsageMetricTotal;
metricsForServiceMetric: Array<AccountUsageMetric>;
};
export type AccountUsageMetricsByBillingPeriodArgs = {
date: Scalars['DateTime'];
};
export type AccountUsageMetricsMetricsForServiceMetricArgs = {
granularity: UsageMetricsGranularity;
serviceMetric: EasServiceMetric;
metric: EasServiceMetric;
timespan: UsageMetricsTimespan;
};
@@ -1023,20 +1002,15 @@ export type AppDataInput = {
export type AppIcon = {
__typename?: 'AppIcon';
/** @deprecated No longer supported */
/** Nullable color palette of the app icon. If null, color palette couldn't be retrieved from external service (imgix) */
colorPalette?: Maybe<Scalars['JSON']>;
originalUrl: Scalars['String'];
primaryColor?: Maybe<Scalars['String']>;
url: Scalars['String'];
};
export type AppInfoInput = {
displayName?: InputMaybe<Scalars['String']>;
};
export type AppInput = {
accountId: Scalars['ID'];
appInfo?: InputMaybe<AppInfoInput>;
privacy: AppPrivacy;
projectName: Scalars['String'];
};
@@ -1047,8 +1021,6 @@ export type AppMutation = {
createApp: App;
/** @deprecated No longer supported */
grantAccess?: Maybe<App>;
/** Set display info for app */
setAppInfo: App;
/** Require api token to send push notifs for experience */
setPushSecurityEnabled: App;
};
@@ -1065,12 +1037,6 @@ export type AppMutationGrantAccessArgs = {
};
export type AppMutationSetAppInfoArgs = {
appId: Scalars['ID'];
appInfo: AppInfoInput;
};
export type AppMutationSetPushSecurityEnabledArgs = {
appId: Scalars['ID'];
pushSecurityEnabled: Scalars['Boolean'];
@@ -1939,13 +1905,6 @@ export type CodeSigningInfoInput = {
sig: Scalars['String'];
};
export type Concurrencies = {
__typename?: 'Concurrencies';
android: Scalars['Int'];
ios: Scalars['Int'];
total: Scalars['Int'];
};
export type CreateAccessTokenInput = {
actorID: Scalars['ID'];
note?: InputMaybe<Scalars['String']>;
@@ -1961,7 +1920,6 @@ export type CreateAccessTokenResponse = {
export type CreateAndroidSubmissionInput = {
appId: Scalars['ID'];
archiveUrl?: InputMaybe<Scalars['String']>;
config: AndroidSubmissionConfigInput;
submittedBuildId?: InputMaybe<Scalars['ID']>;
};
@@ -1979,11 +1937,17 @@ export type CreateEnvironmentSecretInput = {
export type CreateIosSubmissionInput = {
appId: Scalars['ID'];
archiveUrl?: InputMaybe<Scalars['String']>;
config: IosSubmissionConfigInput;
submittedBuildId?: InputMaybe<Scalars['ID']>;
};
export type CreateSubmissionInput = {
appId: Scalars['ID'];
config: Scalars['JSONObject'];
platform: AppPlatform;
submittedBuildId?: InputMaybe<Scalars['ID']>;
};
export type CreateSubmissionResult = {
__typename?: 'CreateSubmissionResult';
/** Created submission */
@@ -2093,10 +2057,7 @@ export enum EasBuildDeprecationInfoType {
}
export enum EasServiceMetric {
AssetsRequests = 'ASSETS_REQUESTS',
BandwidthUsage = 'BANDWIDTH_USAGE',
ManifestRequests = 'MANIFEST_REQUESTS',
UniqueUsers = 'UNIQUE_USERS'
ManifestRequests = 'MANIFEST_REQUESTS'
}
export type EditUpdateBranchInput = {
@@ -3147,8 +3108,6 @@ export type Submission = ActivityTimelineProjectActivity & {
actor?: Maybe<Actor>;
androidConfig?: Maybe<AndroidSubmissionConfig>;
app: App;
archiveUrl?: Maybe<Scalars['String']>;
canRetry: Scalars['Boolean'];
cancelingActor?: Maybe<Actor>;
createdAt: Scalars['DateTime'];
error?: Maybe<SubmissionError>;
@@ -3156,7 +3115,6 @@ export type Submission = ActivityTimelineProjectActivity & {
initiatingActor?: Maybe<Actor>;
iosConfig?: Maybe<IosSubmissionConfig>;
logsUrl?: Maybe<Scalars['String']>;
parentSubmission?: Maybe<Submission>;
platform: AppPlatform;
status: SubmissionStatus;
submittedBuild?: Maybe<Build>;
@@ -3201,13 +3159,16 @@ export type SubmissionMutation = {
createAndroidSubmission: CreateSubmissionResult;
/** Create an iOS EAS Submit submission */
createIosSubmission: CreateSubmissionResult;
/** Retry an EAS Submit submission */
retrySubmission: CreateSubmissionResult;
/**
* Create an EAS Submit submission
* @deprecated Use createIosSubmission / createAndroidSubmission instead
*/
createSubmission: CreateSubmissionResult;
};
export type SubmissionMutationCancelSubmissionArgs = {
submissionId: Scalars['ID'];
submissionId?: InputMaybe<Scalars['ID']>;
};
@@ -3221,8 +3182,8 @@ export type SubmissionMutationCreateIosSubmissionArgs = {
};
export type SubmissionMutationRetrySubmissionArgs = {
parentSubmissionId: Scalars['ID'];
export type SubmissionMutationCreateSubmissionArgs = {
input: CreateSubmissionInput;
};
export type SubmissionQuery = {
@@ -3249,7 +3210,6 @@ export type SubscriptionDetails = {
__typename?: 'SubscriptionDetails';
addons: Array<AddonDetails>;
cancelledAt?: Maybe<Scalars['DateTime']>;
concurrencies?: Maybe<Concurrencies>;
endedAt?: Maybe<Scalars['DateTime']>;
id: Scalars['ID'];
isDowngrading?: Maybe<Scalars['Boolean']>;
@@ -3435,20 +3395,8 @@ export enum UploadSessionType {
EasSubmitAppArchive = 'EAS_SUBMIT_APP_ARCHIVE'
}
export type UsageMetricTotal = {
__typename?: 'UsageMetricTotal';
billingPeriod: BillingPeriod;
id: Scalars['ID'];
overageMetrics: Array<AccountUsageMetricAndCost>;
planMetrics: Array<AccountUsageMetricAndCost>;
/** Total cost of overages, in cents */
totalCost: Scalars['Float'];
};
export enum UsageMetricType {
Bandwidth = 'BANDWIDTH',
Request = 'REQUEST',
User = 'USER'
Request = 'REQUEST'
}
export enum UsageMetricsGranularity {
+12 -1
View File
@@ -1 +1,12 @@
export { run } from '@oclif/core';
import { run } from '@oclif/core';
/**
* Invokes the `testcli-modern` CLI with args programmatically.
*
* @param {...string} args - args to pass to CLI.
*
* @returns {Promise<void>}
*/
export default async function eascliAsync(...args: any[]): Promise<unknown> {
return run(args, import.meta.url);
}
@@ -1,5 +1,6 @@
import { vol } from 'memfs';
import { asMock } from '../../__tests__/utils';
import { confirmAsync } from '../../prompts';
import { Actor, getUserAsync } from '../../user/User';
import {
@@ -122,58 +123,52 @@ describe(getProjectAccountNameAsync, () => {
const expWithoutOwner: any = {};
beforeEach(() => {
jest.mocked(getUserAsync).mockReset();
asMock(getUserAsync).mockReset();
});
it(`returns the owner field's value from app.json / app.config.js`, async () => {
jest.mocked(getUserAsync).mockImplementation(
async (): Promise<Actor> => ({
__typename: 'User',
id: 'user_id',
username: 'notnotbrent',
accounts: [
{ id: 'account_id_1', name: 'notnotbrent' },
{ id: 'account_id_2', name: 'dominik' },
],
isExpoAdmin: false,
})
);
asMock(getUserAsync).mockImplementation((): Actor | undefined => ({
__typename: 'User',
id: 'user_id',
username: 'notnotbrent',
accounts: [
{ id: 'account_id_1', name: 'notnotbrent' },
{ id: 'account_id_2', name: 'dominik' },
],
isExpoAdmin: false,
}));
const projectAccountName = await getProjectAccountNameAsync(expWithOwner);
expect(projectAccountName).toBe('dominik');
});
it(`returns the username if owner field is not set in app.json / app.config.js`, async () => {
jest.mocked(getUserAsync).mockImplementation(
async (): Promise<Actor> => ({
__typename: 'User',
id: 'user_id',
username: 'notnotbrent',
accounts: [
{ id: 'account_id_1', name: 'notnotbrent' },
{ id: 'account_id_2', name: 'dominik' },
],
isExpoAdmin: false,
})
);
asMock(getUserAsync).mockImplementation((): Actor | undefined => ({
__typename: 'User',
id: 'user_id',
username: 'notnotbrent',
accounts: [
{ id: 'account_id_1', name: 'notnotbrent' },
{ id: 'account_id_2', name: 'dominik' },
],
isExpoAdmin: false,
}));
const projectAccountName = await getProjectAccountNameAsync(expWithoutOwner);
expect(projectAccountName).toBe('notnotbrent');
});
it(`throws when project owner is undefined for robot actors`, async () => {
jest.mocked(getUserAsync).mockImplementation(
async (): Promise<Actor> => ({
__typename: 'Robot',
id: 'user_id',
firstName: 'GLaDOS',
accounts: [
{ id: 'account_id_1', name: 'notnotbrent' },
{ id: 'account_id_2', name: 'dominik' },
],
isExpoAdmin: false,
})
);
asMock(getUserAsync).mockImplementation((): Actor | undefined => ({
__typename: 'Robot',
id: 'user_id',
firstName: 'GLaDOS',
accounts: [
{ id: 'account_id_1', name: 'notnotbrent' },
{ id: 'account_id_2', name: 'dominik' },
],
isExpoAdmin: false,
}));
await expect(getProjectAccountNameAsync(expWithoutOwner)).rejects.toThrow(
'manifest property is required'
);
@@ -182,67 +177,57 @@ describe(getProjectAccountNameAsync, () => {
describe(promptToCreateProjectIfNotExistsAsync, () => {
beforeEach(() => {
jest.mocked(getUserAsync).mockReset();
jest.mocked(findProjectIdByAccountNameAndSlugNullableAsync).mockReset();
asMock(getUserAsync).mockReset();
asMock(findProjectIdByAccountNameAndSlugNullableAsync).mockReset();
});
it('returns the existing project', async () => {
jest.mocked(getUserAsync).mockImplementation(
async (): Promise<Actor> => ({
__typename: 'User',
id: 'user_id',
username: 'quin',
accounts: [{ id: 'account_id_1', name: 'quin' }],
isExpoAdmin: false,
})
asMock(getUserAsync).mockImplementation((): Actor | undefined => ({
__typename: 'User',
id: 'user_id',
username: 'quin',
accounts: [{ id: 'account_id_1', name: 'quin' }],
isExpoAdmin: false,
}));
asMock(findProjectIdByAccountNameAndSlugNullableAsync).mockImplementation(
() => 'already-existing-project-id'
);
jest
.mocked(findProjectIdByAccountNameAndSlugNullableAsync)
.mockImplementation(async () => 'already-existing-project-id');
const exp: any = {};
const projectId = await promptToCreateProjectIfNotExistsAsync(exp);
expect(projectId).toBe('already-existing-project-id');
});
it('makes a new project if none exists', async () => {
jest.mock('../../prompts');
jest.mocked(getUserAsync).mockImplementation(
async (): Promise<Actor> => ({
__typename: 'User',
id: 'user_id',
username: 'quin',
accounts: [{ id: 'account_id_1', name: 'quin' }],
isExpoAdmin: false,
})
);
jest
.mocked(findProjectIdByAccountNameAndSlugNullableAsync)
.mockImplementation(async () => null);
jest.mocked(ensureProjectExistsAsync).mockImplementation(async () => 'new-project-id');
asMock(getUserAsync).mockImplementation((): Actor | undefined => ({
__typename: 'User',
id: 'user_id',
username: 'quin',
accounts: [{ id: 'account_id_1', name: 'quin' }],
isExpoAdmin: false,
}));
asMock(findProjectIdByAccountNameAndSlugNullableAsync).mockImplementation(() => null);
asMock(ensureProjectExistsAsync).mockImplementation(() => 'new-project-id');
// user agrees to make new project
jest.mocked(confirmAsync).mockImplementation(async () => true);
asMock(confirmAsync).mockImplementation(() => true);
const exp: any = {};
const projectId = await promptToCreateProjectIfNotExistsAsync(exp);
expect(projectId).toBe('new-project-id');
});
it('aborts making a new project if the user declines', async () => {
jest.mock('../../prompts');
jest.mocked(getUserAsync).mockImplementation(
async (): Promise<Actor> => ({
__typename: 'User',
id: 'user_id',
username: 'quin',
accounts: [{ id: 'account_id_1', name: 'quin' }],
isExpoAdmin: false,
})
);
jest
.mocked(findProjectIdByAccountNameAndSlugNullableAsync)
.mockImplementation(async () => null);
jest.mocked(ensureProjectExistsAsync).mockImplementation(async () => 'new-project-id');
asMock(getUserAsync).mockImplementation((): Actor | undefined => ({
__typename: 'User',
id: 'user_id',
username: 'quin',
accounts: [{ id: 'account_id_1', name: 'quin' }],
isExpoAdmin: false,
}));
asMock(findProjectIdByAccountNameAndSlugNullableAsync).mockImplementation(() => null);
asMock(ensureProjectExistsAsync).mockImplementation(() => 'new-project-id');
// user doesnt agree to make new project
jest.mocked(confirmAsync).mockImplementation(async () => false);
asMock(confirmAsync).mockImplementation(() => false);
const exp: any = {};
const projectId = await promptToCreateProjectIfNotExistsAsync(exp);
expect(projectId).toBe(null);
@@ -412,9 +412,9 @@ describe(uploadAssetsAsync, () => {
return { specifications: ['{}', '{}', '{}'] };
});
// beforeEach(() => {
// jest.useFakeTimers();
// });
beforeEach(() => {
jest.useFakeTimers();
});
it('throws an error if the upload exceeds TIMEOUT_LIMIT', async () => {
jest.spyOn(PublishQuery, 'getAssetMetadataAsync').mockImplementation(async () => {
const status = AssetMetadataStatus.DoesNotExist;
@@ -2,6 +2,7 @@ import fs from 'fs-extra';
import { vol } from 'memfs';
import os from 'os';
import { asMock } from '../../../__tests__/utils';
import { jester as mockJester } from '../../../credentials/__tests__/fixtures-constants';
import { promptAsync } from '../../../prompts';
import {
@@ -20,7 +21,7 @@ beforeEach(async () => {
// this fixes a weird error with tempy in @expo/image-utils
await fs.mkdirp(os.tmpdir());
jest.mocked(promptAsync).mockReset();
asMock(promptAsync).mockReset();
});
describe(getApplicationIdAsync, () => {
@@ -121,7 +122,7 @@ describe(ensureApplicationIdIsDefinedForManagedProjectAsync, () => {
'/app'
);
jest.mocked(promptAsync).mockImplementationOnce(async () => ({
asMock(promptAsync).mockImplementationOnce(() => ({
packageName: 'com.expo.notdominik',
}));
@@ -138,7 +139,7 @@ describe(ensureApplicationIdIsDefinedForManagedProjectAsync, () => {
'/app'
);
jest.mocked(promptAsync).mockImplementationOnce(async () => ({
asMock(promptAsync).mockImplementationOnce(() => ({
packageName: 'com.expo.notdominik',
}));
@@ -2,6 +2,7 @@ import fs from 'fs-extra';
import { vol } from 'memfs';
import os from 'os';
import { asMock } from '../../../__tests__/utils';
import { jester as mockJester } from '../../../credentials/__tests__/fixtures-constants';
import { promptAsync } from '../../../prompts';
import { resolveGradleBuildContextAsync } from '../gradle';
@@ -16,7 +17,7 @@ beforeEach(async () => {
// this fixes a weird error with tempy in @expo/image-utils
await fs.mkdirp(os.tmpdir());
jest.mocked(promptAsync).mockReset();
asMock(promptAsync).mockReset();
});
describe(resolveGradleBuildContextAsync, () => {
@@ -28,7 +28,7 @@ export async function ensureApplicationIdIsDefinedForManagedProjectAsync(
return await getApplicationIdAsync(projectDir, exp, {
moduleName: gradleUtils.DEFAULT_MODULE_NAME,
});
} catch {
} catch (err) {
return await configureApplicationIdAsync(projectDir, exp);
}
}
@@ -3,6 +3,7 @@ import { vol } from 'memfs';
import os from 'os';
import path from 'path';
import { asMock } from '../../../__tests__/utils';
import { jester as mockJester } from '../../../credentials/__tests__/fixtures-constants';
import { promptAsync } from '../../../prompts';
import {
@@ -22,7 +23,7 @@ beforeEach(async () => {
// this fixes a weird error with tempy in @expo/image-utils
await fs.mkdirp(os.tmpdir());
jest.mocked(promptAsync).mockReset();
asMock(promptAsync).mockReset();
});
const originalFs = jest.requireActual('fs') as typeof fs;
@@ -104,7 +105,7 @@ describe(ensureBundleIdentifierIsDefinedForManagedProjectAsync, () => {
'/app'
);
jest.mocked(promptAsync).mockImplementationOnce(async () => ({
asMock(promptAsync).mockImplementationOnce(() => ({
bundleIdentifier: 'com.expo.notdominik',
}));
@@ -121,7 +122,7 @@ describe(ensureBundleIdentifierIsDefinedForManagedProjectAsync, () => {
'/app'
);
jest.mocked(promptAsync).mockImplementationOnce(async () => ({
asMock(promptAsync).mockImplementationOnce(() => ({
bundleIdentifier: 'com.expo.notdominik',
}));
@@ -2,6 +2,7 @@ import fs from 'fs-extra';
import { vol } from 'memfs';
import os from 'os';
import { asMock } from '../../../__tests__/utils';
import { promptAsync } from '../../../prompts';
import { selectSchemeAsync } from '../scheme';
@@ -15,7 +16,7 @@ beforeEach(async () => {
// this fixes a weird error with tempy in @expo/image-utils
await fs.mkdirp(os.tmpdir());
jest.mocked(promptAsync).mockReset();
asMock(promptAsync).mockReset();
});
describe(selectSchemeAsync, () => {
@@ -57,7 +58,7 @@ describe(selectSchemeAsync, () => {
},
projectDir
);
jest.mocked(promptAsync).mockImplementationOnce(async () => ({
asMock(promptAsync).mockImplementationOnce(() => ({
selectedScheme: 'scheme3',
}));
@@ -23,7 +23,7 @@ export async function ensureBundleIdentifierIsDefinedForManagedProjectAsync(
try {
return await getBundleIdentifierAsync(projectDir, exp);
} catch {
} catch (err) {
return await configureBundleIdentifierAsync(projectDir, exp);
}
}
+66 -92
View File
@@ -1,30 +1,17 @@
import { ExpoConfig } from '@expo/config';
import { IOSConfig } from '@expo/config-plugins';
import { Platform, Workflow } from '@expo/eas-build-job';
import { JSONObject } from '@expo/json-file';
import Joi from 'joi';
import { Target } from '../../credentials/ios/types';
import { resolveWorkflowAsync } from '../workflow';
import { getBundleIdentifierAsync } from './bundleIdentifier';
import {
getManagedApplicationTargetEntitlementsAsync,
getNativeTargetEntitlementsAsync,
} from './entitlements';
import { XcodeBuildContext } from './scheme';
interface UserDefinedTarget {
targetName: string;
bundleIdentifier: string;
parentBundleIdentifier?: string;
entitlements?: JSONObject;
}
interface ResolveTargetOptions {
projectDir: string;
exp: ExpoConfig;
env?: Record<string, string>;
xcodeBuildContext: XcodeBuildContext;
}
const AppExtensionsConfigSchema = Joi.array().items(
@@ -32,85 +19,27 @@ const AppExtensionsConfigSchema = Joi.array().items(
targetName: Joi.string().required(),
bundleIdentifier: Joi.string().required(),
parentBundleIdentifier: Joi.string(),
entitlements: Joi.object(),
})
);
export async function resolveMangedProjectTargetsAsync({
exp,
projectDir,
xcodeBuildContext,
env,
}: ResolveTargetOptions): Promise<Target[]> {
const { buildScheme, buildConfiguration } = xcodeBuildContext;
const applicationTargetName = buildScheme;
const applicationTargetBundleIdentifier = await getBundleIdentifierAsync(projectDir, exp, {
targetName: applicationTargetName,
buildConfiguration,
});
const applicationTargetEntitlements = await getManagedApplicationTargetEntitlementsAsync(
projectDir,
env ?? {}
);
const appExtensions: UserDefinedTarget[] =
exp.extra?.eas?.build?.experimental?.ios?.appExtensions ?? [];
const { error } = AppExtensionsConfigSchema.validate(appExtensions, {
allowUnknown: false,
abortEarly: false,
});
if (error) {
throw new Error(
`Failed to validate "extra.eas.build.experimental.ios.appExtensions" in you app config.\n${error.message}`
);
}
const extensionsTargets: Target[] = appExtensions.map(extension => ({
targetName: extension.targetName,
buildConfiguration,
bundleIdentifier: extension.bundleIdentifier,
parentBundleIdentifier: extension.parentBundleIdentifier ?? applicationTargetBundleIdentifier,
entitlements: extension.entitlements ?? {},
}));
return [
{
targetName: applicationTargetName,
bundleIdentifier: applicationTargetBundleIdentifier,
buildConfiguration,
entitlements: applicationTargetEntitlements,
},
...extensionsTargets,
];
}
export async function resolveBareProjectTargetsAsync({
exp,
projectDir,
xcodeBuildContext,
}: ResolveTargetOptions): Promise<Target[]> {
const { buildScheme, buildConfiguration } = xcodeBuildContext;
export async function resolveTargetsAsync(
{ exp, projectDir }: { exp: ExpoConfig; projectDir: string },
{ buildConfiguration, buildScheme }: XcodeBuildContext
): Promise<Target[]> {
const result: Target[] = [];
const applicationTarget = await IOSConfig.Target.findApplicationTargetWithDependenciesAsync(
projectDir,
buildScheme
);
const applicationTarget = await readApplicationTargetForSchemeAsync(projectDir, buildScheme);
const bundleIdentifier = await getBundleIdentifierAsync(projectDir, exp, {
targetName: applicationTarget.name,
buildConfiguration,
});
const entitlements = await getNativeTargetEntitlementsAsync(projectDir, {
targetName: applicationTarget.name,
buildConfiguration,
});
result.push({
targetName: applicationTarget.name,
bundleIdentifier,
buildConfiguration,
entitlements: entitlements ?? {},
});
const dependencies = await resolveBareProjectDependenciesAsync({
const dependencies = await resolveDependenciesAsync({
exp,
projectDir,
buildConfiguration,
@@ -121,21 +50,55 @@ export async function resolveBareProjectTargetsAsync({
result.push(...dependencies);
}
result.push(
...(await resolveManagedAppExtensionsAsync({
exp,
projectDir,
buildConfiguration,
applicationTargetBundleIdentifier: bundleIdentifier,
}))
);
return result;
}
export async function resolveTargetsAsync(opts: ResolveTargetOptions): Promise<Target[]> {
const workflow = await resolveWorkflowAsync(opts.projectDir, Platform.IOS);
if (workflow === Workflow.GENERIC) {
return await resolveBareProjectTargetsAsync(opts);
} else if (workflow === Workflow.MANAGED) {
return await resolveMangedProjectTargetsAsync(opts);
} else {
throw new Error(`Unknown workflow: ${workflow}`);
async function resolveManagedAppExtensionsAsync({
exp,
projectDir,
buildConfiguration,
applicationTargetBundleIdentifier,
}: {
exp: ExpoConfig;
projectDir: string;
buildConfiguration?: string;
applicationTargetBundleIdentifier: string;
}): Promise<Target[]> {
const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS);
const managedAppExtensions: UserDefinedTarget[] =
exp.extra?.eas?.build?.experimental?.ios?.appExtensions;
if (workflow === Workflow.GENERIC || !managedAppExtensions) {
return [];
}
const { error } = AppExtensionsConfigSchema.validate(managedAppExtensions, {
allowUnknown: false,
abortEarly: false,
});
if (error) {
throw new Error(
`Failed to validate "extra.eas.build.experimental.ios.appExtensions" in you app config\n${error.message}`
);
}
return managedAppExtensions.map(extension => ({
targetName: extension.targetName,
buildConfiguration,
bundleIdentifier: extension.bundleIdentifier,
parentBundleIdentifier: extension.parentBundleIdentifier ?? applicationTargetBundleIdentifier,
}));
}
async function resolveBareProjectDependenciesAsync({
async function resolveDependenciesAsync({
exp,
projectDir,
buildConfiguration,
@@ -156,18 +119,13 @@ async function resolveBareProjectDependenciesAsync({
targetName: dependency.name,
buildConfiguration,
});
const entitlements = await getNativeTargetEntitlementsAsync(projectDir, {
targetName: target.name,
buildConfiguration,
});
result.push({
targetName: dependency.name,
buildConfiguration,
bundleIdentifier: dependencyBundleIdentifier,
parentBundleIdentifier: bundleIdentifier,
entitlements: entitlements ?? {},
});
const dependencyDependencies = await resolveBareProjectDependenciesAsync({
const dependencyDependencies = await resolveDependenciesAsync({
exp,
projectDir,
buildConfiguration,
@@ -183,6 +141,22 @@ async function resolveBareProjectDependenciesAsync({
return result;
}
async function readApplicationTargetForSchemeAsync(
projectDir: string,
scheme: string
): Promise<IOSConfig.Target.Target> {
const workflow = await resolveWorkflowAsync(projectDir, Platform.IOS);
if (workflow === Workflow.GENERIC) {
return await IOSConfig.Target.findApplicationTargetWithDependenciesAsync(projectDir, scheme);
} else {
return {
name: scheme,
type: IOSConfig.Target.TargetType.APPLICATION,
dependencies: [],
};
}
}
export function findApplicationTarget(targets: Target[]): Target {
const applicationTarget = targets.find(({ parentBundleIdentifier }) => !parentBundleIdentifier);
if (!applicationTarget) {
+4 -4
View File
@@ -54,7 +54,7 @@ export async function validateMetroConfigForManagedWorkflowAsync(
}
}
function importMetroConfigFromProject(projectDir: string): typeof MetroConfig {
async function importMetroConfigFromProjectAsync(projectDir: string): Promise<typeof MetroConfig> {
const resolvedPath = resolveFrom.silent(projectDir, 'metro-config');
if (!resolvedPath) {
throw new MetroConfigPackageMissingError(
@@ -64,12 +64,12 @@ function importMetroConfigFromProject(projectDir: string): typeof MetroConfig {
'and run `yarn` or `npm install`.'
);
}
return require(resolvedPath);
return await import(resolvedPath);
}
async function configExistsAsync(projectRoot: string): Promise<boolean> {
try {
const MetroConfig = importMetroConfigFromProject(projectRoot);
const MetroConfig = await importMetroConfigFromProjectAsync(projectRoot);
const result = await MetroConfig.resolveConfig(undefined, projectRoot);
return !result.isEmpty;
} catch (err) {
@@ -82,7 +82,7 @@ async function configExistsAsync(projectRoot: string): Promise<boolean> {
}
async function loadConfigAsync(projectDir: string): Promise<MetroConfig.ConfigT> {
const MetroConfig = importMetroConfigFromProject(projectDir);
const MetroConfig = await importMetroConfigFromProjectAsync(projectDir);
return await MetroConfig.loadConfig({ cwd: projectDir }, {});
}
+1 -1
View File
@@ -482,7 +482,7 @@ function validateUrl(url: string): boolean {
? protocols.map(x => `${x.toLowerCase()}:`).includes(parsed.protocol)
: false
: true;
} catch {
} catch (err) {
return false;
}
}
@@ -3,6 +3,7 @@ import fs from 'fs-extra';
import { vol } from 'memfs';
import { v4 as uuidv4 } from 'uuid';
import { asMock } from '../../__tests__/utils';
import { AppPlatform, BuildFragment, UploadSessionType } from '../../graphql/generated';
import { BuildQuery } from '../../graphql/queries/BuildQuery';
import { toAppPlatform } from '../../graphql/types/AppPlatform';
@@ -50,13 +51,13 @@ describe(getArchiveAsync, () => {
beforeEach(() => {
vol.reset();
jest.mocked(promptAsync).mockReset();
jest.mocked(promptAsync).mockImplementation(async () => {
asMock(promptAsync).mockReset();
asMock(promptAsync).mockImplementation(() => {
throw new Error(`unhandled prompts call - this shouldn't happen - fix tests!`);
});
jest.mocked(confirmAsync).mockReset();
jest.mocked(confirmAsync).mockImplementation(async () => {
asMock(confirmAsync).mockReset();
asMock(confirmAsync).mockImplementation(() => {
throw new Error(`unhandled prompts call - this shouldn't happen - fix tests!`);
});
});
@@ -72,8 +73,7 @@ describe(getArchiveAsync, () => {
});
it('prompts again if provided URL is invalid', async () => {
jest
.mocked(promptAsync)
asMock(promptAsync)
.mockResolvedValueOnce({ sourceType: ArchiveSourceType.url })
.mockResolvedValueOnce({ url: ARCHIVE_URL });
@@ -88,8 +88,8 @@ describe(getArchiveAsync, () => {
});
it('asks the user if use build id instead of build details page url', async () => {
jest.mocked(confirmAsync).mockResolvedValueOnce(true);
jest.mocked(BuildQuery.byIdAsync).mockResolvedValueOnce(MOCK_BUILD_FRAGMENT as BuildFragment);
asMock(confirmAsync).mockResolvedValueOnce(true);
asMock(BuildQuery.byIdAsync).mockResolvedValueOnce(MOCK_BUILD_FRAGMENT);
const archive = await getArchiveAsync({
...SOURCE_STUB_INPUT,
@@ -103,8 +103,7 @@ describe(getArchiveAsync, () => {
});
it('handles prompt source', async () => {
jest
.mocked(promptAsync)
asMock(promptAsync)
.mockResolvedValueOnce({ sourceType: ArchiveSourceType.url })
.mockResolvedValueOnce({ url: ARCHIVE_URL });
@@ -118,7 +117,7 @@ describe(getArchiveAsync, () => {
it('handles Build ID source', async () => {
const buildId = uuidv4();
jest.mocked(BuildQuery.byIdAsync).mockResolvedValueOnce(MOCK_BUILD_FRAGMENT as BuildFragment);
asMock(BuildQuery.byIdAsync).mockResolvedValueOnce(MOCK_BUILD_FRAGMENT);
const archive = await getArchiveAsync({
...SOURCE_STUB_INPUT,
@@ -131,9 +130,8 @@ describe(getArchiveAsync, () => {
});
it('prompts again if build with provided ID doesnt exist', async () => {
jest.mocked(BuildQuery.byIdAsync).mockRejectedValue(new Error('Build doesnt exist'));
jest
.mocked(promptAsync)
asMock(BuildQuery.byIdAsync).mockRejectedValue(new Error('Build doesnt exist'));
asMock(promptAsync)
.mockResolvedValueOnce({ sourceType: ArchiveSourceType.url })
.mockResolvedValueOnce({ url: ARCHIVE_URL });
@@ -148,9 +146,7 @@ describe(getArchiveAsync, () => {
it('handles latest build source', async () => {
const projectId = uuidv4();
jest
.mocked(getRecentBuildsForSubmissionAsync)
.mockResolvedValueOnce([MOCK_BUILD_FRAGMENT as BuildFragment]);
asMock(getRecentBuildsForSubmissionAsync).mockResolvedValueOnce([MOCK_BUILD_FRAGMENT]);
const archive = await getArchiveAsync({
...SOURCE_STUB_INPUT,
@@ -166,9 +162,8 @@ describe(getArchiveAsync, () => {
});
it('prompts again if no builds exists when selected latest', async () => {
jest.mocked(getRecentBuildsForSubmissionAsync).mockResolvedValueOnce([]);
jest
.mocked(promptAsync)
asMock(getRecentBuildsForSubmissionAsync).mockResolvedValueOnce([]);
asMock(promptAsync)
.mockResolvedValueOnce({ sourceType: ArchiveSourceType.url })
.mockResolvedValueOnce({ url: ARCHIVE_URL });
@@ -182,10 +177,8 @@ describe(getArchiveAsync, () => {
it('handles build-list-select source', async () => {
const projectId = uuidv4();
jest
.mocked(getRecentBuildsForSubmissionAsync)
.mockResolvedValueOnce([MOCK_BUILD_FRAGMENT as BuildFragment]);
jest.mocked(promptAsync).mockResolvedValueOnce({ selectedBuild: MOCK_BUILD_FRAGMENT });
asMock(getRecentBuildsForSubmissionAsync).mockResolvedValueOnce([MOCK_BUILD_FRAGMENT]);
asMock(promptAsync).mockResolvedValueOnce({ selectedBuild: MOCK_BUILD_FRAGMENT });
const archive = await getArchiveAsync({
...SOURCE_STUB_INPUT,
@@ -202,14 +195,13 @@ describe(getArchiveAsync, () => {
});
it('prompts again if all builds have expired', async () => {
jest.mocked(getRecentBuildsForSubmissionAsync).mockResolvedValueOnce([
asMock(getRecentBuildsForSubmissionAsync).mockResolvedValueOnce([
{
...MOCK_BUILD_FRAGMENT,
updatedAt: new Date(Date.now() - 31 * 24 * 3600 * 1000),
} as BuildFragment,
},
]);
jest
.mocked(promptAsync)
asMock(promptAsync)
.mockResolvedValueOnce({ sourceType: ArchiveSourceType.url })
.mockResolvedValueOnce({ url: ARCHIVE_URL });
@@ -222,11 +214,8 @@ describe(getArchiveAsync, () => {
});
it('falls back to prompt if user selected "None of the above"', async () => {
jest
.mocked(getRecentBuildsForSubmissionAsync)
.mockResolvedValueOnce([MOCK_BUILD_FRAGMENT as BuildFragment]);
jest
.mocked(promptAsync)
asMock(getRecentBuildsForSubmissionAsync).mockResolvedValueOnce([MOCK_BUILD_FRAGMENT]);
asMock(promptAsync)
.mockResolvedValueOnce({ selectedBuild: null })
.mockResolvedValueOnce({ sourceType: ArchiveSourceType.url })
.mockResolvedValueOnce({ url: ARCHIVE_URL });
@@ -242,7 +231,7 @@ describe(getArchiveAsync, () => {
it('handles path source', async () => {
const path = '/archive.apk';
await fs.writeFile(path, 'some content');
jest.mocked(uploadAsync).mockResolvedValueOnce({ url: ARCHIVE_URL, bucketKey: 'wat' });
asMock(uploadAsync).mockResolvedValueOnce({ url: ARCHIVE_URL });
const archive = await getArchiveAsync({
...SOURCE_STUB_INPUT,
@@ -259,8 +248,7 @@ describe(getArchiveAsync, () => {
});
it('prompts again if provided path doesnt exist', async () => {
jest
.mocked(promptAsync)
asMock(promptAsync)
.mockResolvedValueOnce({ sourceType: ArchiveSourceType.url })
.mockResolvedValueOnce({ url: ARCHIVE_URL });
@@ -3,6 +3,7 @@ import { AndroidReleaseStatus, AndroidReleaseTrack } from '@expo/eas-json';
import { vol } from 'memfs';
import { v4 as uuidv4 } from 'uuid';
import { asMock } from '../../../__tests__/utils';
import { jester as mockJester } from '../../../credentials/__tests__/fixtures-constants';
import {
AppPlatform,
@@ -73,7 +74,7 @@ describe(AndroidSubmitCommand, () => {
});
afterEach(() => {
jest.mocked(getProjectIdAsync).mockClear();
asMock(getProjectIdAsync).mockClear();
});
describe('non-interactive mode', () => {
@@ -135,9 +136,7 @@ describe(AndroidSubmitCommand, () => {
it('assigns the build ID to submission', async () => {
const projectId = uuidv4();
jest
.mocked(getRecentBuildsForSubmissionAsync)
.mockResolvedValueOnce([fakeBuildFragment as BuildFragment]);
asMock(getRecentBuildsForSubmissionAsync).mockResolvedValueOnce([fakeBuildFragment]);
const ctx = await createSubmissionContextAsync({
platform: Platform.ANDROID,
@@ -3,6 +3,7 @@ import { AndroidReleaseStatus, AndroidReleaseTrack } from '@expo/eas-json';
import { vol } from 'memfs';
import { v4 as uuidv4 } from 'uuid';
import { asMock } from '../../../__tests__/utils';
import { testAndroidAppCredentialsFragment } from '../../../credentials/__tests__/fixtures-android';
import { jester as mockJester } from '../../../credentials/__tests__/fixtures-constants';
import { SetUpGoogleServiceAccountKey } from '../../../credentials/android/actions/SetUpGoogleServiceAccountKey';
@@ -59,14 +60,14 @@ afterAll(() => {
});
afterEach(() => {
jest.mocked(promptAsync).mockClear();
asMock(promptAsync).mockClear();
jest.restoreAllMocks();
});
describe(getServiceAccountKeyPathAsync, () => {
describe('when source is ServiceAccountSourceType.path', () => {
it("prompts for path if the provided file doesn't exist", async () => {
jest.mocked(promptAsync).mockImplementationOnce(async () => ({
asMock(promptAsync).mockImplementationOnce(() => ({
filePath: '/google-service-account.json',
}));
const source: ServiceAccountSource = {
@@ -99,7 +100,7 @@ describe(getServiceAccountKeyPathAsync, () => {
describe('when source is ServiceAccountSourceType.prompt', () => {
it('prompts for path', async () => {
jest.mocked(promptAsync).mockImplementationOnce(async () => ({
asMock(promptAsync).mockImplementationOnce(() => ({
filePath: '/google-service-account.json',
}));
const source: ServiceAccountSource = {
@@ -111,15 +112,14 @@ describe(getServiceAccountKeyPathAsync, () => {
});
it('prompts for path until the user provides an existing file', async () => {
jest
.mocked(promptAsync)
.mockImplementationOnce(async () => ({
asMock(promptAsync)
.mockImplementationOnce(() => ({
filePath: '/doesnt-exist.json',
}))
.mockImplementationOnce(async () => ({
.mockImplementationOnce(() => ({
filePath: '/googl-service-account.json',
}))
.mockImplementationOnce(async () => ({
.mockImplementationOnce(() => ({
filePath: '/google-service-account.json',
}));
const source: ServiceAccountSource = {
@@ -166,7 +166,7 @@ describe(getServiceAccountKeyResultAsync, () => {
});
it('returns a local Service Account Key file with a ServiceAccountSourceType.prompt source', async () => {
jest.mocked(promptAsync).mockImplementationOnce(async () => ({
asMock(promptAsync).mockImplementationOnce(() => ({
filePath: '/project_dir/subdir/service-account.json',
}));
const ctx = await createSubmissionContextAsync({
@@ -2,6 +2,7 @@ import { Platform } from '@expo/eas-build-job';
import { vol } from 'memfs';
import { v4 as uuidv4 } from 'uuid';
import { asMock } from '../../../__tests__/utils';
import { jester as mockJester } from '../../../credentials/__tests__/fixtures-constants';
import { getCredentialsFromUserAsync } from '../../../credentials/utils/promptForCredentials';
import { createTestProject } from '../../../project/__tests__/project-utils';
@@ -62,16 +63,16 @@ afterAll(() => {
});
afterEach(() => {
jest.mocked(promptAsync).mockClear();
asMock(promptAsync).mockClear();
});
describe(getAscApiKeyPathAsync, () => {
describe('when source is AscApiKeySourceType.path', () => {
it("prompts for path if the provided file doesn't exist", async () => {
jest.mocked(promptAsync).mockImplementationOnce(async () => ({
asMock(promptAsync).mockImplementationOnce(() => ({
keyP8Path: '/asc-api-key.p8',
}));
jest.mocked(getCredentialsFromUserAsync).mockImplementation(async () => ({
asMock(getCredentialsFromUserAsync).mockImplementation(() => ({
keyId: 'test-key-id',
issuerId: 'test-issuer-id',
}));
@@ -116,10 +117,10 @@ describe(getAscApiKeyPathAsync, () => {
describe('when source is AscApiKeySourceType.prompt', () => {
it('prompts for path', async () => {
jest.mocked(promptAsync).mockImplementationOnce(async () => ({
asMock(promptAsync).mockImplementationOnce(() => ({
keyP8Path: '/asc-api-key.p8',
}));
jest.mocked(getCredentialsFromUserAsync).mockImplementation(async () => ({
asMock(getCredentialsFromUserAsync).mockImplementation(() => ({
keyId: 'test-key-id',
issuerId: 'test-issuer-id',
}));
@@ -137,18 +138,17 @@ describe(getAscApiKeyPathAsync, () => {
});
it('prompts for path until the user provides an existing file', async () => {
jest
.mocked(promptAsync)
.mockImplementationOnce(async () => ({
asMock(promptAsync)
.mockImplementationOnce(() => ({
keyP8Path: '/doesnt-exist.p8',
}))
.mockImplementationOnce(async () => ({
.mockImplementationOnce(() => ({
keyP8Path: '/blah.p8',
}))
.mockImplementationOnce(async () => ({
.mockImplementationOnce(() => ({
keyP8Path: '/asc-api-key.p8',
}));
jest.mocked(getCredentialsFromUserAsync).mockImplementation(async () => ({
asMock(getCredentialsFromUserAsync).mockImplementation(() => ({
keyId: 'test-key-id',
issuerId: 'test-issuer-id',
}));
@@ -190,10 +190,10 @@ describe(getAscApiKeyLocallyAsync, () => {
});
it('returns a local Asc API Key file with a AscApiKeySourceType.prompt source', async () => {
jest.mocked(promptAsync).mockImplementationOnce(async () => ({
asMock(promptAsync).mockImplementationOnce(() => ({
keyP8Path: '/asc-api-key.p8',
}));
jest.mocked(getCredentialsFromUserAsync).mockImplementationOnce(async () => ({
asMock(getCredentialsFromUserAsync).mockImplementationOnce(() => ({
keyId: 'test-key-id',
issuerId: 'test-issuer-id',
}));
@@ -1,6 +1,7 @@
import { Platform } from '@expo/eas-build-job';
import { v4 as uuidv4 } from 'uuid';
import { asMock } from '../../../__tests__/utils';
import { jester as mockJester } from '../../../credentials/__tests__/fixtures-constants';
import { testCommonIosAppCredentialsFragment } from '../../../credentials/__tests__/fixtures-ios';
import { SetUpSubmissionCredentials } from '../../../credentials/ios/actions/SetUpSubmissionCredentials';
@@ -56,8 +57,8 @@ describe(getFromCredentialsServiceAsync, () => {
jest
.spyOn(SetUpSubmissionCredentials.prototype, 'runAsync')
.mockImplementation(async _ctx => 'super secret');
jest.mocked(getBundleIdentifierAsync).mockImplementation(async () => 'com.hello.world');
jest.mocked(promptAsync).mockImplementationOnce(async () => ({ appleId: '[email protected]' }));
asMock(getBundleIdentifierAsync).mockImplementation(() => 'com.hello.world');
asMock(promptAsync).mockImplementationOnce(() => ({ appleId: '[email protected]' }));
const result = await getFromCredentialsServiceAsync(ctx, {
sourceType: CREDENTIALS_SERVICE_SOURCE,
@@ -83,7 +84,7 @@ describe(getFromCredentialsServiceAsync, () => {
jest
.spyOn(SetUpSubmissionCredentials.prototype, 'runAsync')
.mockImplementation(async _ctx => testCommonIosAppCredentialsFragment);
jest.mocked(getBundleIdentifierAsync).mockImplementation(async () => 'com.hello.world');
asMock(getBundleIdentifierAsync).mockImplementation(() => 'com.hello.world');
const result = await getFromCredentialsServiceAsync(ctx, {
sourceType: CREDENTIALS_SERVICE_SOURCE,
@@ -2,6 +2,7 @@ import { Platform } from '@expo/eas-build-job';
import { vol } from 'memfs';
import { v4 as uuidv4 } from 'uuid';
import { asMock } from '../../../__tests__/utils';
import { jester as mockJester } from '../../../credentials/__tests__/fixtures-constants';
import { SubmissionMutation } from '../../../graphql/mutations/SubmissionMutation';
import { createTestProject } from '../../../project/__tests__/project-utils';
@@ -49,7 +50,7 @@ describe(IosSubmitCommand, () => {
});
afterEach(() => {
jest.mocked(getProjectIdAsync).mockClear();
asMock(getProjectIdAsync).mockClear();
});
describe('non-interactive mode', () => {
+1 -1
View File
@@ -8,7 +8,7 @@ export async function isExistingFileAsync(filePath: string): Promise<boolean> {
try {
const stats = await fs.stat(filePath);
return stats.isFile();
} catch {
} catch (e) {
return false;
}
}
+1 -1
View File
@@ -53,7 +53,7 @@ function parseLogs(logs: string): LogLine[] {
let parsedLine;
try {
parsedLine = JSON.parse(line);
} catch {
} catch (error) {
continue;
}
let level: LogLine['level'];
@@ -35,7 +35,7 @@ export async function readReleaseChannelSafelyAsync(projectDir: string): Promise
androidManifest,
AndroidConfig.Updates.Config.RELEASE_CHANNEL
);
} catch {
} catch (err) {
return null;
}
}
@@ -51,7 +51,7 @@ export async function readChannelSafelyAsync(projectDir: string): Promise<string
return null;
}
return JSON.parse(stringifiedRequestHeaders)['expo-channel-name'] ?? null;
} catch {
} catch (err) {
return null;
}
}
@@ -42,7 +42,7 @@ export async function readReleaseChannelSafelyAsync(projectDir: string): Promise
try {
const expoPlist = await readExpoPlistAsync(projectDir);
return expoPlist[IOSConfig.Updates.Config.RELEASE_CHANNEL] ?? null;
} catch {
} catch (err) {
return null;
}
}
@@ -52,7 +52,7 @@ export async function readChannelSafelyAsync(projectDir: string): Promise<string
const expoPlist = await readExpoPlistAsync(projectDir);
const updatesRequestHeaders = expoPlist['EXUpdatesRequestHeaders'];
return updatesRequestHeaders['expo-channel-name'] ?? null;
} catch {
} catch (err) {
return null;
}
}
@@ -1,3 +1,4 @@
import { asMock } from '../../__tests__/utils';
import { ApiV2Error } from '../../api';
import { promptAsync } from '../../prompts';
import { Actor, loginAsync } from '../User';
@@ -15,12 +16,12 @@ jest.mock('../User', () => ({
}));
beforeEach(() => {
jest.mocked(promptAsync).mockReset();
jest.mocked(promptAsync).mockImplementation(async () => {
asMock(promptAsync).mockReset();
asMock(promptAsync).mockImplementation(() => {
throw new Error('Should not be called');
});
jest.mocked(loginAsync).mockReset();
asMock(loginAsync).mockReset();
});
const userStub: Actor = {
@@ -51,15 +52,13 @@ describe('ensureActorHasUsername', () => {
describe(showLoginPromptAsync, () => {
it('prompts for OTP when 2FA is enabled', async () => {
jest
.mocked(promptAsync)
.mockImplementationOnce(async () => ({ username: 'hello', password: 'world' }))
.mockImplementationOnce(async () => ({ otp: '123456' }))
.mockImplementation(async () => {
asMock(promptAsync)
.mockImplementationOnce(() => ({ username: 'hello', password: 'world' }))
.mockImplementationOnce(() => ({ otp: '123456' }))
.mockImplementation(() => {
throw new Error("shouldn't happen");
});
jest
.mocked(loginAsync)
asMock(loginAsync)
.mockImplementationOnce(async () => {
throw new ApiV2Error({
message: 'An OTP is required',
@@ -77,7 +76,7 @@ describe(showLoginPromptAsync, () => {
},
});
})
.mockImplementation(async () => {});
.mockImplementation(() => {});
await showLoginPromptAsync();
Loaded 100 of 118 files, more files were not shown because too many files have changed in this diff. Show more