add selenium doc and prepare script

We have no documentation in Confluence or this repo for running
Selenium tests natively. This commit adds a doc for that.

We're also migrating the "prepare" script from the qa_tools repo
to this one.

Test Plan:
  - follow the instructions in doc/testing_with_selenium.md to
  verify it's accurate
  - run script/canvas_update to verify it still works
  - follow the setup instructions in script/prepare/README.md and
  verify it works

Change-Id: I11d63e60dc1faa8be1dfacfc9bebfcbea55c31f1
Reviewed-on: https://gerrit.instructure.com/167300
Tested-by: Jenkins
Reviewed-by: David Tan <dtan@instructure.com>
QA-Review: David Tan <dtan@instructure.com>
Product-Review: Michael Hargiss <mhargiss@instructure.com>
This commit is contained in:
Michael Hargiss 2018-10-05 16:28:36 -04:00
parent f119cfd489
commit 3cfbdf31ac
5 changed files with 449 additions and 103 deletions

View File

@ -0,0 +1,90 @@
# Testing with Selenium
You may run the Selenium tests either natively or in docker.
## Running Selenium Tests Natively (Mac)
We're making a few assumptions here:
- you're using an Apple computer
- you've already installed Homebrew
- you've already installed Postgres (postgresapp.com is an excellent option),
and Postgres is running on your computer
- you've already installed Node.js
0. Install `yarn` if you haven't already:
```sh
brew install yarn
```
If you find you need an older version of yarn, follow these instructions to
install the older version and switch to it:
[https://stackoverflow.com/a/52525732/3038677](https://stackoverflow.com/a/52525732/3038677)
1. Follow the instructions in `script/prepare/README.md` to setup the `prepare`
script.
You'll use the `prepare` script later to automate installing and updating Canvas
on your computer.
Note: some features of `prepare` only work if you have access to Instructure's
Gerrit host. See the README for details.
2. Install a web browser driver on your computer for the browser you wish to run
the tests in. Homebrew is the easiest way to go:
```sh
brew install chromedriver # necessary for running tests in Chrome
brew install geckodriver # necessary for running tests in Firefox
```
Now let's get Canvas ready to run the tests.
3. Copy the Selenium and database configuration files:
```sh
cp config/selenium.yml.example config/selenium.yml
cp config/database.yml.example config/database.yml
```
4. Use `prepare` to install Canvas plugins and dependencies, create databases,
run database migrations, etc:
```sh
prepare
```
You might encounter problems with some Ruby dependencies. The ["Dependency
Installation" section](https://github.com/instructure/canvas-lms/wiki/Quick-Start#dependency-installation)
in the public Canvas LMS Github wiki has some useful tips.
4.a. Optional. Run delayed jobs in the foreground (not all Selenium tests need
this but some do):
```sh
script/delayed_job run
```
or run it in the background:
```sh
script/delayed_job run &
```
5. Run the Selenium tests:
```sh
bundle exec rspec spec/selenium
```
or run a specific Selenium test:
```sh
bundle exec rspec spec/selenium/accounts_spec.rb:36
```
## Running Selenium Tests in Docker
See the [Selenium section](https://github.com/instructure/canvas-lms/blob/master/doc/docker/developing_with_docker.md#selenium)
of the `doc/docker/developing_with_docker.md` instructions.

View File

@ -1,5 +1,9 @@
#!/bin/bash
source script/common.sh
LOG="$(pwd)/log/canvas_update.log"
# TODO: improve usage / help
function usage {
echo "usage: canvas_update [-qhv] [-n phase]"
@ -10,104 +14,6 @@ function bad_usage {
exit 1
}
function echo_console_and_log {
echo "$1"
echo "$1" >>"$LOG"
}
function is_git_dir {
git rev-parse --is-inside-git-dir >/dev/null 2>&1 && [ -d .git ]
return $?
}
function is_canvas_root {
CANVAS_IN_README=$(head -1 README.md 2>/dev/null | grep 'Canvas LMS')
[[ "$CANVAS_IN_README" != "" ]] && is_git_dir
return $?
}
function ensure_in_canvas_root_directory {
if ! is_canvas_root; then
echo "Please run from a Canvas root directory"
exit 0
fi
}
function intro_message {
echo "Bringing Canvas up to date ..."
echo " Log file is $LOG"
echo >>"$LOG"
echo "-----------------------------" >>"$LOG"
echo "Canvas Update ($(date)):" >>"$LOG"
echo "-----------------------------" >>"$LOG"
}
function update_plugin {
(
cd "$1"
if is_git_dir; then
echo_console_and_log " Updating plugin $1 ..."
( git checkout master >>"$LOG" 2>&1 && \
git pull --rebase >>"$LOG" 2>&1 ) || \
( echo " failed to pull plugin (see $LOG)"; kill -INT $$ )
fi
)
}
function update_plugins {
# Loop through each plugin dir, and if it's a git repo, update it
# This needs to be done first so that db:migrate can pull in any plugin-
# precipitated changes to the database.
for dir in {gems,vendor}; do
if [ -d "$dir/plugins" ]; then
for plugin in $dir/plugins/*; do update_plugin "$plugin"; done
fi
done
}
function rebase_canvas {
echo_console_and_log " Pulling Canvas code ..."
( git checkout master >>"$LOG" 2>&1 && git pull --rebase >>"$LOG" 2>&1 ) || \
( echo " failed to pull Canvas (see $LOG)"; kill -INT $$ )
}
function bundle_install_with_check {
echo_console_and_log " Checking your gems (bundle check) ..."
if bundle check >>"$LOG" 2>&1 ; then
echo_console_and_log " Gems are up to date, no need to bundle install ..."
else
bundle_install
fi
}
function bundle_install {
echo_console_and_log " Installing gems (bundle install) ..."
rm Gemfile.lock* >/dev/null 2>&1
bundle install >>"$LOG" 2>&1 || \
( echo " failed to bundle install (see $LOG)"; kill -INT $$ )
}
function rake_db_migrate_dev_and_test {
echo_console_and_log " Migrating DB ..."
RAILS_ENV=development bundle exec rake db:migrate >>"$LOG" 2>&1 || \
( echo " failed to migrate db (development) (see $LOG)"; kill -INT $$ )
RAILS_ENV=test bundle exec rake db:migrate >>"$LOG" 2>&1 || \
( echo " failed to migrate db (test) (see $LOG)"; kill -INT $$ )
}
function yarn_install {
echo_console_and_log " Installing node packages ..."
yarn install >>"$LOG" 2>&1 || \
( echo " failed to install node packages (see $LOG)"; kill -INT $$ )
}
function compile_assets {
echo_console_and_log " compiling assets ..."
bundle exec rake 'canvas:compile_assets_dev' >>"$LOG" 2>&1 || \
( echo " failed to generate JS (see $LOG)"; kill -INT $$ )
}
# TODO: moar tips plz
function tips {
echo "Tips:"
@ -117,16 +23,17 @@ function tips {
function update_canvas {
ensure_in_canvas_root_directory
intro_message
intro_message "Canvas Update"
if [[ -z "$SKIP_CODE" ]] ; then
update_plugins
checkout_master_canvas
rebase_canvas
fi
if [[ -z "$SKIP_DEPS" ]] ; then
bundle_install_with_check
yarn_install
install_node_packages
fi
if [[ -z "$SKIP_DATA" ]] ; then
@ -144,8 +51,6 @@ function update_canvas {
fi
}
LOG="$(pwd)/log/canvas_update.log"
# default options
PERFORM_UPDATE=true
QUICK_MODE=false
@ -184,6 +89,7 @@ do
esac
done
if $PERFORM_UPDATE; then
if [ "$PERFORM_UPDATE" == "true" ]; then
trap print_results INT TERM EXIT
update_canvas
fi

121
script/common.sh Normal file
View File

@ -0,0 +1,121 @@
#!/bin/bash
# This file contains commonly used BASH functions for scripting in canvas-lms,
# particularly script/canvas_update and script/prepare/prepare . As such,
# *be careful* when you modify these functions as doing so will impact multiple
# scripts that likely aren't used or tested in continuous integration builds.
function echo_console_and_log {
echo "$1"
echo "$1" >>"$LOG"
}
function print_results {
exit_code=$?
set +e
if [ "${exit_code}" == "0" ]; then
echo ""
echo_console_and_log " \o/ Success!"
else
echo ""
echo_console_and_log " /o\ Something went wrong. Check ${LOG} for details."
fi
exit ${exit_code}
}
function ensure_in_canvas_root_directory {
if ! is_canvas_root; then
echo "Please run from a Canvas root directory"
exit 0
fi
}
function is_canvas_root {
CANVAS_IN_README=$(head -1 README.md 2>/dev/null | grep 'Canvas LMS')
[[ "$CANVAS_IN_README" != "" ]] && is_git_dir
return $?
}
function is_git_dir {
git rev-parse --is-inside-git-dir >/dev/null 2>&1 && [ -d .git ]
return $?
}
# Parameter: the name of the script calling this function
function intro_message {
script_name="$1"
echo "Bringing Canvas up to date ..."
echo " Log file is $LOG"
echo >>"$LOG"
echo "-----------------------------" >>"$LOG"
echo "$1 ($(date)):" >>"$LOG"
echo "-----------------------------" >>"$LOG"
}
function update_plugin {
(
cd "$1"
if is_git_dir; then
echo_console_and_log " Updating plugin $1 ..."
git checkout master >>"$LOG" 2>&1
git pull --rebase >>"$LOG" 2>&1
fi
)
}
function update_plugins {
# Loop through each plugin dir, and if it's a git repo, update it
# This needs to be done first so that db:migrate can pull in any plugin-
# precipitated changes to the database.
for dir in {gems,vendor}; do
if [ -d "$dir/plugins" ]; then
for plugin in $dir/plugins/*; do update_plugin "$plugin"; done
fi
done
}
function checkout_master_canvas {
echo_console_and_log " Checking out canvas-lms master ..."
git checkout master >>"$LOG" 2>&1
}
function rebase_canvas {
echo_console_and_log " Rebasing canvas-lms on HEAD ..."
git pull --rebase >>"$LOG" 2>&1
}
function bundle_install {
echo_console_and_log " Installing gems (bundle install) ..."
rm Gemfile.lock* >/dev/null 2>&1
bundle install >>"$LOG" 2>&1
}
function bundle_install_with_check {
echo_console_and_log " Checking your gems (bundle check) ..."
if bundle check >>"$LOG" 2>&1 ; then
echo_console_and_log " Gems are up to date, no need to bundle install ..."
else
bundle_install
fi
}
function rake_db_migrate_dev_and_test {
echo_console_and_log " Migrating development DB ..."
RAILS_ENV=development bundle exec rake db:migrate >>"$LOG" 2>&1
echo_console_and_log " Migrating test DB ..."
RAILS_ENV=test bundle exec rake db:migrate >>"$LOG" 2>&1
}
function install_node_packages {
echo_console_and_log " Installing Node packages ..."
bundle exec rake js:yarn_install >>"$LOG" 2>&1
}
function compile_assets {
echo_console_and_log " Compiling assets (css and js only, no docs or styleguide) ..."
bundle exec rake canvas:compile_assets_dev >>"$LOG" 2>&1
}

105
script/prepare/README.md Normal file
View File

@ -0,0 +1,105 @@
# prepare
There are numerous tasks to execute after checking out a Canvas patchset...
compile assets, migrate database changes, update plugins, etc. Forgetting to do
any one of these may bork your testing, possibly even produce false positives!
Sometimes all you need is the latest master branch of canvas-lms, but you can't
quite recall all the database migration and asset compilation tasks.
`prepare` takes care of all that for you.
## What does it do exactly?
`prepare` will:
1. Make sure you're in a canvas root directory
2. Pull the code changes (either from master or from the patchset you've specified)
3. Update Canvas plugins
4. Check if your Canvas gem dependencies need updating
* If they do, it will install them for you
5. Migrate any database changes to your development db
6. Migrate any database changes to your test db
7. Install Canvas javascript dependencies if they aren't already installed
8. Compile CSS and Javascript
9. Make you happy :smiley:
It can also nuke your Javascript dependencies, force re-install them, and more.
See the full feature list below.
## Setup
Add a symlink to `prepare` in your /usr/local/bin/ directory, like so:
```bash
$ cd canvas-lms/script/prepare/
$ ln -s $(pwd)/prepare /usr/local/bin/prepare
```
You're all set!
## How do I use it?
This works just like Portals. To checkout a specific patchset, copy its unique
ID from gerrit. Then execute:
```bash
$ prepare <commit_id>
```
For example:
```bash
$ prepare 89/12345/67
```
Or, if you just want to checkout master, do this:
```bash
$ git checkout master
$ prepare
```
## What else can it do for me?
Let's say you've branched off master and committed code changes. Now you want to
test your changes against the latest Canvas code base on master before you push
your code for review.
You can easily `git pull --rebase` (provided you've already done
`git branch --set-upstream-to=origin/master`), but what if there have been
database, gem, or css changes since you branched?
You can update all those manually. Or you can let `prepare` do it for you.
Simply:
```bash
$ prepare
```
It will `git pull --rebase`, then update your database, compile css, etc.
(Again, see the "What does it do exactly?" section above for details.)
NOTE: `prepare` won't execute `git branch --set-upstream-to=origin/master` for
you. It assumes you've already set your branch's upstream to the desired remote
branch.
## Features
Current and planned:
- [x] Keeps a log of processes and any errors
- [x] Updates your Canvas codebase, including plugins, gems, and node packages
- [x] Performs database migrations on both development and test databases
- [x] Compiles assets (css and javascript)
- [x] Starts or restarts `powder` if you have it installed and linked to Canvas
- [x] Supports `git checkout`
- [x] Supports updating your current branch off master
- [x] Supports nuking your Canvas node_modules, i.e. `rm -rf node_modules && npm install`
- [x] Supports a fresh installation of Javascript dependencies (useful for new Canvas setups)
- [ ] Supports `git cherry-pick`
- [ ] Supports a "quick" option, i.e. skipping asset compilation entirely
## Code Credit
Much of this code is borrowed from [one of Canvas' update scripts](https://github.com/instructure/canvas-lms/blob/stable/script/canvas_update).
I simply added a couple features and repurposed it for Canvas QA folks who are
likely accustomed to the Portals checkout flow.

124
script/prepare/prepare Executable file
View File

@ -0,0 +1,124 @@
#!/bin/bash
source script/common.sh
# Set this manually if your computer's username doesn't match your gerrit username
gerrit_username=$USER
LOG="$(pwd)/log/prepare.log"
function usage() {
echo "usage: prepare [ [-hin] [-p PATCHSET_ID] ]
prepare prepares Canvas to run natively on your computer.
Running prepare without any options will rebase on the current HEAD,
update plugins, install or update Ruby and Javascript dependencies,
create both dev and test databases if necessary, run database migrations,
and finally compile CSS and Javascript. If you use powder then it will also
restart powder for you.
The optional -i flag will install all javascript dependencies.
The optional -n flag will nuke all javascript dependencies, then install
them.
The optional -p PATCHSET_ID will checkout the Gerrit patchset instead of
rebasing on HEAD (the default prepare behavior)."
}
function pull_patchset_from_gerrit() {
echo_console_and_log " Pulling Canvas patchset $1 ..."
git fetch ssh://$gerrit_username@gerrit.instructure.com:29418/canvas-lms refs/changes/$1 >>"$LOG" 2>&1 && git checkout FETCH_HEAD >>"$LOG" 2>&1
}
function db_create_dev_and_test() {
echo_console_and_log " Creating development DB if it doesn't already exist ..."
createdb canvas_development >>"$LOG" 2>&1
echo_console_and_log " Creating test DB if it doesn't already exist ..."
createdb canvas_test >>"$LOG" 2>&1
}
function nuke_node_modules() {
echo_console_and_log " Removing node_modules and gems/**/node_modules ..."
rm -rf node_modules && rm -rf gems/**/node_modules >>"$LOG" 2>&1
}
function restart_powder() {
echo " Checking if powder is being used in this repo ..."
if [ -f .powder ] ; then
echo " Checking if pow is running ..."
if ps aux | grep pow | grep -v grep > /dev/null ; then
echo_console_and_log " Restarting powder ..."
powder restart >>"$LOG" 2>&1
fi
else
echo " ... nope."
fi
}
function prepare() {
ensure_in_canvas_root_directory
intro_message "Prepare"
if [ "$1" == "" ]; then
rebase_canvas
else
pull_patchset_from_gerrit "$1"
fi
update_plugins
bundle_install_with_check
db_create_dev_and_test
rake_db_migrate_dev_and_test
if [ "$NUKE_NODE_MODULES" == "true" ]; then
nuke_node_modules
fi
if [ "$INSTALL_NODE_PACKAGES" == "true" ] || [ ! -d node_modules ]; then
install_node_packages
fi
compile_assets
restart_powder
}
# default options
PERFORM_UPDATE=true
INSTALL_NODE_PACKAGES=false
NUKE_NODE_MODULES=false
# parse options
# http://www.tldp.org/LDP/abs/html/internal.html#EX33
while getopts ":hin" Option
do
case $Option in
h )
PERFORM_UPDATE=false
usage;;
i )
PERFORM_UPDATE=true
INSTALL_NODE_PACKAGES=true;;
n )
PERFORM_UPDATE=true
INSTALL_NODE_PACKAGES=true
NUKE_NODE_MODULES=true;;
* )
PERFORM_UPDATE=false
echo "Sorry, that's not a valid option!"
usage;;
esac
done
if [ "$PERFORM_UPDATE" == "true" ]; then
trap print_results INT TERM EXIT
patchset_id="$1"
if [ "$patchset_id" == "-i" ] || [ "$patchset_id" == "-n" ]; then
patchset_id=""
fi
prepare "$patchset_id"
fi