Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Epic7 e2e tests #1413

Merged
merged 10 commits into from
Oct 21, 2017
Merged

Epic7 e2e tests #1413

merged 10 commits into from
Oct 21, 2017

Conversation

dan-f
Copy link
Contributor

@dan-f dan-f commented Oct 12, 2017

This one fixes #1316.

I figured the benefits of driving multiple browsers concurrently (not yet introduced here) outweighs the nicer API cypress offers.

package.json Outdated
@@ -14,35 +14,35 @@
},
"main": "index.js",
"scripts": {
"start": "NODE_ENV=production node --trace-warnings ./src/server/server.babel.js",
"start": "NODE_ENV=${NODE_ENV:-production} node --trace-warnings ./src/server/server.babel.js",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's this do?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there's a NODE_ENV already bound in the environment, it uses that value, otherwise production.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you give me an example where this is useful? it kinda scares me because this makes it seem a little less expressive because after calling this, i still don't know what env it's running in.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could revert this change if you like - it's a holdover from when I was associating the value of NODE_ENV with the .env.<NODE_ENV> files.

That said, here's an example of how it'd work:

$ # when NODE_ENV='' (unbound)
$ npm start # NODE_ENV is bound to 'production'
$ # when NODE_ENV='production'
$ npm start # NODE_ENV is bound to 'production'
$ # when NODE_ENV='development'
$ npm start # NODE_ENV is bound to 'development'
$ # ...etc

@codecov
Copy link

codecov bot commented Oct 12, 2017

Codecov Report

Merging #1413 into epic7 will not change coverage.
The diff coverage is n/a.

Impacted file tree graph

@@           Coverage Diff           @@
##            epic7    #1413   +/-   ##
=======================================
  Coverage   44.01%   44.01%           
=======================================
  Files         359      359           
  Lines        4194     4194           
  Branches      572      572           
=======================================
  Hits         1846     1846           
  Misses       2019     2019           
  Partials      329      329

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 91217a9...880bc05. Read the comment docs.

package.json Outdated
"start:tunnel": "export $(grep ^ULTRAHOOK_API_KEY .env | tr -d '\"') && ultrahook -k $ULTRAHOOK_API_KEY dev 3000",
"dev": "if [ ! -f \"dll/vendors.json\" ] ; then npm run build:dll ; fi ; NODE_ENV=development node --trace-warnings ./src/server/server.babel.js",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[FYI] - most of these changes are just me re-ordering things in rough alphabetical order / trying to keep similar scripts close to each other.

@dan-f
Copy link
Contributor Author

dan-f commented Oct 13, 2017

Hey @mattkrick - if you have any early feedback, please feel free to leave it! I've got the auth tests working, and I'm planning to refactor that runTests script a bit so that it's a bit less monolithic / can run tests against an existing server, rather than always spinning one up on it's own.

I'm also thinking of keeping this PR scoped to a proof-of-concept implementation, and adding more e2e tests incrementally. If you hav other feelings let me know!

const config = {silent: true};

if (NODE_ENV) {
config.path = `.env.${NODE_ENV}`;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this change will have implications for others' dev environments, so I can revert it if need be. The main point is that this used to only use .env.test when NODE_ENV === "test", and I changed it to use .env.${whateverTheNodeEnvIs}. I'm finding it useful to keep a bunch of local .env files for different builds.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'm not a huge fan of the multiple .env files. it may be a necessary evil for running a test on CI, but i'd be more inclined to get rid of the .env.test and move that stuff to the CI internal environment instead of expanding the scope. see https://github.com/motdotla/dotenv#should-i-have-multiple-env-files

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is fine, as long as .env* is in .gitignore. It's a fine thing to be able to be able to run your local instance and fire up the e2e tests for local integration (or even local continuous integration). I'd also suggest a fallback to .env if it cannot find the local .env.$NODE_ENV file...

e2e/runTests.js Outdated
const execP = promisify(child_process.exec);
const globP = promisify(glob);

const SERVER_TIME_TO_START = 10000;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest that any of these "big knob" constants are kept in their own file.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yessir! I'd love to be able to fork the server process and receive a message back when the server is ready to accept connections. If you've got any ideas, let me know! I'll be looking into this in my current refactor.

e2e/runTests.js Outdated
const mocha = new Mocha({
// Note that 20 seconds is a reasonably long timeout. I'm betting that
// tests will generally fail due to errors rather than timeouts.
timeout: 20000,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is also a "big knob" constant, and should be considered to be in its own file

@dan-f dan-f changed the title [WIP] Epic7 e2e tests Epic7 e2e tests Oct 17, 2017
@dan-f
Copy link
Contributor Author

dan-f commented Oct 17, 2017

@jordanh @mattkrick - if either of you'd like to take a final look, I've addressed feedback and got the initial tests running on circle! I'm thinking that further testing can be added on incrementally rather than monolithically in this PR.

Copy link
Member

@mattkrick mattkrick left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

absolutely amazing! i really tried to beat this up but all i could find were a couple nits. really, really great.

- type: shell
name: E2E test build
command: |
cp $DEVOPS_WORKDIR/environments/e2e ./.env
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice!


export async function hasDrivers() {
try {
const realDriverFilenames = new Set(await promisify(fs.readdir)(DRIVERS_DIR));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

woooo this is a lot to take in for a single line, i'm cool with it if you are though

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haha, yeah, I guess it is a bit much. I'll leave it for now; not expecting this file to cause a lot of tension.

export async function hasDrivers() {
try {
const realDriverFilenames = new Set(await promisify(fs.readdir)(DRIVERS_DIR));
return !DRIVERS.find((driver) => !realDriverFilenames.has(driver.driverFilename));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

my brain is a little fried, but couldn't you simplify away the double negative?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm always on the lookout to good solutions to brain fry!

I believe the double negative here is necessary, because if you think about it, it's not a real double negative:

The inner !realDriverFilenames.has(driver.driverFilename): We iterate over the DRIVERS array, looking for one that is not downloaded. If we got rid of this, we might not make it through the whole array, and may miss a missing driver.

The outer !DRIVERS.find(...): if that inner find returns us a driver, then we need to download that webdriver; if it returns undefined, then we've got all our drivers. The truthiness of those values is the opposite of the question "do we have all of our drivers?"

Hope that helps!

}

async function ensureDrivers() {
const drivers = [
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could we replace this with the DRIVERS constant? to dry it up?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doh! Yes! Good catch.

}
}

async function ensureDrivers() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you could make this a sync function & just return Promise.all if you wanted. Usually eslint picks up things that don't need to be async.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch!


commander
.version('1.0.0')
.description('Runs end-to-end tests against the Action app')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

heh, let's call it Parabol. Calling it Action is deprecated, but hard to move away from 😄

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You got it!

.click();
await all(
driver
.wait(until.urlMatches(BASE_URL_REGEX), 2000, 'Logging out did not redirect to the base URL'),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we make that 2000 a constant? i can see us reusing this a bunch
Maybe 2000 for things that relay on our server. 5000 for things that relay on a 3rd party like auth0, etc.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, fair point. My only tension on this is that timing is finicky, and some things may take longer than others. That said, we can address that when we get there. What if I declared a wait object that looks like { short: 2000, medium: 3000, long: 5000 }?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i like it! and maybe a comment to say when to use each. it'll be a heuristic, but at least it'll give the next guy an idea of where to start.

cache.credentials = credentials;
});

it('can log in (and out) with valid credentials', async () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this makes me so happy i could barf. well done!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gee, thanks!

package.json Outdated
@@ -14,35 +14,35 @@
},
"main": "index.js",
"scripts": {
"start": "NODE_ENV=production node --trace-warnings ./src/server/server.babel.js",
"start": "NODE_ENV=${NODE_ENV:-production} node --trace-warnings ./src/server/server.babel.js",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you give me an example where this is useful? it kinda scares me because this makes it seem a little less expressive because after calling this, i still don't know what env it's running in.

@mattkrick
Copy link
Member

@dan-f great work! Only bug is that it borks the build on deployment.
The error is Error: Cannot find module '../../build/assets.json'.
That makes me think that the build directory isn't being built properly.
perhaps the {NODE_ENV:-production} isn't setting it to production? when we run npm run build?
if that's the case, we could always have a build:e2e that explicitly sets the env to test.

@dan-f
Copy link
Contributor Author

dan-f commented Oct 18, 2017

Hey @mattkrick thanks for the suggestions and kind words! Regarding your last comment (Error: Cannot find module '../../build/assets.json')

Oops! I see two patterns here:

  1. Revert that {NODE_ENV:-production} change; every npm script uses the exact NODE_ENV it's declared to use.

    • Pros: Scripts rely on less global state; are predictable
    • Cons: We'll end up with some duplicated scripts; e.g. npm run db:migrate vs npm run db:migrate-e2e; If we were to do this I'd like to see an enforced pattern for consistency.
  2. Keep that change; npm scripts that care about NODE_ENV need to be called with an explicit NODE_ENV

    • Pros: need to maintain fewer scripts; script names are just about what they do and not about where they do it; the NODE_ENV is explicit from how scripts are called
    • Cons: need to explicitly declare NODE_ENV in CI/CD configs

So we could fix this by either reverting the change such that npm run build always uses NODE_ENV=production, or we could just call NODE_ENV=production npm run build in the CI config.

Looking forward to your thoughts!

@mattkrick
Copy link
Member

IMO i like the first option, but i almost always opt for a bunch of smaller single-use scripts.
A 3rd option: we could rewrite the script to take a -e2e CLI param that changes it.

@dan-f
Copy link
Contributor Author

dan-f commented Oct 18, 2017

Well, in any case, we're going to need some CLI interface by which we can specify the particular NODE_ENV. I can understand the arguments for option 1, so let's do that!

@dan-f
Copy link
Contributor Author

dan-f commented Oct 18, 2017

@mattkrick - build passes w/ fixes addressing your final feedback. I'm not quite sure how to verify the prod build, so I'd appreciate your help with that!

@mattkrick mattkrick merged commit 9b6c5e6 into epic7 Oct 21, 2017
@mattkrick mattkrick deleted the epic7-e2e-tests branch October 21, 2017 02:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants