Skip to content

Commit d7a8439

Browse files
authored
v0.1.0 (#5)
* feat(commands): run and open commands added for building config then running/opening cypress * feat(commands): add support for loading path from `cypress.json` * feat(core): add prettier
1 parent bc3f6e0 commit d7a8439

16 files changed

+1884
-1594
lines changed

.eslintrc.js

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,44 @@
11
module.exports = {
22
parser: "babel-eslint",
3-
"extends": "airbnb",
3+
'extends': ['airbnb', 'prettier'],
4+
root: true,
5+
plugins: ['import', 'babel', 'prettier'],
6+
settings: {
7+
'import/resolver': {
8+
node: {
9+
moduleDirectory: ['node_modules', '/']
10+
}
11+
}
12+
},
413
env: {
514
browser: true,
615
node: true
716
},
817
rules: {
9-
"comma-dangle": [2, "never"],
18+
"import/prefer-default-export": 0,
1019
"no-shadow": 0,
1120
"consistent-return": 0,
1221
"no-new": 0,
1322
"new-cap": 0,
14-
"max-len": 0,
15-
"brace-style": [2, "stroustrup"]
23+
"prettier/prettier": [
24+
'error',
25+
{
26+
singleQuote: true, // airbnb
27+
trailingComma: 'all', // airbnb
28+
}
29+
]
1630
},
17-
plugins: [
18-
"react"
19-
]
31+
overrides: {
32+
files: ['cmds/**'],
33+
rules: {
34+
"comma-dangle": ["error", { "functions": "never" }],
35+
"prettier/prettier": [
36+
'error',
37+
{
38+
singleQuote: true, // airbnb
39+
trailingComma: 'none', // airbnb
40+
}
41+
]
42+
}
43+
}
2044
}

README.md

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -112,12 +112,19 @@ Tests will run faster locally if you tests against the build version of your app
112112

113113
### Folders
114114

115-
`cypress` is the default folder where config is loaded from, but these other common folders are supported:
116-
117-
```
118-
test/ui
119-
test/e2e
120-
```
115+
`cypress` is the default folder where config is loaded from, but you can use another folder by specifiying a different setting for the `integrationFolder` parameter in `cypress.json`:
116+
117+
```json
118+
{
119+
"projectId": "<- your project id ->",
120+
"fixturesFolder": "test/e2e/fixtures",
121+
"integrationFolder": "test/e2e/integration",
122+
"pluginsFile": "test/e2e/plugins/index.js",
123+
"screenshotsFolder": "test/e2e/screenshots",
124+
"videosFolder": "test/e2e/videos",
125+
"supportFile": "test/e2e/support/index.js"
126+
}
127+
```
121128

122129
## Docs
123130

cmds/createTestEnvFile.js

Lines changed: 82 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -5,53 +5,66 @@
55

66
const chalk = require('chalk');
77
const fs = require('fs');
8-
const fig = require('figures');
98
const pickBy = require('lodash/pickBy');
9+
const get = require('lodash/get');
1010
const size = require('lodash/size');
1111
const keys = require('lodash/keys');
1212
const isUndefined = require('lodash/isUndefined');
13-
const path = require('path');
14-
const envVarBasedOnCIEnv = require('../lib/utils').envVarBasedOnCIEnv;
15-
const getServiceAccount = require('../lib/utils').getServiceAccount;
16-
const getEnvPrefix = require('../lib/utils').getEnvPrefix;
17-
const constants = require('../lib/constants');
18-
const logger = require('../lib/logger');
19-
2013
const {
21-
DEFAULT_BASE_PATH,
22-
DEFAULT_TEST_ENV_FILE_NAME,
23-
DEFAULT_SERVICE_ACCOUNT_PATH
24-
} = constants;
25-
26-
const testEnvFileFullPath = path.join(DEFAULT_BASE_PATH, DEFAULT_TEST_ENV_FILE_NAME);
27-
const serviceAccountPath = path.join(DEFAULT_BASE_PATH, DEFAULT_SERVICE_ACCOUNT_PATH);
14+
envVarBasedOnCIEnv,
15+
getServiceAccount,
16+
getEnvPrefix,
17+
getCypressFolderPath,
18+
readJsonFile
19+
} = require('../lib/utils');
20+
const {
21+
DEFAULT_CONFIG_FILE_NAME,
22+
DEFAULT_TEST_ENV_FILE_NAME
23+
} = require('../lib/constants');
24+
const {
25+
FIREBASE_CONFIG_FILE_PATH,
26+
TEST_ENV_FILE_PATH
27+
} = require('../lib/filePaths');
28+
const logger = require('../lib/logger');
2829

2930
/**
3031
* @param {functions.Event} event - Function event
3132
* @param {functions.Context} context - Functions context
3233
* @return {Promise}
3334
*/
34-
function createTestEnvFile() {
35-
const envPrefix = getEnvPrefix();
36-
37-
// Get UID from environment (falls back to test/e2e/config.json for local)
35+
function createTestEnvFile(envName) {
36+
const envPrefix = getEnvPrefix(envName);
37+
// Get UID from environment (falls back to cypress/config.json for local)
3838
const uid = envVarBasedOnCIEnv('TEST_UID');
39-
39+
const varName = `${envPrefix}TEST_UID`;
40+
const testFolderPath = getCypressFolderPath();
41+
const configPath = `${testFolderPath}/${DEFAULT_CONFIG_FILE_NAME}`;
4042
// Throw if UID is missing in environment
4143
if (!uid) {
42-
return Promise.reject(new Error(
43-
`${envPrefix}TEST_UID is missing from environment. Confirm that ${
44-
constants.DEFAULT_TEST_FOLDER_PATH
45-
}/config.json contains either ${envPrefix}TEST_UID or TEST_UID.`
46-
));
44+
/* eslint-disable */
45+
const errMsg = `${chalk.cyan(varName)} is missing from environment. Confirm that ${chalk.cyan(configPath)} contains either ${chalk.cyan(varName)} or ${chalk.cyan('TEST_UID')}.`;
46+
/* eslint-enable */
47+
return Promise.reject(new Error(errMsg));
4748
}
4849

49-
const FIREBASE_PROJECT_ID = envVarBasedOnCIEnv('FIREBASE_PROJECT_ID');
50-
51-
logger.info(`Generating custom auth token for Firebase project with projectId: ${chalk.cyan(FIREBASE_PROJECT_ID)}`);
50+
// Get project from .firebaserc
51+
const firebaserc = readJsonFile(FIREBASE_CONFIG_FILE_PATH);
52+
const FIREBASE_PROJECT_ID =
53+
envVarBasedOnCIEnv(`${envPrefix}FIREBASE_PROJECT_ID`) ||
54+
get(
55+
firebaserc,
56+
`projects.${envName}`,
57+
get(firebaserc, 'projects.default', '')
58+
);
59+
60+
logger.info(
61+
`Generating custom auth token for Firebase project with projectId: ${chalk.cyan(
62+
FIREBASE_PROJECT_ID
63+
)}`
64+
);
5265

5366
// Get service account from local file falling back to environment variables
54-
const serviceAccount = getServiceAccount();
67+
const serviceAccount = getServiceAccount(envName);
5568

5669
// Confirm service account has all parameters
5770
const serviceAccountMissingParams = pickBy(serviceAccount, isUndefined);
@@ -62,16 +75,22 @@ function createTestEnvFile() {
6275
return Promise.reject(new Error(errMsg));
6376
}
6477

65-
// Get project ID from environment variable
66-
const projectId = process.env.GCLOUD_PROJECT || envVarBasedOnCIEnv('FIREBASE_PROJECT_ID');
67-
6878
// Remove firebase- prefix
69-
const cleanedProjectId = projectId.replace('firebase-', '');
79+
const cleanedProjectId = FIREBASE_PROJECT_ID.replace('firebase-', '');
7080

7181
// Handle service account not matching settings in config.json (local)
72-
if (serviceAccount.project_id !== FIREBASE_PROJECT_ID && serviceAccount.project_id !== projectId) {
82+
if (
83+
envName !== 'local' &&
84+
serviceAccount.project_id !== FIREBASE_PROJECT_ID
85+
) {
7386
/* eslint-disable no-console */
74-
logger.warn(`project_id "${chalk.cyan(serviceAccount.project_id)}" does not match env var: "${chalk.cyan(envVarBasedOnCIEnv('FIREBASE_PROJECT_ID'))}"`);
87+
logger.warn(
88+
`project_id in service account (${chalk.cyan(
89+
serviceAccount.project_id
90+
)}) does not match associated project in .firebaserc (${chalk.cyan(
91+
FIREBASE_PROJECT_ID
92+
)})`
93+
);
7594
/* eslint-enable no-console */
7695
}
7796

@@ -90,22 +109,25 @@ function createTestEnvFile() {
90109
return appFromSA
91110
.auth()
92111
.createCustomToken(uid, { isTesting: true })
93-
.then((customToken) => {
112+
.then(customToken => {
94113
/* eslint-disable no-console */
95114
logger.success(
96-
`Custom token generated successfully, writing to ${chalk.cyan(constants.DEFAULT_TEST_ENV_FILE_NAME)}`
115+
`Custom token generated successfully, writing to ${chalk.cyan(
116+
DEFAULT_TEST_ENV_FILE_NAME
117+
)}`
97118
);
98119
/* eslint-enable no-console */
99120
// Remove firebase app
100121
appFromSA.delete();
122+
const currentCypressEnvSettings = readJsonFile(TEST_ENV_FILE_PATH);
101123

102-
// Create config object to be written into test env file
103-
const newCypressConfig = {
124+
// Create config object to be written into test env file by combining with existing config
125+
const newCypressConfig = Object.assign({}, currentCypressEnvSettings, {
104126
TEST_UID: envVarBasedOnCIEnv('TEST_UID'),
105127
FIREBASE_API_KEY: envVarBasedOnCIEnv('FIREBASE_API_KEY'),
106128
FIREBASE_PROJECT_ID,
107129
FIREBASE_AUTH_JWT: customToken
108-
};
130+
});
109131
const stageProjectId = envVarBasedOnCIEnv('STAGE_FIREBASE_PROJECT_ID');
110132
const stageApiKey = envVarBasedOnCIEnv('STAGE_FIREBASE_API_KEY');
111133

@@ -115,23 +137,17 @@ function createTestEnvFile() {
115137
}
116138

117139
// Write config file to cypress.env.json
118-
fs.writeFileSync(testEnvFileFullPath, JSON.stringify(newCypressConfig, null, 2));
119-
120-
logger.success(`${chalk.cyan(constants.DEFAULT_TEST_ENV_FILE_NAME)} updated successfully`);
121-
122-
// Create service account file if it does not already exist (for use in reporter)
123-
if (!fs.existsSync(serviceAccountPath)) {
124-
// Write service account file as string
125-
fs.writeFileSync(
126-
serviceAccountPath,
127-
JSON.stringify(serviceAccount, null, 2)
128-
);
140+
fs.writeFileSync(
141+
TEST_ENV_FILE_PATH,
142+
JSON.stringify(newCypressConfig, null, 2)
143+
);
129144

130-
logger.success(`${chalk.cyan('serviceAccount.json')} created successfully`);
131-
}
145+
logger.success(
146+
`${chalk.cyan(DEFAULT_TEST_ENV_FILE_NAME)} updated successfully`
147+
);
132148
return customToken;
133149
})
134-
.catch((err) => {
150+
.catch(err => {
135151
/* eslint-disable no-console */
136152
logger.error(
137153
`Custom token could not be generated for uid: ${chalk.cyan(uid)}`,
@@ -151,17 +167,20 @@ function createTestEnvFile() {
151167
* # make sure you serviceAccount.json exists
152168
* cypress-firebase createEnv
153169
*/
154-
module.exports = function (program) {
170+
module.exports = function runCreateTestEnvFile(program) {
155171
program
156-
.command('createTestEnvFile')
172+
.command('createTestEnvFile [envName]')
157173
.description(
158174
'Build configuration file containing a token for authorizing a firebase instance'
159175
)
160-
.action((directory, options) => createTestEnvFile(options)
161-
.then(() => process.exit(0))
162-
.catch((err) => {
163-
logger.error(`Test env file could not be created:\n${err.message}`);
164-
process.exit(1);
165-
return Promise.reject(err);
166-
}));
176+
.action(envArg => {
177+
const envName = typeof envArg === 'string' ? envArg : 'local';
178+
return createTestEnvFile(envName)
179+
.then(() => process.exit(0))
180+
.catch(err => {
181+
logger.error(`Test env file could not be created:\n${err.message}`);
182+
process.exit(1);
183+
return Promise.reject(err);
184+
});
185+
});
167186
};

cmds/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ module.exports = function setupCommands(client) {
66
}
77

88
client.createTestEnvFile = loadCommand('createTestEnvFile');
9+
client.run = loadCommand('run');
10+
client.open = loadCommand('open');
911

1012
return client;
1113
};

cmds/open.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/**
2+
* run commander component
3+
* To use add require('../cmds/run.js')(program) to your commander.js based node executable before program.parse
4+
*/
5+
6+
const chalk = require('chalk');
7+
const { runCommand } = require('../lib/utils');
8+
const logger = require('../lib/logger');
9+
10+
/**
11+
* @name open
12+
* @description Create test environment config then open Cypress Test Runner
13+
* @param {String} envName
14+
*/
15+
module.exports = function open(program) {
16+
program
17+
.command('open [envName]')
18+
.description(
19+
'Build configuration file containing a token for authorizing a firebase instance'
20+
)
21+
.action((envArg) => {
22+
const envName = typeof envArg === 'string' ? envArg : 'local';
23+
return runCommand({ command: `cypress-firebase createTestEnvFile ${envName}` })
24+
.then(() => {
25+
logger.info(`Opening test runner for environment: ${chalk.cyan(envName)}`);
26+
return runCommand({ command: 'npx', args: ['cypress', 'open', '--env', `envName=${envName}`] });
27+
})
28+
.then(() => process.exit(0))
29+
.catch((err) => {
30+
logger.error(`Test runner open could not be completed:\n${err.message}`);
31+
process.exit(1);
32+
return Promise.reject(err);
33+
});
34+
});
35+
};

cmds/run.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/**
2+
* run commander component
3+
* To use add require('../cmds/run.js')(program) to your commander.js based node executable before program.parse
4+
*/
5+
6+
const chalk = require('chalk');
7+
const { runCommand } = require('../lib/utils');
8+
const logger = require('../lib/logger');
9+
10+
/**
11+
* @name run
12+
* @description Deploy to Firebase only on build branches (master, stage, prod)
13+
* @param {String} envName
14+
*/
15+
module.exports = function run(program) {
16+
program
17+
.command('run [envName]')
18+
.description(
19+
'Build configuration file containing a token for authorizing a firebase instance'
20+
)
21+
.action((envArg) => {
22+
const envName = typeof envArg === 'string' ? envArg : 'local';
23+
return runCommand({ command: `cypress-firebase createTestEnvFile ${envName}` })
24+
.then(() => {
25+
logger.info(`Starting test run for environment: ${chalk.cyan(envName)}`);
26+
return runCommand({ command: 'npx', args: ['cypress', 'run', '--env', `envName=${envName}`] });
27+
})
28+
.then(() => process.exit(0))
29+
.catch((err) => {
30+
logger.error(`Run could not be completed:\n${err.message}`);
31+
process.exit(1);
32+
return Promise.reject(err);
33+
});
34+
});
35+
};

0 commit comments

Comments
 (0)