Skip to content

Commit c1de826

Browse files
committed
sauce-enabled testing and a bunch of notes before I forget all this
1 parent 4d8fa5f commit c1de826

File tree

4 files changed

+198
-39
lines changed

4 files changed

+198
-39
lines changed

.env.test.example

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
GOOGLE_CLIENT_ID=<your-id-here>
2+
GOOGLE_CLIENT_SECRET=<your-secret-here>
3+
BASE_URL=http://localhost:5000
4+
PORT=5000
5+
SESSION_SECRET=not-so-secret
6+
7+
DATABASE_URL="postgres://pyret:pyret@localhost/pyret_test"
8+
DATABASE_TEST_USER="pyret"
9+
DATABASE_TEST_NAME="pyret_test"
10+
DATABASE_TEST_PREFIX="postgres://pyret:pyret@localhost/"
11+
12+
SELENIUM_GOOGLE_USER="<some-gmail-username>"
13+
SELENIUM_GOOGLE_PASSWORD="<some-gmail-password>"
14+
15+
SAUCE_USERNAME="<your-saucy-name>"
16+
SAUCE_ACCESS_KEY="<your-saucy-access-key>"
17+
SAUCE_TEST_TARGET="http://patch-experiment.herokuapp.com"
18+

Procfile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@ web: node src/run.js
22

33
test: node node_modules/jasmine-node/lib/jasmine-node/cli.js --matchall test/db
44

5-
selenium-test: node node_modules/jasmine-node/lib/jasmine-node/cli.js --matchall test/browser/
5+
selenium-test-local: TEST_LOC="local" node node_modules/jasmine-node/lib/jasmine-node/cli.js --matchall test/browser/
6+
selenium-test-sauce: TEST_LOC="sauce" node node_modules/jasmine-node/lib/jasmine-node/cli.js --matchall test/browser/
67

78
migrate: node node_modules/db-migrate/bin/db-migrate up
89

910
sqlgen: node node_modules/sql-generate/bin/node-sql-generate --dsn $DATABASE_URL > src/schema.js
11+
12+

README.md

Lines changed: 82 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
An IDE for Pyret. Use
1+
2+
# code.pyret.org
3+
4+
An IDE and more for Pyret. Use
25

36
$ git submodule init
47
$ git submodule update
@@ -21,10 +24,87 @@ now be able to run `foreman start`, and the app should be running at
2124
`http://localhost:5000`.
2225

2326

24-
TODO:
27+
# Building a database
28+
29+
To keep the development system close to what happens in production, you must
30+
install Postgres to work with the development system. You should create a
31+
user and a database (with names of your choice), and put an entry for them
32+
into your `.env` that looks like:
33+
34+
postgres://<username>:<password>@<your-postgres-host>/<dbname>
35+
36+
Then, to set up the database, you can run:
37+
38+
$ foreman run migrate
39+
$ foreman run sqlgen
40+
41+
And it will build an empty database for doing development.
42+
43+
# Testing
44+
45+
46+
47+
## Testing Databases
48+
49+
Some of our testing requires creating databases, so you need a user with that
50+
permission to run all the tests:
51+
52+
$ psql
53+
> ALTER USER <your-user> GRANT CREATEDB;
54+
55+
You should make a separate environment for testing, which can use the same
56+
client secret from Google, but should be a separate databse from development.
57+
You also need a few extra entries in the test environment for the database,
58+
which are used to create fresh databases; see `.env.test.example`.
59+
60+
To migrate the test database, use:
61+
62+
$ foreman run -e .env.test migrate
63+
64+
This will put the testing database in the right shape.
65+
66+
To run the basic server tests, use:
67+
68+
$ foreman run -e .env.test test
69+
70+
71+
## Selenium
72+
73+
We use Selenium to test flows of authentication and initial signup. If you
74+
want to run tests on your own, you'll need a Google account that you don't
75+
mind potentially getting locked out (for too many weird sign-in attempts) or
76+
potentially lose data. It also can't have 2-factor auth enabled, so you
77+
probably don't want to use your main account for this. You can email Joe if
78+
you want, and he might be able to help.
79+
80+
To run Selenium tests, add `SELENIUM_GOOGLE_USER` and
81+
`SELENIUM_GOOGLE_PASSWORD` to your `.env.test`, and run:
82+
83+
$ foreman -e .env.test selenium-test-local
84+
85+
## Sauce
86+
87+
We also support running tests remotely at Sauce labs. Email joe for the
88+
account information if you need to set this up. Since we've only configured
89+
ourselves to have Sauce point at a public server (no reverse tunnelling back
90+
to our localhost), you need to put a `SAUCE_TEST_TARGET` that can be seen by
91+
the world (for example, `http://patch-experiment.herokuapp.com` will host the
92+
test site). You also need to define `SAUCE_USERNAME` and `SAUCE_ACCESS_KEY`.
93+
The command to run on Sauce is:
94+
95+
$ foreman -e .env.test selenium-test-sauce
96+
97+
Note that `selenium-test-sauce` and `selenium-test-local` run the same set of
98+
tests, so you can do Selenium test development locally, and they will be run
99+
when the live test site is pushed (this is a good thing!).
100+
101+
102+
103+
## TODO
25104

26105
1. Linking to a development copy of Pyret for quicker turnaround on the
27106
language interface.
28107
2. Getting access to Heroku deployment (set the same env vars with `heroku
29108
config:set GOOGLE_CLIENT_ID`, etc, and use `git push heroku master` after
30109
doing the right git config, for people other than Joe.
110+

test/browser/signin.js

Lines changed: 94 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -2,49 +2,106 @@ _ = require("jasmine-node");
22
var server = require("./../../src/server.js");
33
var webdriver = require('selenium-webdriver');
44

5-
server.start({
6-
baseUrl: process.env["BASE_URL"],
7-
port: process.env["PORT"],
8-
sessionSecret: process.env["SESSION_SECRET"],
9-
google: {
10-
clientId: process.env["GOOGLE_CLIENT_ID"],
11-
clientSecret: process.env["GOOGLE_CLIENT_SECRET"],
12-
redirect: "/oauth2callback"
13-
}
14-
}, afterServer);
5+
if (process.env["TEST_LOC"] === "local") {
6+
server.start({
7+
baseUrl: process.env["BASE_URL"],
8+
port: process.env["PORT"],
9+
sessionSecret: process.env["SESSION_SECRET"],
10+
google: {
11+
clientId: process.env["GOOGLE_CLIENT_ID"],
12+
clientSecret: process.env["GOOGLE_CLIENT_SECRET"],
13+
redirect: "/oauth2callback"
14+
}
15+
}, function(app, server) {
16+
var driver = new webdriver.Builder().
17+
withCapabilities({browserName: "chrome"}).
18+
build();
19+
afterServer(server, process.env["BASE_URL"], driver);
20+
});
21+
}
22+
else {
23+
var driver = new webdriver.Builder().
24+
usingServer("https://ondemand.saucelabs.com/wd/hub").
25+
withCapabilities({
26+
browserName: "internet explorer",
27+
username: process.env["SAUCE_USERNAME"],
28+
accessKey: process.env["SAUCE_ACCESS_KEY"]
29+
}).
30+
build();
31+
afterServer(null, process.env["SAUCE_TEST_TARGET"], driver);
32+
}
33+
1534

1635
var googleUsername = process.env["SELENIUM_GOOGLE_USER"];
1736
var googlePassword = process.env["SELENIUM_GOOGLE_PASSWORD"];
1837

19-
function afterServer(app, server) {
38+
function afterServer(maybeServer, baseUrl, driver) {
39+
function googleLogin(driver) {
40+
driver.wait(function() {
41+
return driver.getTitle().then(function(title) {
42+
return title === 'Sign in - Google Accounts';
43+
});
44+
}, 3000);
45+
// Sometimes email isn't present because the browser remembers which
46+
// Google account we last logged in as
47+
driver.findElement(webdriver.By.id("Email")).getAttribute("class").then(function(cls) {
48+
if(cls.indexOf("hidden") === -1) {
49+
driver.findElement(webdriver.By.id("Email")).sendKeys(googleUsername);
50+
}
51+
driver.findElement(webdriver.By.id("Passwd")).sendKeys(googlePassword);
52+
driver.findElement(webdriver.By.id("signIn")).click();
53+
});
54+
}
55+
function waitThenClick(driver, query) {
56+
driver.wait(function() {
57+
return driver.isElementPresent(query);
58+
}, 4000);
59+
return driver.findElement(query).click();
60+
}
2061
describe("Sign in", function() {
21-
var /*driver;
22-
beforeEach(function() {
23-
console.log("running test");*/
24-
driver = new webdriver.Builder().
25-
withCapabilities(webdriver.Capabilities.chrome()).
26-
build();
27-
// });
62+
webdriver.promise.controlFlow().on('uncaughtException', function(e) {
63+
console.error('Unhandled error: ' + e);
64+
fail();
65+
});
66+
67+
it("Should forget everything it knows", function(done) {
68+
try {
69+
driver.get("https://security.google.com/settings/security/permissions");
70+
googleLogin(driver);
71+
console.log("waiting...");
72+
driver.wait(function() {
73+
console.log("Waiting for page...");
74+
return driver.executeScript("return document.readyState === 'complete'");
75+
}, 3000);
76+
// driver.isElementPresent(webdriver.By.xpath("//*[contains(text(), 'Patch Test')]")).then(function(p) { console.log(p); });
77+
driver.isElementPresent(webdriver.By.xpath("//*[contains(text(), 'Patch Test')]")).then(function(present) {
78+
console.log("present: ", present);
79+
if(present) {
80+
waitThenClick(driver, webdriver.By.xpath("//*[contains(text(), 'Patch Test')]"));
81+
waitThenClick(driver, webdriver.By.xpath("//*[contains(text(), 'Revoke access')]"));
82+
return waitThenClick(driver, webdriver.By.name("ok"));
83+
} else {
84+
// do nothing
85+
}
86+
});
87+
driver.get("https://accounts.google.com/Logout");
88+
driver.call(function() { done(); });
89+
}
90+
catch(e) { console.log(e); }
91+
}, 60000);
2892

29-
it("Should sign up", function(done) {
30-
console.log("foo");
31-
driver.get('http://localhost:5000');
93+
it("Should sign up from not being logged in", function(done) {
94+
console.log("sign up");
95+
driver.get(baseUrl);
3296
driver.findElement(webdriver.By.id('login')).click();
33-
driver.wait(function() {
34-
return driver.getTitle().then(function(title) {
35-
return title === 'Sign in - Google Accounts';
36-
});
37-
}, 1000);
38-
driver.findElement(webdriver.By.id("Email")).sendKeys(googleUsername);
39-
driver.findElement(webdriver.By.id("Passwd")).sendKeys(googlePassword);
40-
driver.findElement(webdriver.By.id("signIn")).click();
97+
googleLogin(driver);
4198
driver.wait(function() {
4299
console.log("Waiting for confirmation button to activate...");
43100
return driver.findElement(webdriver.By.id("submit_approve_access")).getAttribute("disabled")
44101
.then(function(disabled) {
45102
return !disabled;
46103
});
47-
});
104+
}, 3000);
48105
driver.findElement(webdriver.By.id("submit_approve_access")).click();
49106
var loaded = driver.wait(function() {
50107
console.log("Waiting for page to load..");
@@ -53,13 +110,14 @@ function afterServer(app, server) {
53110
console.log("style: ", d);
54111
return d === "none";
55112
});
56-
});
57-
loaded.then(function() { done(); });
58-
driver.quit();
59-
}, 15000);
113+
}, 3000);
114+
loaded.then(function() { console.log("done with test"); done(); });
115+
}, 60000);
60116

61-
it("Should close the server", function(done) {
62-
server.close();
117+
it("Should close the server and the connection to the browser", function(done) {
118+
console.log("closing down server");
119+
if(maybeServer) { maybeServer.close(); }
120+
driver.quit();
63121
done();
64122
});
65123

0 commit comments

Comments
 (0)