-
Notifications
You must be signed in to change notification settings - Fork 4
set up e2e tests with playwright #941
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
Conversation
a2d7d87
to
19703f6
Compare
Netlify Deployments: |
Lighthouse results: results for https://ocw-hugo-themes-www-pr-941--ocw-next.netlify.app/:
results for https://ocw-hugo-themes-www-pr-941--ocw-next.netlify.app/search/:
results for https://ocw-hugo-themes-course-v2-pr-941--ocw-next.netlify.app/:
|
c67d525
to
bc449de
Compare
@@ -3,31 +3,21 @@ | |||
set -euo pipefail | |||
|
|||
source package_scripts/common.sh | |||
load_env --require-dot-env |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I got rid of load_env
entirely. This function made it very hard to specify variables manually. It was less like "load env" and more like "forcefully override any environment variables that were already defined; replace them with the content of .env"
So now:
yarn start:course
will by default use your env variables from.env
- but
OCW_TEST_COURSE=whatever yarn start:course
will use OCW_TEST_COURSE as the value of that variable . I.e., now we can override them at the command line.
The downside is that running ./package_scripts/start_course.sh
by itself in your shell will now probably fail, because...environment variables are missing. I'm sure there's some way to source your env vars if you wanted to, though. (In yarn scripts, we run it via dotenv-cli
.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea, I think we added this a while back because we were having trouble with dotenv
. Like I mentioned above, I didn't realize that the dotenv-cli
package was required and that you had to prefix any commands that needed the .env
file with it. They don't mention it anywhere on the dotenv
Github and it's kind of puzzling why the "CLI" component of that is a separate package. Either way, it works much better so yea it makes sense to get rid of this.
@@ -1,9 +1,10 @@ | |||
{{- $port := 3001 -}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This $port
should be $port := getenv "WEBPACK_PORT"
. All other instances of 3001 have been replaced with the environment variable.
The reason I left this one hard-coded is that in order to access the environment variable with hugo we need to whitelist it in the config, which requires a PR to ocw-hugo-projects.
I wanted CI to pass, so for now I have left this as instance as 3001.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the hugo projects PR: mitodl/ocw-hugo-projects#222, and https://github.com/mitodl/ocw-hugo-themes/tree/cc/ci3 branch of themes includes the required change.
<section class="course-detail-section"> | ||
{{ partial "course_description.html" (dict "context" .) }} | ||
</div> | ||
</section> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changing div -> section here made seemed reasonable and made selecting "Course Info" section in Playwright a little easier. See testing-library/dom-testing-library#403 (comment) for a related issue. (I believe that issue is where I picked up the pattern of using .closest()
for stuff like this. 🤷 )
"start:www": "concurrently \"npm:start:webpack\" \"npm:start:www:hugo\" --kill-others", | ||
"start:course": "concurrently \"npm:start:webpack\" \"npm:start:course:hugo\" --kill-others", | ||
"start:fields": "concurrently \"npm:start:webpack\" \"npm:start:fields:hugo\" --kill-others", | ||
"start:webpack": "dotenv -- webpack serve --config ./base-theme/assets/webpack/webpack.dev.ts --hot --host 0.0.0.0 --stats-children", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is dotenv-cli. It loads env vars on the cli.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like an idiot now because I didn't realize this package was required for NPM to pass env variables to package scripts. I thought all you needed install was the dotenv
package and that would happen, but apparently I was mistaken.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I actually had never used this package before; I have used
node -r dotenv/config your_script.js
(See https://www.npmjs.com/package/dotenv#Preload) but that only works if you're calling node.
const env = envalid.cleanEnv(process.env, { | ||
WEBPACK_PORT: envalid.port() | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is https://www.npmjs.com/package/envalid. It's nice for (a) validating that env variables are set and providing good error messages if they are not, and (b) typescript. Since process.env.WHATEVER
is string | undefined
by default.
I've scattered a few envalid
calls into the repo. I think one common usage of envalid is to have a single file that validates the environment variables and sets defaults and stuff. (Like settings.py
in Django, I guess). I think that would be really nice, but we'd have to have some centralized js-based cli through which we call hugo (Rather than our current shell scripts). I briefly explored doing something like that with oclif, but it seemed like way too big a change for the moment.
Aside: oclif is cool. And it's built by Salesforce / it's what the heroku CLI is built upon. And while I like the idea of a centralized CLI for this repo, like django provides us elsewhere, I would not recommend oclif
because https://npmtrends.com/commander-vs-oclif-vs-yargs
@@ -3,7 +3,6 @@ | |||
set -euo pipefail |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@gumaerc Do you think we need this script anymore?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A while back I had been using this script to build all courses locally, and even before that it was actually being used in an EC2 box to deploy the whole site before we had ocw-studio
and Concourse. Now that we have a full dev setup for ocw-studio
that can build the whole site locally, I think we can safely remove this without consequence.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unless someone wants it here, I think I'll drop that script in a separate PR.
@@ -0,0 +1 @@ | |||
# ocw-ci-test-www |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have not written very many tests for www
yet, and this is the only file in the test site.
Really I included www
mostly as a proof-of-concept for building / running tests against multiple sites using multiple hugo configs.
|
||
### Is there a `--watch` mode for re-running tests? | ||
|
||
No. For VS Code users, a similar experience can be achieved with [Playwright for VS Code](https://marketplace.visualstudio.com/items?itemName=ms-playwright.playwright). Individual tests can be run from your editor with the click of a button. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For VS Code users, ... Individual tests can be run from your editor with the click of a button.
I swear I'm not just a shill for Microsoft.
bc449de
to
df0be02
Compare
df0be02
to
f5ba169
Compare
During testing, found out we first need to run |
Runs in my tests with ocw-hugo-themes/playwright.config.ts Line 41 in f5ba169
http://0.0.0.0:${env.WEBPACK_PORT}/static/css/main.css .
|
bbeb67b
to
47c9006
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me!
testDir: './tests-e2e', | ||
/* Maximum time one test can run for. */ | ||
timeout: 30 * 1000, | ||
expect: { | ||
timeout: 5000 | ||
}, | ||
fullyParallel: true, | ||
forbidOnly: !!process.env.CI, | ||
retries: process.env.CI ? 2 : 0, | ||
workers: process.env.CI ? 1 : undefined, | ||
reporter: 'html', | ||
use: { | ||
actionTimeout: 0, | ||
trace: 'on-first-retry', | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BTW: Most of these settings are what Playwright recommends. I created this config file via (https://playwright.dev/docs/intro#installing-playwright)
yarn create playwright
and these were the settings it gave me. My customizations were:
- removing some comments because it seemed cluttery and TS docstrings / playwright docs have the same information.
- removing the extra browsers (firefox, webkit)
- customizing the
webServer
portion.
We could restore firefox + webkit. I'm indifferent. I'm not sure how useful the extra browsers would be at the moment; maybe if we were doing visual or very interactive tests.
"packageManager": "[email protected]" | ||
"packageManager": "[email protected]", | ||
"devDependencies": { | ||
"@playwright/test": "^1.27.1" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BTW: I mentioned on a zoom call once that there is playwright-testing-library akin to other libs we use like react-testing-library (all part of the Testing Library family).
I was initially using it, but decided to drop it. Playwright itself introduced some features inspired by testing-library, and the creator of playwright-testing-library says 1:
Start out without playwright-testing-library and see how the new Testing Library API in 1.27 serves y
Footnotes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All of this looks great and is a good start to getting some actual testing done here. Everything works, I just have one request regarding the WEBPACK_PORT
environment variable:
@@ -18,7 +23,7 @@ const devOverrides: Configuration = { | |||
devtool: "eval-source-map", | |||
|
|||
devServer: { | |||
port: process.env.PORT || 3001, | |||
port: env.WEBPACK_PORT, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not env.WEBPACK_PORT || 3001
? I get an error if I try to run yarn test:e2e
without WEBPACK_PORT
explicitly in my env. We default it to 3001 when running a site for dev, so why wouldn't we do that for tests?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We default it to 3001 when running a site for dev
If we merge this PR as-is, that is not longer true.
But, to be sure, having a required WEBPACK_PORT env variable also annoys me. But I do not think we currently have a good way to set the default that is accessible in JS+Hugo, and the current situation on main
illustrates this.
Currently on main:
- webpack.dev.ts uses
process.env.PORT || 3001
- but base-theme/layout/partials/webpack_url.html always uses
3001
.
So you can see the inconsistency: if you have PORT
env var set, webpack will respect it but hugo will not. 💥.
Here are some options that occur to me:
-
Hardcode 3001 everywhere. I don't really want to do this, but it doesn't actually seem terrible
-
Use an environment variable with no default. If it's not set, you get an error message. A little annoying.
-
Use an environment variable with inlined-defaults. This is the current situation, and IMO it is worse than (1) or (2), because of the possibility for inconsistency, as demonstrated by current
maint
branch. -
Somehow have a "single source of truth" for environment variables that also allows setting defaults... I can think of two ways:
-
4A. Build a CLI... call it
ocw
. It would be like./manage.py
in django. We'd use it like:yarn ocw --help yarn ocw test yarn ocw test-e2e yarn ocw build course <-- build a course; we'd use this in pipelines yarn ocw build course --help <-- get help info for all the flags/args to build yarn ocw serve course <-- serve a course in dev mode
where the
ocw
CLI would do something like load environment variables, set default values for some, provide useful error messages, have docstrings...likemanage.py
does.This is appealing to me for a variety of reasons. Setting env variables is one. Another is it's a way to provide documentation without cluttering the README.
-
4B. Another option is to have
.env
and.env.defaults
and# replace this yarn dotenv <cmd> # with this: yarn dotenv -e .env -e .env.defaults <cmd>
I don't dislike this idea. But then do we have three files? (env, env.defaults, and env.sample)? Or do we just use env.sample in place of env.defaults? I'm not sure all the values in .env.sample are reasonable as defaults.
-
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oops, submitted that too earlier...
In summary: 4A is my longer-term preference, but the approach of this PR is (2) which seems ok. Maybe 4B is good.
What do you think? (of these options, or of some other idea)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I actually think 3 is the best option specifically referring to the port. There aren't that many places where it's used to generate inconsistency, and I have never heard of software that spins up a service that binds to a port requiring you to specify said port without a default.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There aren't that many places where it's used to generate inconsistency,
But even with not many, we already have the inconsistency on main
. Run PORT=3456 yarn start:course
and observe that OCW does not load webpack assets.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, I'm going to try (3)... if I can set all the js defaults in one place, that's good enough. If the env var is needed in Hugo, too, we'll have to set the default in two places. If we can keep it to two places, I think that's ok.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
However it's accomplished, Node and Hugo don't require you to set a port when you run them and will run on a default port. We shouldn't require that it be set either, but allow it to be overridden. I'd rather hard code 3000 for Hugo and 3001 for Webpack than require the env variable to be set to spin up the server.
"start:www": "concurrently \"npm:start:webpack\" \"npm:start:www:hugo\" --kill-others", | ||
"start:course": "concurrently \"npm:start:webpack\" \"npm:start:course:hugo\" --kill-others", | ||
"start:fields": "concurrently \"npm:start:webpack\" \"npm:start:fields:hugo\" --kill-others", | ||
"start:webpack": "dotenv -- webpack serve --config ./base-theme/assets/webpack/webpack.dev.ts --hot --host 0.0.0.0 --stats-children", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like an idiot now because I didn't realize this package was required for NPM to pass env variables to package scripts. I thought all you needed install was the dotenv
package and that would happen, but apparently I was mistaken.
@@ -3,31 +3,21 @@ | |||
set -euo pipefail | |||
|
|||
source package_scripts/common.sh | |||
load_env --require-dot-env |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea, I think we added this a while back because we were having trouble with dotenv
. Like I mentioned above, I didn't realize that the dotenv-cli
package was required and that you had to prefix any commands that needed the .env
file with it. They don't mention it anywhere on the dotenv
Github and it's kind of puzzling why the "CLI" component of that is a separate package. Either way, it works much better so yea it makes sense to get rid of this.
package.json
Outdated
"start:webpack": "dotenv -- webpack serve --config ./base-theme/assets/webpack/webpack.dev.ts --hot --host 0.0.0.0 --stats-children", | ||
"start:webpack": "dotenv -- webpack serve --config ./base-theme/assets/webpack/webpack.dev.ts --hot --stats-children", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For posterity: @pt2302 and @ChristopherChudzicki on M1's reproducibly had an issue where:
- all of ocw works fine on both node 16 and node 18, with one exception...
yarn test:e2e
, which runsyarn start:webpack
, did not work on node 18.
Removing --host 0.0.0.0
here fixes the issue for me.
I am not sure why we had --host 0.0.0.0
here in the first place. Based on https://stackoverflow.com/questions/35412137/how-to-get-access-to-webpack-dev-server-from-devices-in-local-network, I think this was probably set to enable testing OCW on phones.
But that doesn't seem to be working for me. Both my computer and my phone are on the same wifi network, and my phone cannot access http://localhost:3001/static/css/main.css.
So I think we should just remove this.
@gumaerc Do you know if there is any good reason to leave --host 0.0.0.0
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Noting that with this change, it runs for me on Apple Silicon using Node 18.14. @ChristopherChudzicki suggested that this might be due to a security setting on M1.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ChristopherChudzicki Hitting http://localhost:3001
on your phone is going to try and access port 3001 on your phone, but your phone isn't running a web server. Binding to 0.0.0.0
means that you can hit the local IP address of your dev machine from any other device on your network. Say your dev machine has a local IP of 192.168.1.100
. That means that if you run this with --host 0.0.0.0
then on your phone you can go to http://192.168.1.100:3001
and you will get your webpack instance.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@gumaerc Awesome, thank you for that explanation! Sounds like we do want 0.0.0.0. I've reverted the "replace 0.0.0.0 with localhost" for now.
dbc8a01
to
ed29972
Compare
ed29972
to
c9cc0ee
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@gumaerc I believe that the most recent commit (c9cc0ee) has all changes since your last review. Aside from a rebase with no conflicts.
I went ahead and set a default port in a 4 places. I'm not super happy with this and I plan to make a small folllowup PR that reorganizes a few env variable things, but I'd rather not add even more env-reorganization to this PR.
Thanks again for your explanations about 0.0.0.0 (I reverted the 0.0.0.0 --> localhost change for now; will also address that in my incoming PR).
Anyway, try: start:www
, start:course
, and test:e2e
without a WEBPACK_PORT explicitly defined. Should work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 LGTM
Pre-Flight checklist
What are the relevant tickets?
#919
What's this PR do?
This PR adds e2e testing via Playwright to OCW Hugo Themes.
There are a variety of other significant changes that I will try to call out in individual comments.
How should this be manually tested?
First: Run
yarn install
and read the readme: https://github.com/mitodl/ocw-hugo-themes/tree/cc/ci2/tests-e2e If you use VS Code, I highly recommend installing the Playwright extension. (See the Readme.).yarn test:e2e
. It should fail. Add the required environment variable. (Use value 3001; see set up e2e tests with playwright #941 (comment) ).yarn test:e2e
again. It should suceed but it will also be kinda slow.yarn start:webpack
. In a separate shell, runyarn test:e2e
. It should be pretty fast. (Now Playwright is reusing the webpack server.).only
on one of the tests, and runyarn test:e2e --debug
. You can step through the test.yarn start:course
andyarn start:www
, too. I changed some stuff with environment variables and scripts. Those should both still work. You could also tryOCW_TEST_COURSE=different-from-value-in-env-file yarn start:course
. That should work now, whereas previously it would not.Where should the reviewer start?
I suggest starting with the readme, then reading some of the test files. The test files should more or less make sense without context.
Then look at all the other junk.