From 2fe8dbb490f2399489ad70b0b392fc227b033c9c Mon Sep 17 00:00:00 2001 From: cwillaim Date: Thu, 8 Dec 2016 17:45:17 -0500 Subject: [PATCH 01/32] Add tests for the educator registration flow, up to the phone number step. --- ...est_educators_registration_demographics.js | 78 ++++++++++++ .../test_educators_registration_name_step.js | 69 +++++++++++ .../test_educators_registration_phone_step.js | 83 +++++++++++++ ...st_educators_registration_username_step.js | 111 ++++++++++++++++++ 4 files changed, 341 insertions(+) create mode 100644 test/integration/test_educators_registration_demographics.js create mode 100644 test/integration/test_educators_registration_name_step.js create mode 100644 test/integration/test_educators_registration_phone_step.js create mode 100644 test/integration/test_educators_registration_username_step.js diff --git a/test/integration/test_educators_registration_demographics.js b/test/integration/test_educators_registration_demographics.js new file mode 100644 index 000000000..6f3b56f8b --- /dev/null +++ b/test/integration/test_educators_registration_demographics.js @@ -0,0 +1,78 @@ +/* + * Checks the behavior of demographics step in the educators registration process + * + * Test cases: https://github.com/LLK/scratch-www/wiki/Testing-Scratch-www#All_Test_Cases_Teacher_Join_Flow + */ +require('chromedriver'); +var seleniumWebdriver = require('selenium-webdriver'); +var tap = require('tap'); + +//chrome driver +var driver = new seleniumWebdriver.Builder().withCapabilities(seleniumWebdriver.Capabilities.chrome()).build(); + +var fillUsernameSlide = function () { + var passwordInput = driver.findElement(seleniumWebdriver.By.name('user.password')); + var usernameInput = driver.findElement(seleniumWebdriver.By.name('user.username')); + var usernamePromise = usernameInput.sendKeys('ladybug'); + var passwordPromise = passwordInput.sendKeys('educators'); + var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath('//button[span[contains(text(),' + + '"Next Step")]]')); + return Promise.all([usernamePromise, passwordPromise]).then(function () { + nextStepButton.click().then(function () { + driver.wait(seleniumWebdriver.until + .elementLocated(seleniumWebdriver.By.className('demographics-step'))); + }); + }); +}; + +tap.plan(2); + +tap.tearDown(function () { + driver.quit(); +}); + +tap.beforeEach(function () { + driver.get('https://scratch.mit.edu/educators/register'); + return fillUsernameSlide(); +}); + +//if the user selects the other gender option, they must input a gender +//selects the other gender option and attempt to advance the slide +tap.test('checkOtherGenderInput', function (t) { + var otherGenderRadio = driver.findElement(seleniumWebdriver.By.xpath('//input[@value="other"' + + 'and @type="radio"]')); + var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath('//button[span[contains(text(),' + + '"Next Step")]]')); + var errorMessage = 'This field is required'; + var errorMessageXPath = '//span[@class="help-block validation-message" and contains(text(),"' + + errorMessage + '")]'; + driver.findElement(seleniumWebdriver.By.xpath('//select[@name="user.country"]/option[2]')).click(); + otherGenderRadio.click().then(function () { + nextStepButton.click().then(function () { + driver.findElements(seleniumWebdriver.By.xpath(errorMessageXPath)) + .then(function (validationMessages) { + t.equal(validationMessages.length, 1); + t.end(); + }); + }); + }); +}); + +//the user must select a gender +//tries to advance the slide without selecting a gender +tap.test('checkNoGenderInput', function (t) { + var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath('//button[span[contains(text(),' + + '"Next Step")]]')); + var errorMessage = 'This field is required'; + var errorMessageXPath = '//span[@class="help-block validation-message" and contains(text(),"' + + errorMessage + '")]'; + driver.findElement(seleniumWebdriver.By.xpath('//select[@name="user.country"]/option[2]')).click(); + nextStepButton.click().then(function () { + driver.findElements(seleniumWebdriver.By.xpath(errorMessageXPath)) + .then(function (validationMessages) { + t.equal(validationMessages.length, 1); + t.end(); + }); + }); +}); + diff --git a/test/integration/test_educators_registration_name_step.js b/test/integration/test_educators_registration_name_step.js new file mode 100644 index 000000000..35abb50ab --- /dev/null +++ b/test/integration/test_educators_registration_name_step.js @@ -0,0 +1,69 @@ +/* + * Checks the behavior of the name step in the educators registration process + * + * Test cases: https://github.com/LLK/scratch-www/wiki/Testing-Scratch-www#All_Test_Cases_Teacher_Join_Flow + */ +require('chromedriver'); +var seleniumWebdriver = require('selenium-webdriver'); +var tap = require('tap'); + +//chrome driver +var driver = new seleniumWebdriver.Builder().withCapabilities(seleniumWebdriver.Capabilities.chrome()).build(); + +var fillUsernameSlide = function () { + var passwordInput = driver.findElement(seleniumWebdriver.By.name('user.password')); + var usernameInput = driver.findElement(seleniumWebdriver.By.name('user.username')); + var usernamePromise = usernameInput.sendKeys('ladybug'); + var passwordPromise = passwordInput.sendKeys('educators'); + var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath('//button[span[contains(text(),' + + '"Next Step")]]')); + return Promise.all([usernamePromise, passwordPromise]).then(function () { + nextStepButton.click().then(function () { + driver.wait(seleniumWebdriver.until + .elementLocated(seleniumWebdriver.By.className('demographics-step'))); + }); + }); +}; + +var fillDemographicsSlide = function () { + var clickMaleInput = driver.findElement(seleniumWebdriver.By.xpath('//input[@value="male"' + + ' and @type="radio"]')).click(); + var selectCountry = driver.findElement(seleniumWebdriver.By.xpath('//select[@name="user.country"]' + + '/option[2]')).click(); + var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath('//button[span[contains(text(),' + + '"Next Step")]]')); + return Promise.all([clickMaleInput, selectCountry]).then(function () { + nextStepButton.click().then(function () { + driver.wait(seleniumWebdriver.until + .elementLocated(seleniumWebdriver.By.className('name-step'))); + }); + }); +}; + +tap.plan(1); + +tap.tearDown(function () { + driver.quit(); +}); + +tap.beforeEach(function () { + driver.get('https://scratch.mit.edu/educators/register'); + return fillUsernameSlide().then(fillDemographicsSlide); +}); + +//attempts to advance the slide without inputting either name, checks that both give the correct error +tap.test('checkBothNamesRequired', function (t) { + var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath('//button[span[contains(text(),' + + '"Next Step")]]')); + var errorMessage = 'This field is required'; + var errorMessageXPath = '//span[@class="help-block validation-message" and contains(text(),"' + + errorMessage + '")]'; + nextStepButton.click().then(function () { + driver.findElements(seleniumWebdriver.By.xpath(errorMessageXPath)) + .then(function (validationMessages) { + t.equal(validationMessages.length, 2); + t.end(); + }); + }); +}); + diff --git a/test/integration/test_educators_registration_phone_step.js b/test/integration/test_educators_registration_phone_step.js new file mode 100644 index 000000000..7a6a3ff2c --- /dev/null +++ b/test/integration/test_educators_registration_phone_step.js @@ -0,0 +1,83 @@ +/* + * Checks the behavior of the phone number step in the educators registration process + * + * Test cases: https://github.com/LLK/scratch-www/wiki/Testing-Scratch-www#All_Test_Cases_Teacher_Join_Flow + */ +require('chromedriver'); +var seleniumWebdriver = require('selenium-webdriver'); +var tap = require('tap'); + +//chrome driver +var driver = new seleniumWebdriver.Builder().withCapabilities(seleniumWebdriver.Capabilities.chrome()).build(); + +var fillUsernameSlide = function () { + var passwordInput = driver.findElement(seleniumWebdriver.By.name('user.password')); + var usernameInput = driver.findElement(seleniumWebdriver.By.name('user.username')); + var usernamePromise = usernameInput.sendKeys('ladybug'); + var passwordPromise = passwordInput.sendKeys('educators'); + var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath('//button[span[contains(text(),' + + '"Next Step")]]')); + return Promise.all([usernamePromise, passwordPromise]).then(function () { + nextStepButton.click().then(function () { + driver.wait(seleniumWebdriver.until + .elementLocated(seleniumWebdriver.By.className('demographics-step'))); + }); + }); +}; + +var fillDemographicsSlide = function () { + var clickMaleInput = driver.findElement(seleniumWebdriver.By.xpath('//input[@value="male"' + + 'and @type="radio"]')).click(); + var selectCountry = driver.findElement(seleniumWebdriver.By.xpath('//select[@name="user.country"]' + + '/option[2]')).click(); + var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath('//button[span[contains(text(),' + + '"Next Step")]]')); + return Promise.all([clickMaleInput, selectCountry]).then(function () { + nextStepButton.click().then(function () { + driver.wait(seleniumWebdriver.until + .elementLocated(seleniumWebdriver.By.className('name-step'))); + }); + }); +}; + +var fillNameSlide = function () { + var firstNamePromise = driver.findElement(seleniumWebdriver.By.name('user.name.first')).sendKeys('first'); + var lastNamePromise = driver.findElement(seleniumWebdriver.By.name('user.name.last')).sendKeys('surname'); + var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath('//button[span[contains(text(),' + + '"Next Step")]]')); + return Promise.all([firstNamePromise, lastNamePromise]).then(function () { + nextStepButton.click().then(function () { + driver.wait(seleniumWebdriver.until + .elementLocated(seleniumWebdriver.By.className('phone-step'))); + }); + }); +}; + +tap.plan(1); + +tap.tearDown(function () { + driver.quit(); +}); + +tap.beforeEach(function () { + driver.get('https://scratch.mit.edu/educators/register'); + return fillUsernameSlide() + .then(fillDemographicsSlide) + .then(fillNameSlide); +}); + +//inputs an invalid phone number and checks that the correct error message appears +tap.test('validatePhoneNumber', function (t) { + var phoneInput = driver.findElement(seleniumWebdriver.By.xpath('//input[@type="tel"]')); + var errorMessage = 'Please enter a valid phone number'; + var errorMessageXPath = '//span[@class="help-block validation-message"]/span[contains(text(),"' + + errorMessage + '")]'; + phoneInput.sendKeys(1234567890).then(function () { + driver.findElements(seleniumWebdriver.By.xpath(errorMessageXPath)) + .then(function (validationMessages) { + t.equal(validationMessages.length, 1); + t.end(); + }); + }); +}); + diff --git a/test/integration/test_educators_registration_username_step.js b/test/integration/test_educators_registration_username_step.js new file mode 100644 index 000000000..99366673d --- /dev/null +++ b/test/integration/test_educators_registration_username_step.js @@ -0,0 +1,111 @@ +/* +* Checks the behavior of first step in the educators registration process +* +* Test cases: https://github.com/LLK/scratch-www/wiki/Testing-Scratch-www#All_Test_Cases_Teacher_Join_Flow +*/ + +require('chromedriver'); +var seleniumWebdriver = require('selenium-webdriver'); +var tap = require('tap'); + +//chrome driver +var driver = new seleniumWebdriver.Builder().withCapabilities(seleniumWebdriver.Capabilities.chrome()).build(); + +tap.plan(5); + +tap.tearDown(function () { + driver.quit(); +}); + +tap.beforeEach(function () { + return driver.get('https://scratch.mit.edu/educators/register'); +}); + +//an error message should appear for a username less than 3 characters long +//input a username less than 3 characters and look for the validation message +tap.test('checkAtLeastThreeCharacters', function (t) { + //open scratch in a new instance of the browser + driver.get('https://scratch.mit.edu/educators/register'); + var usernameInput = driver.findElement(seleniumWebdriver.By.name('user.username')); + var errorMessage = 'Usernames must be at least 3 characters'; + var errorMessageXPath = '//span[@class="help-block validation-message" and contains(text(),"' + + errorMessage + '")]'; + usernameInput.sendKeys('hi').then(function () { + driver.findElements(seleniumWebdriver.By.xpath(errorMessageXPath)).then(function (validationMessages) { + t.equal(validationMessages.length, 1); + t.end(); + }); + }); +}); + +//usernames have to be unique +//input a username that exists and check that an error message appears +tap.test('checkUsernameExistsError', function (t) { + var usernameInput = driver.findElement(seleniumWebdriver.By.name('user.username')); + var passwordInput = driver.findElement(seleniumWebdriver.By.name('user.password')); + var inputUsername = usernameInput.sendKeys('mres'); + var passwordClick = passwordInput.click(); + var errorMessage = 'Sorry, that username already exists'; + var errorMessageXPath = '//span[@class="help-block validation-message" and contains(text(),"' + + errorMessage + '")]'; + Promise.all([inputUsername, passwordClick]).then(function () { + var errorBubble = driver.wait(seleniumWebdriver.until. + elementLocated(seleniumWebdriver.By.xpath(errorMessageXPath)), 10000); + t.notEqual(errorBubble, undefined); + t.end(); + }); +}); + +//passwords must be at least 6 characters +//find the validation message if the input password is less than 6 characters +tap.test('checkPasswordAtLeastSixCharacters', function (t) { + var passwordInput = driver.findElement(seleniumWebdriver.By.name('user.password')); + var errorMessage = 'Passwords must be at least six characters'; + var errorMessageXPath = '//span[@class="help-block validation-message" and contains(text(),"' + + errorMessage + '")]'; + passwordInput.sendKeys('hello').then(function () { + driver.findElements(seleniumWebdriver.By.xpath(errorMessageXPath)).then(function (validationMessages) { + t.equal(validationMessages.length, 1); + t.end(); + }); + }); +}); + +//password cannot be "password" +//find the validation message if the user inputs "password" +tap.test('checkPasswordNotPassword', function (t) { + driver.get('https://scratch.mit.edu/educators/register'); + var passwordInput = driver.findElement(seleniumWebdriver.By.name('user.password')); + //keeping "password" in messed with the xPath, may need to find a better way + var errorMessage = 'Your password may not be'; + var errorMessageXPath = '//span[@class="help-block validation-message" and contains(text(),"' + + errorMessage + '")]'; + passwordInput.sendKeys('password').then(function () { + driver.findElements(seleniumWebdriver.By.xpath(errorMessageXPath)).then(function (validationMessages) { + t.equal(validationMessages.length, 1); + t.end(); + }); + }); +}); + +//the username and password cannot be the same +//find the validation message if the username and password match +tap.test('checkPasswordNotUsername', function (t) { + driver.get('https://scratch.mit.edu/educators/register'); + var passwordInput = driver.findElement(seleniumWebdriver.By.name('user.password')); + var usernameInput = driver.findElement(seleniumWebdriver.By.name('user.username')); + var errorMessage = 'Your password may not be your username'; + var errorMessageXPath = '//span[@class="help-block validation-message" and contains(text(),"' + + errorMessage + '")]'; + var usernamePromise = usernameInput.sendKeys('educator'); + var passwordPromise = passwordInput.sendKeys('educator'); + //wait for both inputs to have the same text, and check for validation message + Promise.all([usernamePromise, passwordPromise]).then(function () { + driver.findElements(seleniumWebdriver.By.xpath(errorMessageXPath)).then(function (validationMessages) { + //there should be only one validation message + t.equal(validationMessages.length, 1); + t.end(); + }); + }); +}); + From 871bb4b52dbfe053aae2c52790fe5c61640fb476 Mon Sep 17 00:00:00 2001 From: cwillaim Date: Thu, 8 Dec 2016 18:04:09 -0500 Subject: [PATCH 02/32] Use a different username when filling in first slide, ladybug was taken. --- test/integration/test_educators_registration_demographics.js | 2 +- test/integration/test_educators_registration_name_step.js | 2 +- test/integration/test_educators_registration_phone_step.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/integration/test_educators_registration_demographics.js b/test/integration/test_educators_registration_demographics.js index 6f3b56f8b..bf1e2b834 100644 --- a/test/integration/test_educators_registration_demographics.js +++ b/test/integration/test_educators_registration_demographics.js @@ -13,7 +13,7 @@ var driver = new seleniumWebdriver.Builder().withCapabilities(seleniumWebdriver. var fillUsernameSlide = function () { var passwordInput = driver.findElement(seleniumWebdriver.By.name('user.password')); var usernameInput = driver.findElement(seleniumWebdriver.By.name('user.username')); - var usernamePromise = usernameInput.sendKeys('ladybug'); + var usernamePromise = usernameInput.sendKeys('clipspringer'); var passwordPromise = passwordInput.sendKeys('educators'); var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath('//button[span[contains(text(),' + '"Next Step")]]')); diff --git a/test/integration/test_educators_registration_name_step.js b/test/integration/test_educators_registration_name_step.js index 35abb50ab..f62ac0b7d 100644 --- a/test/integration/test_educators_registration_name_step.js +++ b/test/integration/test_educators_registration_name_step.js @@ -13,7 +13,7 @@ var driver = new seleniumWebdriver.Builder().withCapabilities(seleniumWebdriver. var fillUsernameSlide = function () { var passwordInput = driver.findElement(seleniumWebdriver.By.name('user.password')); var usernameInput = driver.findElement(seleniumWebdriver.By.name('user.username')); - var usernamePromise = usernameInput.sendKeys('ladybug'); + var usernamePromise = usernameInput.sendKeys('clipspringer'); var passwordPromise = passwordInput.sendKeys('educators'); var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath('//button[span[contains(text(),' + '"Next Step")]]')); diff --git a/test/integration/test_educators_registration_phone_step.js b/test/integration/test_educators_registration_phone_step.js index 7a6a3ff2c..2985a6ac2 100644 --- a/test/integration/test_educators_registration_phone_step.js +++ b/test/integration/test_educators_registration_phone_step.js @@ -13,7 +13,7 @@ var driver = new seleniumWebdriver.Builder().withCapabilities(seleniumWebdriver. var fillUsernameSlide = function () { var passwordInput = driver.findElement(seleniumWebdriver.By.name('user.password')); var usernameInput = driver.findElement(seleniumWebdriver.By.name('user.username')); - var usernamePromise = usernameInput.sendKeys('ladybug'); + var usernamePromise = usernameInput.sendKeys('clipspringer'); var passwordPromise = passwordInput.sendKeys('educators'); var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath('//button[span[contains(text(),' + '"Next Step")]]')); From 487b6cd1526acd9f2fd97362f72fc8d9eb20612f Mon Sep 17 00:00:00 2001 From: cwillaim Date: Fri, 16 Dec 2016 11:07:42 -0500 Subject: [PATCH 03/32] Add utils for reused strings in teacher registration tests, and update tests to import them. Add tests for the organization step. --- .../educator_registration_utils.js | 5 + ...est_educators_registration_demographics.js | 10 +- .../test_educators_registration_name_step.js | 38 ++++-- ...est_educators_registration_organization.js | 127 ++++++++++++++++++ .../test_educators_registration_phone_step.js | 11 +- 5 files changed, 167 insertions(+), 24 deletions(-) create mode 100644 test/integration/educator_registration_utils.js create mode 100644 test/integration/test_educators_registration_organization.js diff --git a/test/integration/educator_registration_utils.js b/test/integration/educator_registration_utils.js new file mode 100644 index 000000000..85d2c94de --- /dev/null +++ b/test/integration/educator_registration_utils.js @@ -0,0 +1,5 @@ +module.exports.constants = { + 'nextStepXpath': '//button[span[contains(text(), "Next Step")]]', + 'generalErrorMessageXpath': '//span[@class="help-block validation-message" and contains(text(),' + + '"This field is required")]' +}; diff --git a/test/integration/test_educators_registration_demographics.js b/test/integration/test_educators_registration_demographics.js index bf1e2b834..d52001eaa 100644 --- a/test/integration/test_educators_registration_demographics.js +++ b/test/integration/test_educators_registration_demographics.js @@ -7,6 +7,8 @@ require('chromedriver'); var seleniumWebdriver = require('selenium-webdriver'); var tap = require('tap'); +var constants = require('./educator_registration_utils.js').constants; + //chrome driver var driver = new seleniumWebdriver.Builder().withCapabilities(seleniumWebdriver.Capabilities.chrome()).build(); @@ -61,14 +63,10 @@ tap.test('checkOtherGenderInput', function (t) { //the user must select a gender //tries to advance the slide without selecting a gender tap.test('checkNoGenderInput', function (t) { - var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath('//button[span[contains(text(),' - + '"Next Step")]]')); - var errorMessage = 'This field is required'; - var errorMessageXPath = '//span[@class="help-block validation-message" and contains(text(),"' - + errorMessage + '")]'; + var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath(constants.nextStepXpath)); driver.findElement(seleniumWebdriver.By.xpath('//select[@name="user.country"]/option[2]')).click(); nextStepButton.click().then(function () { - driver.findElements(seleniumWebdriver.By.xpath(errorMessageXPath)) + driver.findElements(seleniumWebdriver.By.xpath(constants.generalErrorMessageXpath)) .then(function (validationMessages) { t.equal(validationMessages.length, 1); t.end(); diff --git a/test/integration/test_educators_registration_name_step.js b/test/integration/test_educators_registration_name_step.js index f62ac0b7d..410d2f33a 100644 --- a/test/integration/test_educators_registration_name_step.js +++ b/test/integration/test_educators_registration_name_step.js @@ -7,6 +7,8 @@ require('chromedriver'); var seleniumWebdriver = require('selenium-webdriver'); var tap = require('tap'); +var constants = require('./educator_registration_utils.js').constants; + //chrome driver var driver = new seleniumWebdriver.Builder().withCapabilities(seleniumWebdriver.Capabilities.chrome()).build(); @@ -15,8 +17,7 @@ var fillUsernameSlide = function () { var usernameInput = driver.findElement(seleniumWebdriver.By.name('user.username')); var usernamePromise = usernameInput.sendKeys('clipspringer'); var passwordPromise = passwordInput.sendKeys('educators'); - var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath('//button[span[contains(text(),' - + '"Next Step")]]')); + var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath(constants.nextStepXpath)); return Promise.all([usernamePromise, passwordPromise]).then(function () { nextStepButton.click().then(function () { driver.wait(seleniumWebdriver.until @@ -30,8 +31,7 @@ var fillDemographicsSlide = function () { ' and @type="radio"]')).click(); var selectCountry = driver.findElement(seleniumWebdriver.By.xpath('//select[@name="user.country"]' + '/option[2]')).click(); - var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath('//button[span[contains(text(),' - + '"Next Step")]]')); + var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath(constants.nextStepXpath)); return Promise.all([clickMaleInput, selectCountry]).then(function () { nextStepButton.click().then(function () { driver.wait(seleniumWebdriver.until @@ -40,7 +40,7 @@ var fillDemographicsSlide = function () { }); }; -tap.plan(1); +tap.plan(2); tap.tearDown(function () { driver.quit(); @@ -52,16 +52,30 @@ tap.beforeEach(function () { }); //attempts to advance the slide without inputting either name, checks that both give the correct error -tap.test('checkBothNamesRequired', function (t) { - var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath('//button[span[contains(text(),' - + '"Next Step")]]')); - var errorMessage = 'This field is required'; - var errorMessageXPath = '//span[@class="help-block validation-message" and contains(text(),"' - + errorMessage + '")]'; +tap.test('checkFirstNameRequired', function (t) { + var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath(constants.nextStepXpath)); + var errorMessageXPath = '//input[@name="user.name.first"]/following-sibling::' + + 'span[@class="help-block validation-message" and contains(text(),' + + '"This field is required")]'; nextStepButton.click().then(function () { driver.findElements(seleniumWebdriver.By.xpath(errorMessageXPath)) .then(function (validationMessages) { - t.equal(validationMessages.length, 2); + t.equal(validationMessages.length, 1); + t.end(); + }); + }); +}); + +//attempts to advance the slide without inputting either name, checks that both give the correct error +tap.test('checkLastNameRequired', function (t) { + var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath(constants.nextStepXpath)); + var errorMessageXPath = '//input[@name="user.name.last"]/following-sibling::' + + 'span[@class="help-block validation-message" and contains(text(),' + + '"This field is required")]'; + nextStepButton.click().then(function () { + driver.findElements(seleniumWebdriver.By.xpath(errorMessageXPath)) + .then(function (validationMessages) { + t.equal(validationMessages.length, 1); t.end(); }); }); diff --git a/test/integration/test_educators_registration_organization.js b/test/integration/test_educators_registration_organization.js new file mode 100644 index 000000000..eb738b2b0 --- /dev/null +++ b/test/integration/test_educators_registration_organization.js @@ -0,0 +1,127 @@ +/* + * Checks the behavior of the phone number step in the educators registration process + * + * Test cases: https://github.com/LLK/scratch-www/wiki/Testing-Scratch-www#All_Test_Cases_Teacher_Join_Flow + */ +require('chromedriver'); +var seleniumWebdriver = require('selenium-webdriver'); +var tap = require('tap'); + +var constants = require('./educator_registration_utils.js').constants; + +//Set test url through environment variable +//var rootUrl = process.ENV.ROOT_URL || 'http://localhost:8333'; + +//chrome driver +var driver = new seleniumWebdriver.Builder().withCapabilities(seleniumWebdriver.Capabilities.chrome()).build(); + +var fillUsernameSlide = function () { + var passwordInput = driver.findElement(seleniumWebdriver.By.name('user.password')); + var usernameInput = driver.findElement(seleniumWebdriver.By.name('user.username')); + var usernamePromise = usernameInput.sendKeys('clipspringer'); + var passwordPromise = passwordInput.sendKeys('educators'); + var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath(constants.nextStepXpath)); + return Promise.all([usernamePromise, passwordPromise]).then(function () { + nextStepButton.click().then(function () { + driver.wait(seleniumWebdriver.until + .elementLocated(seleniumWebdriver.By.className('demographics-step'))); + }); + }); +}; + +var fillDemographicsSlide = function () { + var clickMaleInput = driver.findElement(seleniumWebdriver.By.xpath('//input[@value="male"' + + 'and @type="radio"]')).click(); + var selectCountry = driver.findElement(seleniumWebdriver.By.xpath('//select[@name="user.country"]' + + '/option[2]')).click(); + var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath(constants.nextStepXpath)); + return Promise.all([clickMaleInput, selectCountry]).then(function () { + nextStepButton.click().then(function () { + driver.wait(seleniumWebdriver.until + .elementLocated(seleniumWebdriver.By.className('name-step'))); + }); + }); +}; + +var fillNameSlide = function () { + var firstNamePromise = driver.findElement(seleniumWebdriver.By.name('user.name.first')).sendKeys('first'); + var lastNamePromise = driver.findElement(seleniumWebdriver.By.name('user.name.last')).sendKeys('surname'); + var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath(constants.nextStepXpath)); + return Promise.all([firstNamePromise, lastNamePromise]).then(function () { + nextStepButton.click().then(function () { + driver.wait(seleniumWebdriver.until + .elementLocated(seleniumWebdriver.By.className('phone-step'))); + }); + }); +}; + +var fillPhoneSlide = function () { + var phoneInput = driver.findElement(seleniumWebdriver.By.xpath('//input[@type="tel"]')); + var consentCheckbox = driver.findElement(seleniumWebdriver.By.name('phoneConsent')); + var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath(constants.nextStepXpath)); + var phoneNumberPromise = phoneInput.sendKeys('6172535960'); + var consentPromise = consentCheckbox.click(); + return Promise.all([phoneNumberPromise, consentPromise]).then(function () { + nextStepButton.click().then(function () { + driver.wait(seleniumWebdriver.until + .elementLocated(seleniumWebdriver.By.className('organization-step'))); + }); + }); +}; + +tap.plan(3); + +tap.tearDown(function () { + driver.quit(); +}); + +tap.beforeEach(function () { + driver.get('https://scratch.mit.edu/educators/register'); + return fillUsernameSlide() + .then(fillDemographicsSlide) + .then(fillNameSlide) + .then(fillPhoneSlide); +}); + +tap.test('otherFieldRequiredIfChecked', function (t) { + var otherCheckbox = driver.findElement(seleniumWebdriver.By.xpath('//input[@type="checkbox" and @value="8"]')); + var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath(constants.nextStepXpath)); + var errorMessageXPath = '//div[@class="other-input"]' + constants.generalErrorMessageXpath; + otherCheckbox.click().then(function () { + nextStepButton.click().then(function () { + driver.findElements(seleniumWebdriver.By.xpath(errorMessageXPath)) + .then(function (validationMessages) { + t.equal(validationMessages.length, 1); + t.end(); + }); + }); + }); +}); + +tap.test('checkOrganizationFieldRequired', function (t) { + var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath(constants.nextStepXpath)); + var errorMessageXPath = '//input[@name="organization.name"]/following-sibling::' + + 'span[@class="help-block validation-message" and contains(text(),' + + '"This field is required")]'; + nextStepButton.click().then(function () { + driver.findElements(seleniumWebdriver.By.xpath(errorMessageXPath)) + .then(function (validationMessages) { + t.equal(validationMessages.length, 1); + t.end(); + }); + }); +}); + +tap.test('checkRoleFieldRequired', function (t) { + var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath(constants.nextStepXpath)); + var errorMessageXPath = '//input[@name="organization.title"]/following-sibling::' + + 'span[@class="help-block validation-message" and contains(text(),' + + '"This field is required")]'; + nextStepButton.click().then(function () { + driver.findElements(seleniumWebdriver.By.xpath(errorMessageXPath)) + .then(function (validationMessages) { + t.equal(validationMessages.length, 1); + t.end(); + }); + }); +}); diff --git a/test/integration/test_educators_registration_phone_step.js b/test/integration/test_educators_registration_phone_step.js index 2985a6ac2..06a1df146 100644 --- a/test/integration/test_educators_registration_phone_step.js +++ b/test/integration/test_educators_registration_phone_step.js @@ -7,6 +7,8 @@ require('chromedriver'); var seleniumWebdriver = require('selenium-webdriver'); var tap = require('tap'); +var constants = require('./educator_registration_utils.js').constants; + //chrome driver var driver = new seleniumWebdriver.Builder().withCapabilities(seleniumWebdriver.Capabilities.chrome()).build(); @@ -15,8 +17,7 @@ var fillUsernameSlide = function () { var usernameInput = driver.findElement(seleniumWebdriver.By.name('user.username')); var usernamePromise = usernameInput.sendKeys('clipspringer'); var passwordPromise = passwordInput.sendKeys('educators'); - var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath('//button[span[contains(text(),' - + '"Next Step")]]')); + var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath(constants.nextStepXpath)); return Promise.all([usernamePromise, passwordPromise]).then(function () { nextStepButton.click().then(function () { driver.wait(seleniumWebdriver.until @@ -30,8 +31,7 @@ var fillDemographicsSlide = function () { 'and @type="radio"]')).click(); var selectCountry = driver.findElement(seleniumWebdriver.By.xpath('//select[@name="user.country"]' + '/option[2]')).click(); - var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath('//button[span[contains(text(),' - + '"Next Step")]]')); + var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath(constants.nextStepXpath)); return Promise.all([clickMaleInput, selectCountry]).then(function () { nextStepButton.click().then(function () { driver.wait(seleniumWebdriver.until @@ -43,8 +43,7 @@ var fillDemographicsSlide = function () { var fillNameSlide = function () { var firstNamePromise = driver.findElement(seleniumWebdriver.By.name('user.name.first')).sendKeys('first'); var lastNamePromise = driver.findElement(seleniumWebdriver.By.name('user.name.last')).sendKeys('surname'); - var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath('//button[span[contains(text(),' - + '"Next Step")]]')); + var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath(constants.nextStepXpath)); return Promise.all([firstNamePromise, lastNamePromise]).then(function () { nextStepButton.click().then(function () { driver.wait(seleniumWebdriver.until From 3d6b89ac67e20f53ab54b867d1b300eea789f5fc Mon Sep 17 00:00:00 2001 From: cwillaim Date: Fri, 16 Dec 2016 11:15:34 -0500 Subject: [PATCH 04/32] Rename tests to follow proper naming - 'teacher' instead of 'educator.' Move teacher registration tests into subfolder. --- .../teacher_registration_utils.js} | 0 .../test_teacher_registration_demographics.js} | 0 .../test_teacher_registration_name_step.js} | 0 .../test_teacher_registration_organization.js} | 0 .../test_teacher_registration_phone_step.js} | 0 .../test_teacher_registration_username_step.js} | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename test/integration/{educator_registration_utils.js => teacher-registration/teacher_registration_utils.js} (100%) rename test/integration/{test_educators_registration_demographics.js => teacher-registration/test_teacher_registration_demographics.js} (100%) rename test/integration/{test_educators_registration_name_step.js => teacher-registration/test_teacher_registration_name_step.js} (100%) rename test/integration/{test_educators_registration_organization.js => teacher-registration/test_teacher_registration_organization.js} (100%) rename test/integration/{test_educators_registration_phone_step.js => teacher-registration/test_teacher_registration_phone_step.js} (100%) rename test/integration/{test_educators_registration_username_step.js => teacher-registration/test_teacher_registration_username_step.js} (100%) diff --git a/test/integration/educator_registration_utils.js b/test/integration/teacher-registration/teacher_registration_utils.js similarity index 100% rename from test/integration/educator_registration_utils.js rename to test/integration/teacher-registration/teacher_registration_utils.js diff --git a/test/integration/test_educators_registration_demographics.js b/test/integration/teacher-registration/test_teacher_registration_demographics.js similarity index 100% rename from test/integration/test_educators_registration_demographics.js rename to test/integration/teacher-registration/test_teacher_registration_demographics.js diff --git a/test/integration/test_educators_registration_name_step.js b/test/integration/teacher-registration/test_teacher_registration_name_step.js similarity index 100% rename from test/integration/test_educators_registration_name_step.js rename to test/integration/teacher-registration/test_teacher_registration_name_step.js diff --git a/test/integration/test_educators_registration_organization.js b/test/integration/teacher-registration/test_teacher_registration_organization.js similarity index 100% rename from test/integration/test_educators_registration_organization.js rename to test/integration/teacher-registration/test_teacher_registration_organization.js diff --git a/test/integration/test_educators_registration_phone_step.js b/test/integration/teacher-registration/test_teacher_registration_phone_step.js similarity index 100% rename from test/integration/test_educators_registration_phone_step.js rename to test/integration/teacher-registration/test_teacher_registration_phone_step.js diff --git a/test/integration/test_educators_registration_username_step.js b/test/integration/teacher-registration/test_teacher_registration_username_step.js similarity index 100% rename from test/integration/test_educators_registration_username_step.js rename to test/integration/teacher-registration/test_teacher_registration_username_step.js From 69c9cdba175ac28e93e4a13fa4dd081e7af2f330 Mon Sep 17 00:00:00 2001 From: cwillaim Date: Fri, 16 Dec 2016 11:18:55 -0500 Subject: [PATCH 05/32] Update reference to the utils where each test imports them (educator...utils to teacher...utils) --- .../test_teacher_registration_demographics.js | 2 +- .../teacher-registration/test_teacher_registration_name_step.js | 2 +- .../test_teacher_registration_organization.js | 2 +- .../test_teacher_registration_phone_step.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/integration/teacher-registration/test_teacher_registration_demographics.js b/test/integration/teacher-registration/test_teacher_registration_demographics.js index d52001eaa..08370fc71 100644 --- a/test/integration/teacher-registration/test_teacher_registration_demographics.js +++ b/test/integration/teacher-registration/test_teacher_registration_demographics.js @@ -7,7 +7,7 @@ require('chromedriver'); var seleniumWebdriver = require('selenium-webdriver'); var tap = require('tap'); -var constants = require('./educator_registration_utils.js').constants; +var constants = require('./teacher_registration_utils.js').constants; //chrome driver var driver = new seleniumWebdriver.Builder().withCapabilities(seleniumWebdriver.Capabilities.chrome()).build(); diff --git a/test/integration/teacher-registration/test_teacher_registration_name_step.js b/test/integration/teacher-registration/test_teacher_registration_name_step.js index 410d2f33a..b08dda237 100644 --- a/test/integration/teacher-registration/test_teacher_registration_name_step.js +++ b/test/integration/teacher-registration/test_teacher_registration_name_step.js @@ -7,7 +7,7 @@ require('chromedriver'); var seleniumWebdriver = require('selenium-webdriver'); var tap = require('tap'); -var constants = require('./educator_registration_utils.js').constants; +var constants = require('./teacher_registration_utils.js').constants; //chrome driver var driver = new seleniumWebdriver.Builder().withCapabilities(seleniumWebdriver.Capabilities.chrome()).build(); diff --git a/test/integration/teacher-registration/test_teacher_registration_organization.js b/test/integration/teacher-registration/test_teacher_registration_organization.js index eb738b2b0..286a981cd 100644 --- a/test/integration/teacher-registration/test_teacher_registration_organization.js +++ b/test/integration/teacher-registration/test_teacher_registration_organization.js @@ -7,7 +7,7 @@ require('chromedriver'); var seleniumWebdriver = require('selenium-webdriver'); var tap = require('tap'); -var constants = require('./educator_registration_utils.js').constants; +var constants = require('./teacher_registration_utils.js').constants; //Set test url through environment variable //var rootUrl = process.ENV.ROOT_URL || 'http://localhost:8333'; diff --git a/test/integration/teacher-registration/test_teacher_registration_phone_step.js b/test/integration/teacher-registration/test_teacher_registration_phone_step.js index 06a1df146..456463ae9 100644 --- a/test/integration/teacher-registration/test_teacher_registration_phone_step.js +++ b/test/integration/teacher-registration/test_teacher_registration_phone_step.js @@ -7,7 +7,7 @@ require('chromedriver'); var seleniumWebdriver = require('selenium-webdriver'); var tap = require('tap'); -var constants = require('./educator_registration_utils.js').constants; +var constants = require('./teacher_registration_utils.js').constants; //chrome driver var driver = new seleniumWebdriver.Builder().withCapabilities(seleniumWebdriver.Capabilities.chrome()).build(); From 4ef6dc193df9ef565cc0358c12f85e4a9e2aff3b Mon Sep 17 00:00:00 2001 From: cwillaim Date: Sat, 17 Dec 2016 23:07:49 -0500 Subject: [PATCH 06/32] Move code for filling in slides to utils, and update tests accordingly. --- .../teacher_registration_utils.js | 55 +++++++++++++++++++ .../test_teacher_registration_demographics.js | 20 +------ .../test_teacher_registration_name_step.js | 30 ++-------- .../test_teacher_registration_organization.js | 49 ++--------------- .../test_teacher_registration_phone_step.js | 36 ++---------- 5 files changed, 74 insertions(+), 116 deletions(-) diff --git a/test/integration/teacher-registration/teacher_registration_utils.js b/test/integration/teacher-registration/teacher_registration_utils.js index 85d2c94de..2889fed5d 100644 --- a/test/integration/teacher-registration/teacher_registration_utils.js +++ b/test/integration/teacher-registration/teacher_registration_utils.js @@ -3,3 +3,58 @@ module.exports.constants = { 'generalErrorMessageXpath': '//span[@class="help-block validation-message" and contains(text(),' + '"This field is required")]' }; + +module.exports.fillUsernameSlide = function (driver, seleniumWebdriver) { + var passwordInput = driver.findElement(seleniumWebdriver.By.name('user.password')); + var usernameInput = driver.findElement(seleniumWebdriver.By.name('user.username')); + var usernamePromise = usernameInput.sendKeys('clipspringer'); + var passwordPromise = passwordInput.sendKeys('educators'); + var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath('//button[span[contains(text(),' + + '"Next Step")]]')); + return Promise.all([usernamePromise, passwordPromise]).then(function () { + nextStepButton.click().then(function () { + driver.wait(seleniumWebdriver.until + .elementLocated(seleniumWebdriver.By.className('demographics-step'))); + }); + }); +}; + +module.exports.fillDemographicsSlide = function (driver, seleniumWebdriver) { + var clickMaleInput = driver.findElement(seleniumWebdriver.By.xpath('//input[@value="male"' + + 'and @type="radio"]')).click(); + var selectCountry = driver.findElement(seleniumWebdriver.By.xpath('//select[@name="user.country"]' + + '/option[2]')).click(); + var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath(module.exports.constants.nextStepXpath)); + return Promise.all([clickMaleInput, selectCountry]).then(function () { + nextStepButton.click().then(function () { + driver.wait(seleniumWebdriver.until + .elementLocated(seleniumWebdriver.By.className('name-step'))); + }); + }); +}; + +module.exports.fillNameSlide = function (driver, seleniumWebdriver) { + var firstNamePromise = driver.findElement(seleniumWebdriver.By.name('user.name.first')).sendKeys('first'); + var lastNamePromise = driver.findElement(seleniumWebdriver.By.name('user.name.last')).sendKeys('surname'); + var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath(module.exports.constants.nextStepXpath)); + return Promise.all([firstNamePromise, lastNamePromise]).then(function () { + nextStepButton.click().then(function () { + driver.wait(seleniumWebdriver.until + .elementLocated(seleniumWebdriver.By.className('phone-step'))); + }); + }); +}; + +module.exports.fillPhoneSlide = function (driver, seleniumWebdriver) { + var phoneInput = driver.findElement(seleniumWebdriver.By.xpath('//input[@type="tel"]')); + var consentCheckbox = driver.findElement(seleniumWebdriver.By.name('phoneConsent')); + var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath(module.exports.constants.nextStepXpath)); + var phoneNumberPromise = phoneInput.sendKeys('6172535960'); + var consentPromise = consentCheckbox.click(); + return Promise.all([phoneNumberPromise, consentPromise]).then(function () { + nextStepButton.click().then(function () { + driver.wait(seleniumWebdriver.until + .elementLocated(seleniumWebdriver.By.className('organization-step'))); + }); + }); +}; diff --git a/test/integration/teacher-registration/test_teacher_registration_demographics.js b/test/integration/teacher-registration/test_teacher_registration_demographics.js index 08370fc71..e2126f62b 100644 --- a/test/integration/teacher-registration/test_teacher_registration_demographics.js +++ b/test/integration/teacher-registration/test_teacher_registration_demographics.js @@ -7,26 +7,12 @@ require('chromedriver'); var seleniumWebdriver = require('selenium-webdriver'); var tap = require('tap'); -var constants = require('./teacher_registration_utils.js').constants; +var utils = require('./teacher_registration_utils.js'); +var constants = utils.constants; //chrome driver var driver = new seleniumWebdriver.Builder().withCapabilities(seleniumWebdriver.Capabilities.chrome()).build(); -var fillUsernameSlide = function () { - var passwordInput = driver.findElement(seleniumWebdriver.By.name('user.password')); - var usernameInput = driver.findElement(seleniumWebdriver.By.name('user.username')); - var usernamePromise = usernameInput.sendKeys('clipspringer'); - var passwordPromise = passwordInput.sendKeys('educators'); - var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath('//button[span[contains(text(),' - + '"Next Step")]]')); - return Promise.all([usernamePromise, passwordPromise]).then(function () { - nextStepButton.click().then(function () { - driver.wait(seleniumWebdriver.until - .elementLocated(seleniumWebdriver.By.className('demographics-step'))); - }); - }); -}; - tap.plan(2); tap.tearDown(function () { @@ -35,7 +21,7 @@ tap.tearDown(function () { tap.beforeEach(function () { driver.get('https://scratch.mit.edu/educators/register'); - return fillUsernameSlide(); + return utils.fillUsernameSlide(driver, seleniumWebdriver); }); //if the user selects the other gender option, they must input a gender diff --git a/test/integration/teacher-registration/test_teacher_registration_name_step.js b/test/integration/teacher-registration/test_teacher_registration_name_step.js index b08dda237..13cbc22b6 100644 --- a/test/integration/teacher-registration/test_teacher_registration_name_step.js +++ b/test/integration/teacher-registration/test_teacher_registration_name_step.js @@ -7,37 +7,18 @@ require('chromedriver'); var seleniumWebdriver = require('selenium-webdriver'); var tap = require('tap'); -var constants = require('./teacher_registration_utils.js').constants; +var utils = require('./teacher_registration_utils.js'); +var constants = utils.constants; //chrome driver var driver = new seleniumWebdriver.Builder().withCapabilities(seleniumWebdriver.Capabilities.chrome()).build(); var fillUsernameSlide = function () { - var passwordInput = driver.findElement(seleniumWebdriver.By.name('user.password')); - var usernameInput = driver.findElement(seleniumWebdriver.By.name('user.username')); - var usernamePromise = usernameInput.sendKeys('clipspringer'); - var passwordPromise = passwordInput.sendKeys('educators'); - var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath(constants.nextStepXpath)); - return Promise.all([usernamePromise, passwordPromise]).then(function () { - nextStepButton.click().then(function () { - driver.wait(seleniumWebdriver.until - .elementLocated(seleniumWebdriver.By.className('demographics-step'))); - }); - }); + return utils.fillUsernameSlide(driver, seleniumWebdriver); }; var fillDemographicsSlide = function () { - var clickMaleInput = driver.findElement(seleniumWebdriver.By.xpath('//input[@value="male"' + - ' and @type="radio"]')).click(); - var selectCountry = driver.findElement(seleniumWebdriver.By.xpath('//select[@name="user.country"]' + - '/option[2]')).click(); - var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath(constants.nextStepXpath)); - return Promise.all([clickMaleInput, selectCountry]).then(function () { - nextStepButton.click().then(function () { - driver.wait(seleniumWebdriver.until - .elementLocated(seleniumWebdriver.By.className('name-step'))); - }); - }); + return utils.fillDemographicsSlide(driver, seleniumWebdriver); }; tap.plan(2); @@ -48,7 +29,8 @@ tap.tearDown(function () { tap.beforeEach(function () { driver.get('https://scratch.mit.edu/educators/register'); - return fillUsernameSlide().then(fillDemographicsSlide); + return fillUsernameSlide() + .then(fillDemographicsSlide); }); //attempts to advance the slide without inputting either name, checks that both give the correct error diff --git a/test/integration/teacher-registration/test_teacher_registration_organization.js b/test/integration/teacher-registration/test_teacher_registration_organization.js index 286a981cd..53415afa1 100644 --- a/test/integration/teacher-registration/test_teacher_registration_organization.js +++ b/test/integration/teacher-registration/test_teacher_registration_organization.js @@ -7,7 +7,8 @@ require('chromedriver'); var seleniumWebdriver = require('selenium-webdriver'); var tap = require('tap'); -var constants = require('./teacher_registration_utils.js').constants; +var utils = require('./teacher_registration_utils.js'); +var constants = utils.constants; //Set test url through environment variable //var rootUrl = process.ENV.ROOT_URL || 'http://localhost:8333'; @@ -16,57 +17,19 @@ var constants = require('./teacher_registration_utils.js').constants; var driver = new seleniumWebdriver.Builder().withCapabilities(seleniumWebdriver.Capabilities.chrome()).build(); var fillUsernameSlide = function () { - var passwordInput = driver.findElement(seleniumWebdriver.By.name('user.password')); - var usernameInput = driver.findElement(seleniumWebdriver.By.name('user.username')); - var usernamePromise = usernameInput.sendKeys('clipspringer'); - var passwordPromise = passwordInput.sendKeys('educators'); - var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath(constants.nextStepXpath)); - return Promise.all([usernamePromise, passwordPromise]).then(function () { - nextStepButton.click().then(function () { - driver.wait(seleniumWebdriver.until - .elementLocated(seleniumWebdriver.By.className('demographics-step'))); - }); - }); + return utils.fillUsernameSlide(driver, seleniumWebdriver); }; var fillDemographicsSlide = function () { - var clickMaleInput = driver.findElement(seleniumWebdriver.By.xpath('//input[@value="male"' + - 'and @type="radio"]')).click(); - var selectCountry = driver.findElement(seleniumWebdriver.By.xpath('//select[@name="user.country"]' + - '/option[2]')).click(); - var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath(constants.nextStepXpath)); - return Promise.all([clickMaleInput, selectCountry]).then(function () { - nextStepButton.click().then(function () { - driver.wait(seleniumWebdriver.until - .elementLocated(seleniumWebdriver.By.className('name-step'))); - }); - }); + return utils.fillDemographicsSlide(driver, seleniumWebdriver); }; var fillNameSlide = function () { - var firstNamePromise = driver.findElement(seleniumWebdriver.By.name('user.name.first')).sendKeys('first'); - var lastNamePromise = driver.findElement(seleniumWebdriver.By.name('user.name.last')).sendKeys('surname'); - var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath(constants.nextStepXpath)); - return Promise.all([firstNamePromise, lastNamePromise]).then(function () { - nextStepButton.click().then(function () { - driver.wait(seleniumWebdriver.until - .elementLocated(seleniumWebdriver.By.className('phone-step'))); - }); - }); + return utils.fillNameSlide(driver, seleniumWebdriver); }; var fillPhoneSlide = function () { - var phoneInput = driver.findElement(seleniumWebdriver.By.xpath('//input[@type="tel"]')); - var consentCheckbox = driver.findElement(seleniumWebdriver.By.name('phoneConsent')); - var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath(constants.nextStepXpath)); - var phoneNumberPromise = phoneInput.sendKeys('6172535960'); - var consentPromise = consentCheckbox.click(); - return Promise.all([phoneNumberPromise, consentPromise]).then(function () { - nextStepButton.click().then(function () { - driver.wait(seleniumWebdriver.until - .elementLocated(seleniumWebdriver.By.className('organization-step'))); - }); - }); + return utils.fillPhoneSlide(driver, seleniumWebdriver); }; tap.plan(3); diff --git a/test/integration/teacher-registration/test_teacher_registration_phone_step.js b/test/integration/teacher-registration/test_teacher_registration_phone_step.js index 456463ae9..460d3f0d5 100644 --- a/test/integration/teacher-registration/test_teacher_registration_phone_step.js +++ b/test/integration/teacher-registration/test_teacher_registration_phone_step.js @@ -7,49 +7,21 @@ require('chromedriver'); var seleniumWebdriver = require('selenium-webdriver'); var tap = require('tap'); -var constants = require('./teacher_registration_utils.js').constants; +var utils = require('./teacher_registration_utils.js'); //chrome driver var driver = new seleniumWebdriver.Builder().withCapabilities(seleniumWebdriver.Capabilities.chrome()).build(); var fillUsernameSlide = function () { - var passwordInput = driver.findElement(seleniumWebdriver.By.name('user.password')); - var usernameInput = driver.findElement(seleniumWebdriver.By.name('user.username')); - var usernamePromise = usernameInput.sendKeys('clipspringer'); - var passwordPromise = passwordInput.sendKeys('educators'); - var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath(constants.nextStepXpath)); - return Promise.all([usernamePromise, passwordPromise]).then(function () { - nextStepButton.click().then(function () { - driver.wait(seleniumWebdriver.until - .elementLocated(seleniumWebdriver.By.className('demographics-step'))); - }); - }); + return utils.fillUsernameSlide(driver, seleniumWebdriver); }; var fillDemographicsSlide = function () { - var clickMaleInput = driver.findElement(seleniumWebdriver.By.xpath('//input[@value="male"' + - 'and @type="radio"]')).click(); - var selectCountry = driver.findElement(seleniumWebdriver.By.xpath('//select[@name="user.country"]' + - '/option[2]')).click(); - var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath(constants.nextStepXpath)); - return Promise.all([clickMaleInput, selectCountry]).then(function () { - nextStepButton.click().then(function () { - driver.wait(seleniumWebdriver.until - .elementLocated(seleniumWebdriver.By.className('name-step'))); - }); - }); + return utils.fillDemographicsSlide(driver, seleniumWebdriver); }; var fillNameSlide = function () { - var firstNamePromise = driver.findElement(seleniumWebdriver.By.name('user.name.first')).sendKeys('first'); - var lastNamePromise = driver.findElement(seleniumWebdriver.By.name('user.name.last')).sendKeys('surname'); - var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath(constants.nextStepXpath)); - return Promise.all([firstNamePromise, lastNamePromise]).then(function () { - nextStepButton.click().then(function () { - driver.wait(seleniumWebdriver.until - .elementLocated(seleniumWebdriver.By.className('phone-step'))); - }); - }); + return utils.fillNameSlide(driver, seleniumWebdriver); }; tap.plan(1); From 29a38ca917a968c46e6e37a7300c2c1e006571db Mon Sep 17 00:00:00 2001 From: cwillaim Date: Sun, 18 Dec 2016 21:36:47 -0500 Subject: [PATCH 07/32] Finish up tests for the organization step. --- .../test_teacher_registration_organization.js | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/test/integration/teacher-registration/test_teacher_registration_organization.js b/test/integration/teacher-registration/test_teacher_registration_organization.js index 53415afa1..bda81b693 100644 --- a/test/integration/teacher-registration/test_teacher_registration_organization.js +++ b/test/integration/teacher-registration/test_teacher_registration_organization.js @@ -1,5 +1,5 @@ /* - * Checks the behavior of the phone number step in the educators registration process + * Checks the behavior of the organization step in the educators registration process * * Test cases: https://github.com/LLK/scratch-www/wiki/Testing-Scratch-www#All_Test_Cases_Teacher_Join_Flow */ @@ -32,7 +32,7 @@ var fillPhoneSlide = function () { return utils.fillPhoneSlide(driver, seleniumWebdriver); }; -tap.plan(3); +tap.plan(4); tap.tearDown(function () { driver.quit(); @@ -88,3 +88,17 @@ tap.test('checkRoleFieldRequired', function (t) { }); }); }); + +tap.test('checkOrganizationTypeRequired', function (t) { + var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath(constants.nextStepXpath)); + var errorMessageXPath = '//div[@class="checkbox"]/following-sibling::' + + 'span[@class="help-block validation-message" and contains(text(),' + + '"This field is required")]'; + nextStepButton.click().then(function () { + driver.findElements(seleniumWebdriver.By.xpath(errorMessageXPath)) + .then(function (validationMessages) { + t.equal(validationMessages.length, 1); + t.end(); + }); + }); +}); From 432fd1a81bd18c4d710894313231c0c6d77a6096 Mon Sep 17 00:00:00 2001 From: cwillaim Date: Sun, 18 Dec 2016 21:57:47 -0500 Subject: [PATCH 08/32] Test the address and 'use scratch' steps. --- .../teacher_registration_utils.js | 42 ++++++++- .../test_teacher_registration_address_step.js | 78 +++++++++++++++ ...st_teacher_registration_usescratch_step.js | 94 +++++++++++++++++++ 3 files changed, 212 insertions(+), 2 deletions(-) create mode 100644 test/integration/teacher-registration/test_teacher_registration_address_step.js create mode 100644 test/integration/teacher-registration/test_teacher_registration_usescratch_step.js diff --git a/test/integration/teacher-registration/teacher_registration_utils.js b/test/integration/teacher-registration/teacher_registration_utils.js index 2889fed5d..51f3f6ade 100644 --- a/test/integration/teacher-registration/teacher_registration_utils.js +++ b/test/integration/teacher-registration/teacher_registration_utils.js @@ -1,7 +1,11 @@ module.exports.constants = { 'nextStepXpath': '//button[span[contains(text(), "Next Step")]]', 'generalErrorMessageXpath': '//span[@class="help-block validation-message" and contains(text(),' - + '"This field is required")]' + + '"This field is required")]', + 'loremIpsumTextLong': 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur viverra' + + 'nec mauris efficitur tincidunt. Vestibulum ut diam odio. Cum sociis natoque penatibus et magnis dis' + + 'parturient montes, nascetur ridiculus mus. Morbi non enim dolor. Vestibulum at enim vestibulum, ullamcorper' + + 'Duis eget quam pharetra, ultricies est eu, pharetra nisi. In tempor cursus nisi, non sagittis quam gravida.' }; module.exports.fillUsernameSlide = function (driver, seleniumWebdriver) { @@ -23,7 +27,7 @@ module.exports.fillDemographicsSlide = function (driver, seleniumWebdriver) { var clickMaleInput = driver.findElement(seleniumWebdriver.By.xpath('//input[@value="male"' + 'and @type="radio"]')).click(); var selectCountry = driver.findElement(seleniumWebdriver.By.xpath('//select[@name="user.country"]' + - '/option[2]')).click(); + '/option[@value="us"]')).click(); var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath(module.exports.constants.nextStepXpath)); return Promise.all([clickMaleInput, selectCountry]).then(function () { nextStepButton.click().then(function () { @@ -58,3 +62,37 @@ module.exports.fillPhoneSlide = function (driver, seleniumWebdriver) { }); }); }; + +module.exports.fillOrganizationSlide = function (driver, seleniumWebdriver) { + var organizationInput = driver.findElement(seleniumWebdriver.By.name('organization.name')); + var titleInput = driver.findElement(seleniumWebdriver.By.name('organization.title')); + var typeCheckbox = driver.findElement(seleniumWebdriver.By.xpath('//input[@type="checkbox" and @value="3"]')); + var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath(module.exports.constants.nextStepXpath)); + var organizationPromise = organizationInput.sendKeys('MIT Media Lab'); + var titlePromise = titleInput.sendKeys('Software Developer'); + var typePromise = typeCheckbox.click(); + return Promise.all([organizationPromise, titlePromise, typePromise]).then(function () { + nextStepButton.click().then(function () { + driver.wait(seleniumWebdriver.until + .elementLocated(seleniumWebdriver.By.className('address-step'))); + }); + }); +}; + +module.exports.fillAddressSlide = function (driver, seleniumWebdriver) { + var addressInput = driver.findElement(seleniumWebdriver.By.name('address.line1')); + var cityInput = driver.findElement(seleniumWebdriver.By.name('address.city')); + var zipCodeInput = driver.findElement(seleniumWebdriver.By.name('address.zip')); + var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath(module.exports.constants.nextStepXpath)); + var addressPromise = addressInput.sendKeys('77 Massachusetts Avenue, E14/E15'); + var cityPromise = cityInput.sendKeys('Cambridge'); + var statePromise = driver.findElement(seleniumWebdriver.By.xpath('//select[@name="address.state"]' + + '/option[@value="us-ma"]')).click(); + var zipPromise = zipCodeInput.sendKeys('02139'); + return Promise.all([addressPromise, cityPromise, statePromise, zipPromise]).then(function () { + nextStepButton.click().then(function () { + driver.wait(seleniumWebdriver.until + .elementLocated(seleniumWebdriver.By.className('usescratch-step'))); + }); + }); +}; diff --git a/test/integration/teacher-registration/test_teacher_registration_address_step.js b/test/integration/teacher-registration/test_teacher_registration_address_step.js new file mode 100644 index 000000000..63770ee72 --- /dev/null +++ b/test/integration/teacher-registration/test_teacher_registration_address_step.js @@ -0,0 +1,78 @@ +/* + * Checks the behavior of the address step in the educators registration process + * + * Test cases: https://github.com/LLK/scratch-www/wiki/Testing-Scratch-www#All_Test_Cases_Teacher_Join_Flow + */ +require('chromedriver'); +var seleniumWebdriver = require('selenium-webdriver'); +var tap = require('tap'); + +var utils = require('./teacher_registration_utils.js'); +var constants = utils.constants; + +//Set test url through environment variable +//var rootUrl = process.ENV.ROOT_URL || 'http://localhost:8333'; + +//chrome driver +var driver = new seleniumWebdriver.Builder().withCapabilities(seleniumWebdriver.Capabilities.chrome()).build(); + +var fillUsernameSlide = function () { + return utils.fillUsernameSlide(driver, seleniumWebdriver); +}; + +var fillDemographicsSlide = function () { + return utils.fillDemographicsSlide(driver, seleniumWebdriver); +}; + +var fillNameSlide = function () { + return utils.fillNameSlide(driver, seleniumWebdriver); +}; + +var fillPhoneSlide = function () { + return utils.fillPhoneSlide(driver, seleniumWebdriver); +}; + +var fillOrganizationSlide = function () { + return utils.fillOrganizationSlide(driver, seleniumWebdriver); +}; + +tap.plan(2); + +tap.tearDown(function () { + driver.quit(); +}); + +tap.beforeEach(function () { + driver.get('https://scratch.mit.edu/educators/register'); + return fillUsernameSlide() + .then(fillDemographicsSlide) + .then(fillNameSlide) + .then(fillPhoneSlide) + .then(fillOrganizationSlide); +}); + +//Selects Vatican City as the country, and checks that the state dropdown disappears +tap.test('checkStateDropdownOnlyPresentWhenNeeded', function (t) { + var selectCountry = driver.findElement(seleniumWebdriver.By.xpath('//select[@name="address.country"]' + + '/option[@value="va"]')).click(); + driver.findElements(seleniumWebdriver.By.name('address.state')) + .then(function (stateDropdown) { + t.equal(stateDropdown.length, 0); + t.end(); + }); +}); + +tap.test('checkZipCodeRequired', function (t) { + var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath(constants.nextStepXpath)); + var errorMessageXPath = '//input[@name="address.zip"]/following-sibling::' + + 'span[@class="help-block validation-message" and contains(text(),' + + '"This field is required")]'; + nextStepButton.click().then(function () { + driver.findElements(seleniumWebdriver.By.xpath(errorMessageXPath)) + .then(function (validationMessages) { + t.equal(validationMessages.length, 1); + t.end(); + }); + }); +}); + diff --git a/test/integration/teacher-registration/test_teacher_registration_usescratch_step.js b/test/integration/teacher-registration/test_teacher_registration_usescratch_step.js new file mode 100644 index 000000000..511813b21 --- /dev/null +++ b/test/integration/teacher-registration/test_teacher_registration_usescratch_step.js @@ -0,0 +1,94 @@ +/* + * Checks the behavior of the 'use scratch' step in the educators registration process + * + * Test cases: https://github.com/LLK/scratch-www/wiki/Testing-Scratch-www#All_Test_Cases_Teacher_Join_Flow + */ +require('chromedriver'); +var seleniumWebdriver = require('selenium-webdriver'); +var tap = require('tap'); + +var utils = require('./teacher_registration_utils.js'); +var constants = utils.constants; + +//Set test url through environment variable +//var rootUrl = process.ENV.ROOT_URL || 'http://localhost:8333'; + +//chrome driver +var driver = new seleniumWebdriver.Builder().withCapabilities(seleniumWebdriver.Capabilities.chrome()).build(); + +var fillUsernameSlide = function () { + return utils.fillUsernameSlide(driver, seleniumWebdriver); +}; + +var fillDemographicsSlide = function () { + return utils.fillDemographicsSlide(driver, seleniumWebdriver); +}; + +var fillNameSlide = function () { + return utils.fillNameSlide(driver, seleniumWebdriver); +}; + +var fillPhoneSlide = function () { + return utils.fillPhoneSlide(driver, seleniumWebdriver); +}; + +var fillOrganizationSlide = function () { + return utils.fillOrganizationSlide(driver, seleniumWebdriver); +}; + +var fillAddressSlide = function () { + return utils.fillAddressSlide(driver, seleniumWebdriver); +}; + +tap.plan(3); + +tap.tearDown(function () { + driver.quit(); +}); + +tap.beforeEach(function () { + driver.get('https://scratch.mit.edu/educators/register'); + return fillUsernameSlide() + .then(fillDemographicsSlide) + .then(fillNameSlide) + .then(fillPhoneSlide) + .then(fillOrganizationSlide) + .then(fillAddressSlide); +}); + +tap.test('checkCharacterCountIsCorrect', function (t) { + var textarea = driver.findElement(seleniumWebdriver.By.name('useScratch')); + var charCount = driver.findElement(seleniumWebdriver.By.xpath('//p[@class="char-count"]')); + textarea.sendKeys('hello').then(function () { + charCount.getText().then(function (charCountText) { + t.equal(charCountText, '5/300'); + t.end(); + }); + }); +}); + +//Inputs more than 300 characters and checks that the char count gets the class 'overmax' +//which turns the text orange +tap.test('checkCharacterCountTurnsOrangeWhenTooLong', function (t) { + var textarea = driver.findElement(seleniumWebdriver.By.name('useScratch')); + var charCount = driver.findElement(seleniumWebdriver.By.xpath('//p[@class="char-count"]')); + textarea.sendKeys(constants.loremIpsumTextLong).then(function () { + charCount.getAttribute('class').then(function (charCountClasses) { + t.ok(charCountClasses.includes('overmax')); + t.end(); + }); + }); +}); + +tap.test('checkCharacterCountErrorAppersWhenTooLong', function (t) { + var textarea = driver.findElement(seleniumWebdriver.By.name('useScratch')); + var errorMessage = 'Description must be at most 300 characters'; + var errorMessageXPath = '//span[@class="help-block validation-message" and contains(text(),"' + + errorMessage + '")]'; + textarea.sendKeys(constants.loremIpsumTextLong).then(function () { + driver.findElements(seleniumWebdriver.By.xpath(errorMessageXPath)).then(function (validationMessages) { + t.equal(validationMessages.length, 1); + t.end(); + }); + }); +}); From 60565d22c6dc992e2092a76a9867710e38a4fdc3 Mon Sep 17 00:00:00 2001 From: cwillaim Date: Sun, 18 Dec 2016 22:05:38 -0500 Subject: [PATCH 09/32] Remove unused variable in one of the address step tests --- .../test_teacher_registration_address_step.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/test/integration/teacher-registration/test_teacher_registration_address_step.js b/test/integration/teacher-registration/test_teacher_registration_address_step.js index 63770ee72..36d3b623d 100644 --- a/test/integration/teacher-registration/test_teacher_registration_address_step.js +++ b/test/integration/teacher-registration/test_teacher_registration_address_step.js @@ -53,12 +53,14 @@ tap.beforeEach(function () { //Selects Vatican City as the country, and checks that the state dropdown disappears tap.test('checkStateDropdownOnlyPresentWhenNeeded', function (t) { - var selectCountry = driver.findElement(seleniumWebdriver.By.xpath('//select[@name="address.country"]' + - '/option[@value="va"]')).click(); - driver.findElements(seleniumWebdriver.By.name('address.state')) - .then(function (stateDropdown) { - t.equal(stateDropdown.length, 0); - t.end(); + driver.findElement(seleniumWebdriver.By.xpath('//select[@name="address.country"]' + + '/option[@value="va"]')).click() //select Vatican City as the country + .then(function () { + driver.findElements(seleniumWebdriver.By.name('address.state')) + .then(function (stateDropdown) { + t.equal(stateDropdown.length, 0); + t.end(); + }); }); }); From e357a62c2cddb0079593bf84ce7f07cf01cde22e Mon Sep 17 00:00:00 2001 From: cwillaim Date: Wed, 21 Dec 2016 15:18:24 -0500 Subject: [PATCH 10/32] Rename tests for the demographic and organization steps for consistency. Clean up teacher_registration_utils --- .../teacher_registration_utils.js | 3 +-- ...teacher_registration_demographics_step.js} | 8 ++----- ...teacher_registration_organization_step.js} | 24 ++++--------------- 3 files changed, 7 insertions(+), 28 deletions(-) rename test/integration/teacher-registration/{test_teacher_registration_demographics.js => test_teacher_registration_demographics_step.js} (86%) rename test/integration/teacher-registration/{test_teacher_registration_organization.js => test_teacher_registration_organization_step.js} (86%) diff --git a/test/integration/teacher-registration/teacher_registration_utils.js b/test/integration/teacher-registration/teacher_registration_utils.js index 51f3f6ade..8b29c1c1d 100644 --- a/test/integration/teacher-registration/teacher_registration_utils.js +++ b/test/integration/teacher-registration/teacher_registration_utils.js @@ -13,8 +13,7 @@ module.exports.fillUsernameSlide = function (driver, seleniumWebdriver) { var usernameInput = driver.findElement(seleniumWebdriver.By.name('user.username')); var usernamePromise = usernameInput.sendKeys('clipspringer'); var passwordPromise = passwordInput.sendKeys('educators'); - var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath('//button[span[contains(text(),' - + '"Next Step")]]')); + var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath(module.exports.constants.nextStepXpath)); return Promise.all([usernamePromise, passwordPromise]).then(function () { nextStepButton.click().then(function () { driver.wait(seleniumWebdriver.until diff --git a/test/integration/teacher-registration/test_teacher_registration_demographics.js b/test/integration/teacher-registration/test_teacher_registration_demographics_step.js similarity index 86% rename from test/integration/teacher-registration/test_teacher_registration_demographics.js rename to test/integration/teacher-registration/test_teacher_registration_demographics_step.js index e2126f62b..f0164ca32 100644 --- a/test/integration/teacher-registration/test_teacher_registration_demographics.js +++ b/test/integration/teacher-registration/test_teacher_registration_demographics_step.js @@ -29,15 +29,11 @@ tap.beforeEach(function () { tap.test('checkOtherGenderInput', function (t) { var otherGenderRadio = driver.findElement(seleniumWebdriver.By.xpath('//input[@value="other"' + 'and @type="radio"]')); - var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath('//button[span[contains(text(),' - + '"Next Step")]]')); - var errorMessage = 'This field is required'; - var errorMessageXPath = '//span[@class="help-block validation-message" and contains(text(),"' - + errorMessage + '")]'; + var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath(constants.nextStepXpath)); driver.findElement(seleniumWebdriver.By.xpath('//select[@name="user.country"]/option[2]')).click(); otherGenderRadio.click().then(function () { nextStepButton.click().then(function () { - driver.findElements(seleniumWebdriver.By.xpath(errorMessageXPath)) + driver.findElements(seleniumWebdriver.By.xpath(constants.generalErrorMessageXPath)) .then(function (validationMessages) { t.equal(validationMessages.length, 1); t.end(); diff --git a/test/integration/teacher-registration/test_teacher_registration_organization.js b/test/integration/teacher-registration/test_teacher_registration_organization_step.js similarity index 86% rename from test/integration/teacher-registration/test_teacher_registration_organization.js rename to test/integration/teacher-registration/test_teacher_registration_organization_step.js index bda81b693..8cdf97b7e 100644 --- a/test/integration/teacher-registration/test_teacher_registration_organization.js +++ b/test/integration/teacher-registration/test_teacher_registration_organization_step.js @@ -16,22 +16,6 @@ var constants = utils.constants; //chrome driver var driver = new seleniumWebdriver.Builder().withCapabilities(seleniumWebdriver.Capabilities.chrome()).build(); -var fillUsernameSlide = function () { - return utils.fillUsernameSlide(driver, seleniumWebdriver); -}; - -var fillDemographicsSlide = function () { - return utils.fillDemographicsSlide(driver, seleniumWebdriver); -}; - -var fillNameSlide = function () { - return utils.fillNameSlide(driver, seleniumWebdriver); -}; - -var fillPhoneSlide = function () { - return utils.fillPhoneSlide(driver, seleniumWebdriver); -}; - tap.plan(4); tap.tearDown(function () { @@ -40,10 +24,10 @@ tap.tearDown(function () { tap.beforeEach(function () { driver.get('https://scratch.mit.edu/educators/register'); - return fillUsernameSlide() - .then(fillDemographicsSlide) - .then(fillNameSlide) - .then(fillPhoneSlide); + return utils.fillUsernameSlide(driver, seleniumWebdriver) + .then(function () { utils.fillDemographicsSlide(driver, seleniumWebdriver); }) + .then(function () { utils.fillNameSlide(driver, seleniumWebdriver); }) + .then(function () { utils.fillPhoneSlide(driver, seleniumWebdriver); }); }); tap.test('otherFieldRequiredIfChecked', function (t) { From fe2ce902e609ec63464737ef3ea18f94c3fd6c43 Mon Sep 17 00:00:00 2001 From: cwillaim Date: Wed, 21 Dec 2016 16:54:51 -0500 Subject: [PATCH 11/32] Update tests to use an environment value for the url, and consolidate the slide advancement functions. Update utils with better xpath for error messages --- .../teacher_registration_utils.js | 2 +- .../test_teacher_registration_address_step.js | 34 ++++------------ ..._teacher_registration_demographics_step.js | 7 +++- .../test_teacher_registration_name_step.js | 21 ++++------ ..._teacher_registration_organization_step.js | 10 ++--- .../test_teacher_registration_phone_step.js | 23 ++++------- ...test_teacher_registration_username_step.js | 5 ++- ...st_teacher_registration_usescratch_step.js | 40 ++++--------------- 8 files changed, 45 insertions(+), 97 deletions(-) diff --git a/test/integration/teacher-registration/teacher_registration_utils.js b/test/integration/teacher-registration/teacher_registration_utils.js index 8b29c1c1d..424ca4319 100644 --- a/test/integration/teacher-registration/teacher_registration_utils.js +++ b/test/integration/teacher-registration/teacher_registration_utils.js @@ -1,6 +1,6 @@ module.exports.constants = { 'nextStepXpath': '//button[span[contains(text(), "Next Step")]]', - 'generalErrorMessageXpath': '//span[@class="help-block validation-message" and contains(text(),' + 'generalErrorMessageXpath': '//span[@class="help-block validation-message"]/span[contains(text(),' + '"This field is required")]', 'loremIpsumTextLong': 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur viverra' + 'nec mauris efficitur tincidunt. Vestibulum ut diam odio. Cum sociis natoque penatibus et magnis dis' diff --git a/test/integration/teacher-registration/test_teacher_registration_address_step.js b/test/integration/teacher-registration/test_teacher_registration_address_step.js index 36d3b623d..2e2361adc 100644 --- a/test/integration/teacher-registration/test_teacher_registration_address_step.js +++ b/test/integration/teacher-registration/test_teacher_registration_address_step.js @@ -11,31 +11,11 @@ var utils = require('./teacher_registration_utils.js'); var constants = utils.constants; //Set test url through environment variable -//var rootUrl = process.ENV.ROOT_URL || 'http://localhost:8333'; +var rootUrl = process.env.ROOT_URL || 'http://localhost:8333'; //chrome driver var driver = new seleniumWebdriver.Builder().withCapabilities(seleniumWebdriver.Capabilities.chrome()).build(); -var fillUsernameSlide = function () { - return utils.fillUsernameSlide(driver, seleniumWebdriver); -}; - -var fillDemographicsSlide = function () { - return utils.fillDemographicsSlide(driver, seleniumWebdriver); -}; - -var fillNameSlide = function () { - return utils.fillNameSlide(driver, seleniumWebdriver); -}; - -var fillPhoneSlide = function () { - return utils.fillPhoneSlide(driver, seleniumWebdriver); -}; - -var fillOrganizationSlide = function () { - return utils.fillOrganizationSlide(driver, seleniumWebdriver); -}; - tap.plan(2); tap.tearDown(function () { @@ -43,12 +23,12 @@ tap.tearDown(function () { }); tap.beforeEach(function () { - driver.get('https://scratch.mit.edu/educators/register'); - return fillUsernameSlide() - .then(fillDemographicsSlide) - .then(fillNameSlide) - .then(fillPhoneSlide) - .then(fillOrganizationSlide); + driver.get(rootUrl + '/educators/register'); + return utils.fillUsernameSlide(driver, seleniumWebdriver) + .then(utils.fillDemographicsSlide.bind(this, driver, seleniumWebdriver)) + .then(utils.fillNameSlide.bind(this, driver, seleniumWebdriver)) + .then(utils.fillPhoneSlide.bind(this, driver, seleniumWebdriver)) + .then(utils.fillOrganizationSlide.bind(this, driver, seleniumWebdriver)); }); //Selects Vatican City as the country, and checks that the state dropdown disappears diff --git a/test/integration/teacher-registration/test_teacher_registration_demographics_step.js b/test/integration/teacher-registration/test_teacher_registration_demographics_step.js index f0164ca32..ee176a9fb 100644 --- a/test/integration/teacher-registration/test_teacher_registration_demographics_step.js +++ b/test/integration/teacher-registration/test_teacher_registration_demographics_step.js @@ -10,6 +10,9 @@ var tap = require('tap'); var utils = require('./teacher_registration_utils.js'); var constants = utils.constants; +//Set test url through environment variable +var rootUrl = process.env.ROOT_URL || 'http://localhost:8333'; + //chrome driver var driver = new seleniumWebdriver.Builder().withCapabilities(seleniumWebdriver.Capabilities.chrome()).build(); @@ -20,7 +23,7 @@ tap.tearDown(function () { }); tap.beforeEach(function () { - driver.get('https://scratch.mit.edu/educators/register'); + driver.get(rootUrl + '/educators/register'); return utils.fillUsernameSlide(driver, seleniumWebdriver); }); @@ -33,7 +36,7 @@ tap.test('checkOtherGenderInput', function (t) { driver.findElement(seleniumWebdriver.By.xpath('//select[@name="user.country"]/option[2]')).click(); otherGenderRadio.click().then(function () { nextStepButton.click().then(function () { - driver.findElements(seleniumWebdriver.By.xpath(constants.generalErrorMessageXPath)) + driver.findElements(seleniumWebdriver.By.xpath(constants.generalErrorMessageXpath)) .then(function (validationMessages) { t.equal(validationMessages.length, 1); t.end(); diff --git a/test/integration/teacher-registration/test_teacher_registration_name_step.js b/test/integration/teacher-registration/test_teacher_registration_name_step.js index 13cbc22b6..b30bc5ffe 100644 --- a/test/integration/teacher-registration/test_teacher_registration_name_step.js +++ b/test/integration/teacher-registration/test_teacher_registration_name_step.js @@ -10,17 +10,12 @@ var tap = require('tap'); var utils = require('./teacher_registration_utils.js'); var constants = utils.constants; +//Set test url through environment variable +var rootUrl = process.env.ROOT_URL || 'http://localhost:8333'; + //chrome driver var driver = new seleniumWebdriver.Builder().withCapabilities(seleniumWebdriver.Capabilities.chrome()).build(); -var fillUsernameSlide = function () { - return utils.fillUsernameSlide(driver, seleniumWebdriver); -}; - -var fillDemographicsSlide = function () { - return utils.fillDemographicsSlide(driver, seleniumWebdriver); -}; - tap.plan(2); tap.tearDown(function () { @@ -28,16 +23,16 @@ tap.tearDown(function () { }); tap.beforeEach(function () { - driver.get('https://scratch.mit.edu/educators/register'); - return fillUsernameSlide() - .then(fillDemographicsSlide); + driver.get(rootUrl + '/educators/register'); + return utils.fillUsernameSlide(driver, seleniumWebdriver) + .then(utils.fillDemographicsSlide.bind(this, driver, seleniumWebdriver)); }); //attempts to advance the slide without inputting either name, checks that both give the correct error tap.test('checkFirstNameRequired', function (t) { var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath(constants.nextStepXpath)); var errorMessageXPath = '//input[@name="user.name.first"]/following-sibling::' - + 'span[@class="help-block validation-message" and contains(text(),' + + 'span[@class="help-block validation-message"]/span[contains(text(),' + '"This field is required")]'; nextStepButton.click().then(function () { driver.findElements(seleniumWebdriver.By.xpath(errorMessageXPath)) @@ -52,7 +47,7 @@ tap.test('checkFirstNameRequired', function (t) { tap.test('checkLastNameRequired', function (t) { var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath(constants.nextStepXpath)); var errorMessageXPath = '//input[@name="user.name.last"]/following-sibling::' - + 'span[@class="help-block validation-message" and contains(text(),' + + 'span[@class="help-block validation-message"]/span[contains(text(),' + '"This field is required")]'; nextStepButton.click().then(function () { driver.findElements(seleniumWebdriver.By.xpath(errorMessageXPath)) diff --git a/test/integration/teacher-registration/test_teacher_registration_organization_step.js b/test/integration/teacher-registration/test_teacher_registration_organization_step.js index 8cdf97b7e..01ed6a099 100644 --- a/test/integration/teacher-registration/test_teacher_registration_organization_step.js +++ b/test/integration/teacher-registration/test_teacher_registration_organization_step.js @@ -11,7 +11,7 @@ var utils = require('./teacher_registration_utils.js'); var constants = utils.constants; //Set test url through environment variable -//var rootUrl = process.ENV.ROOT_URL || 'http://localhost:8333'; +var rootUrl = process.env.ROOT_URL || 'http://localhost:8333'; //chrome driver var driver = new seleniumWebdriver.Builder().withCapabilities(seleniumWebdriver.Capabilities.chrome()).build(); @@ -23,11 +23,11 @@ tap.tearDown(function () { }); tap.beforeEach(function () { - driver.get('https://scratch.mit.edu/educators/register'); + driver.get(rootUrl + '/educators/register'); return utils.fillUsernameSlide(driver, seleniumWebdriver) - .then(function () { utils.fillDemographicsSlide(driver, seleniumWebdriver); }) - .then(function () { utils.fillNameSlide(driver, seleniumWebdriver); }) - .then(function () { utils.fillPhoneSlide(driver, seleniumWebdriver); }); + .then(utils.fillDemographicsSlide.bind(this, driver, seleniumWebdriver)) + .then(utils.fillNameSlide.bind(this, driver, seleniumWebdriver)) + .then(utils.fillPhoneSlide.bind(this, driver, seleniumWebdriver)); }); tap.test('otherFieldRequiredIfChecked', function (t) { diff --git a/test/integration/teacher-registration/test_teacher_registration_phone_step.js b/test/integration/teacher-registration/test_teacher_registration_phone_step.js index 460d3f0d5..2456567ac 100644 --- a/test/integration/teacher-registration/test_teacher_registration_phone_step.js +++ b/test/integration/teacher-registration/test_teacher_registration_phone_step.js @@ -9,21 +9,12 @@ var tap = require('tap'); var utils = require('./teacher_registration_utils.js'); +//Set test url through environment variable +var rootUrl = process.env.ROOT_URL || 'http://localhost:8333'; + //chrome driver var driver = new seleniumWebdriver.Builder().withCapabilities(seleniumWebdriver.Capabilities.chrome()).build(); -var fillUsernameSlide = function () { - return utils.fillUsernameSlide(driver, seleniumWebdriver); -}; - -var fillDemographicsSlide = function () { - return utils.fillDemographicsSlide(driver, seleniumWebdriver); -}; - -var fillNameSlide = function () { - return utils.fillNameSlide(driver, seleniumWebdriver); -}; - tap.plan(1); tap.tearDown(function () { @@ -31,10 +22,10 @@ tap.tearDown(function () { }); tap.beforeEach(function () { - driver.get('https://scratch.mit.edu/educators/register'); - return fillUsernameSlide() - .then(fillDemographicsSlide) - .then(fillNameSlide); + driver.get(rootUrl + '/educators/register'); + return utils.fillUsernameSlide(driver, seleniumWebdriver) + .then(utils.fillDemographicsSlide.bind(this, driver, seleniumWebdriver)) + .then(utils.fillNameSlide.bind(this, driver, seleniumWebdriver)); }); //inputs an invalid phone number and checks that the correct error message appears diff --git a/test/integration/teacher-registration/test_teacher_registration_username_step.js b/test/integration/teacher-registration/test_teacher_registration_username_step.js index 99366673d..f8fb7e9a8 100644 --- a/test/integration/teacher-registration/test_teacher_registration_username_step.js +++ b/test/integration/teacher-registration/test_teacher_registration_username_step.js @@ -8,6 +8,9 @@ require('chromedriver'); var seleniumWebdriver = require('selenium-webdriver'); var tap = require('tap'); +//Set test url through environment variable +var rootUrl = process.env.ROOT_URL || 'http://localhost:8333'; + //chrome driver var driver = new seleniumWebdriver.Builder().withCapabilities(seleniumWebdriver.Capabilities.chrome()).build(); @@ -18,7 +21,7 @@ tap.tearDown(function () { }); tap.beforeEach(function () { - return driver.get('https://scratch.mit.edu/educators/register'); + return driver.get(rootUrl + '/educators/register'); }); //an error message should appear for a username less than 3 characters long diff --git a/test/integration/teacher-registration/test_teacher_registration_usescratch_step.js b/test/integration/teacher-registration/test_teacher_registration_usescratch_step.js index 511813b21..97235697c 100644 --- a/test/integration/teacher-registration/test_teacher_registration_usescratch_step.js +++ b/test/integration/teacher-registration/test_teacher_registration_usescratch_step.js @@ -11,35 +11,11 @@ var utils = require('./teacher_registration_utils.js'); var constants = utils.constants; //Set test url through environment variable -//var rootUrl = process.ENV.ROOT_URL || 'http://localhost:8333'; +var rootUrl = process.env.ROOT_URL || 'http://localhost:8333'; //chrome driver var driver = new seleniumWebdriver.Builder().withCapabilities(seleniumWebdriver.Capabilities.chrome()).build(); -var fillUsernameSlide = function () { - return utils.fillUsernameSlide(driver, seleniumWebdriver); -}; - -var fillDemographicsSlide = function () { - return utils.fillDemographicsSlide(driver, seleniumWebdriver); -}; - -var fillNameSlide = function () { - return utils.fillNameSlide(driver, seleniumWebdriver); -}; - -var fillPhoneSlide = function () { - return utils.fillPhoneSlide(driver, seleniumWebdriver); -}; - -var fillOrganizationSlide = function () { - return utils.fillOrganizationSlide(driver, seleniumWebdriver); -}; - -var fillAddressSlide = function () { - return utils.fillAddressSlide(driver, seleniumWebdriver); -}; - tap.plan(3); tap.tearDown(function () { @@ -47,13 +23,13 @@ tap.tearDown(function () { }); tap.beforeEach(function () { - driver.get('https://scratch.mit.edu/educators/register'); - return fillUsernameSlide() - .then(fillDemographicsSlide) - .then(fillNameSlide) - .then(fillPhoneSlide) - .then(fillOrganizationSlide) - .then(fillAddressSlide); + driver.get(rootUrl + '/educators/register'); + return utils.fillUsernameSlide(driver, seleniumWebdriver) + .then(utils.fillDemographicsSlide.bind(this, driver, seleniumWebdriver)) + .then(utils.fillNameSlide.bind(this, driver, seleniumWebdriver)) + .then(utils.fillPhoneSlide.bind(this, driver, seleniumWebdriver)) + .then(utils.fillOrganizationSlide.bind(this, driver, seleniumWebdriver)) + .then(utils.fillAddressSlide.bind(this, driver, seleniumWebdriver)); }); tap.test('checkCharacterCountIsCorrect', function (t) { From 38bdc78d8ad4f14b62eb765e88d108a2e8dd7eb7 Mon Sep 17 00:00:00 2001 From: cwillaim Date: Wed, 21 Dec 2016 17:18:07 -0500 Subject: [PATCH 12/32] Update tests with the right xpath for error messages (nested span for translation) --- .../test_teacher_registration_address_step.js | 2 +- .../test_teacher_registration_organization_step.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/integration/teacher-registration/test_teacher_registration_address_step.js b/test/integration/teacher-registration/test_teacher_registration_address_step.js index 2e2361adc..2a0188422 100644 --- a/test/integration/teacher-registration/test_teacher_registration_address_step.js +++ b/test/integration/teacher-registration/test_teacher_registration_address_step.js @@ -47,7 +47,7 @@ tap.test('checkStateDropdownOnlyPresentWhenNeeded', function (t) { tap.test('checkZipCodeRequired', function (t) { var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath(constants.nextStepXpath)); var errorMessageXPath = '//input[@name="address.zip"]/following-sibling::' - + 'span[@class="help-block validation-message" and contains(text(),' + + 'span[@class="help-block validation-message"]/span[contains(text(),' + '"This field is required")]'; nextStepButton.click().then(function () { driver.findElements(seleniumWebdriver.By.xpath(errorMessageXPath)) diff --git a/test/integration/teacher-registration/test_teacher_registration_organization_step.js b/test/integration/teacher-registration/test_teacher_registration_organization_step.js index 01ed6a099..ba98e5baa 100644 --- a/test/integration/teacher-registration/test_teacher_registration_organization_step.js +++ b/test/integration/teacher-registration/test_teacher_registration_organization_step.js @@ -48,7 +48,7 @@ tap.test('otherFieldRequiredIfChecked', function (t) { tap.test('checkOrganizationFieldRequired', function (t) { var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath(constants.nextStepXpath)); var errorMessageXPath = '//input[@name="organization.name"]/following-sibling::' - + 'span[@class="help-block validation-message" and contains(text(),' + + 'span[@class="help-block validation-message"]/span[contains(text(),' + '"This field is required")]'; nextStepButton.click().then(function () { driver.findElements(seleniumWebdriver.By.xpath(errorMessageXPath)) @@ -62,7 +62,7 @@ tap.test('checkOrganizationFieldRequired', function (t) { tap.test('checkRoleFieldRequired', function (t) { var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath(constants.nextStepXpath)); var errorMessageXPath = '//input[@name="organization.title"]/following-sibling::' - + 'span[@class="help-block validation-message" and contains(text(),' + + 'span[@class="help-block validation-message"]/span[contains(text(),' + '"This field is required")]'; nextStepButton.click().then(function () { driver.findElements(seleniumWebdriver.By.xpath(errorMessageXPath)) From d7e63aadee96e30a152d5e17e5cda0f9062c83e3 Mon Sep 17 00:00:00 2001 From: The_Grits Date: Fri, 14 Apr 2017 10:08:17 -0400 Subject: [PATCH 13/32] Fix gh-875: Double Search Bar --- src/views/search/search.jsx | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/views/search/search.jsx b/src/views/search/search.jsx index 87b4ce78d..cbe9f1707 100644 --- a/src/views/search/search.jsx +++ b/src/views/search/search.jsx @@ -107,16 +107,6 @@ var Search = injectIntl(React.createClass({

-
-
-
From 5cc8911b0409540f8213b1b022da9fea139ac501 Mon Sep 17 00:00:00 2001 From: The_Grits Date: Fri, 14 Apr 2017 11:13:46 -0400 Subject: [PATCH 14/32] Thanks Travis --- src/views/search/search.jsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/views/search/search.jsx b/src/views/search/search.jsx index cbe9f1707..809aa40a7 100644 --- a/src/views/search/search.jsx +++ b/src/views/search/search.jsx @@ -8,8 +8,6 @@ var api = require('../../lib/api'); var Page = require('../../components/page/www/page.jsx'); var TitleBanner = require('../../components/title-banner/title-banner.jsx'); -var Form = require('../../components/forms/form.jsx'); -var Input = require('../../components/forms/input.jsx'); var Button = require('../../components/forms/button.jsx'); var Tabs = require('../../components/tabs/tabs.jsx'); var Grid = require('../../components/grid/grid.jsx'); @@ -99,8 +97,6 @@ var Search = injectIntl(React.createClass({ return allTab; }, render: function () { - var formatMessage = this.props.intl.formatMessage; - return (
From 8f827d812a8dd2def99b31219540d3ae51a34208 Mon Sep 17 00:00:00 2001 From: The_Grits Date: Wed, 26 Apr 2017 23:13:24 -0400 Subject: [PATCH 15/32] Re-add min-width for mobile --- src/components/dropdown/dropdown.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/components/dropdown/dropdown.scss b/src/components/dropdown/dropdown.scss index 0a76e3cda..f6a7e979c 100644 --- a/src/components/dropdown/dropdown.scss +++ b/src/components/dropdown/dropdown.scss @@ -84,3 +84,7 @@ } } } + +@media only screen and (max-width: $mobile - 1) { + min-width: 160px; +} From 83baa2ada609e324a43c7bb483393f678f23d917 Mon Sep 17 00:00:00 2001 From: The_Grits Date: Wed, 26 Apr 2017 23:14:50 -0400 Subject: [PATCH 16/32] import frameless --- src/components/dropdown/dropdown.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/dropdown/dropdown.scss b/src/components/dropdown/dropdown.scss index f6a7e979c..6ac508d31 100644 --- a/src/components/dropdown/dropdown.scss +++ b/src/components/dropdown/dropdown.scss @@ -1,4 +1,5 @@ @import "../../colors"; +@import "../../frameless"; .dropdown { display: none; From 0a747f68bdeb5b645a6a17d919da8a098508e016 Mon Sep 17 00:00:00 2001 From: The_Grits Date: Mon, 1 May 2017 11:24:55 -0400 Subject: [PATCH 17/32] Add DD Liu Resolves #1296 --- src/views/credits/credits.jsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/views/credits/credits.jsx b/src/views/credits/credits.jsx index 496aeca42..810abdfbb 100644 --- a/src/views/credits/credits.jsx +++ b/src/views/credits/credits.jsx @@ -51,6 +51,11 @@ var Credits = React.createClass({ Saskia Avatar Saskia Leggett + +
  • + DD Avatar + DD Liu +
  • Shruti Avatar From 5822b2facb64c29bc9b8491908d06dca554fa2da Mon Sep 17 00:00:00 2001 From: The_Grits Date: Tue, 2 May 2017 13:38:00 -0400 Subject: [PATCH 18/32] Update Firefox Link --- src/views/faq/l10n.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/faq/l10n.json b/src/views/faq/l10n.json index 37f05cef1..4f2763cc6 100644 --- a/src/views/faq/l10n.json +++ b/src/views/faq/l10n.json @@ -13,7 +13,7 @@ "faq.makeGameTitle":"How do I make a game or animation with Scratch?", "faq.makeGameBody":"Check out the help page to see lots of ways to get started with Scratch. Or just dive in to the project editor.", "faq.requirementsTitle":"What are the system requirements for Scratch?", - "faq.requirementsBody":"To run Scratch 2, you need to be using (1) a Mac, Linux, or Windows computer; (2) a version of Adobe Flash Player released on or after June 15, 2016; (3) a relatively recent web browser: one of the latest two versions of Chrome (Mac, Windows, or Linux), Firefox (Mac or Windows only), Safari (Mac or Windows only), Edge (Windows only), or Internet Explorer 10+ (Windows only). If your computer doesn’t meet these requirements, you can try downloading and installing Scratch 1.4, which you can still use to share projects to the Scratch 2 website. We do not support Chromium.", + "faq.requirementsBody":"To run Scratch 2, you need to be using (1) a Mac, Linux, or Windows computer; (2) a version of Adobe Flash Player released on or after June 15, 2016; (3) a relatively recent web browser: one of the latest two versions of Chrome (Mac, Windows, or Linux), Firefox (Mac or Windows only), Safari (Mac or Windows only), Edge (Windows only), or Internet Explorer 10+ (Windows only). If your computer doesn’t meet these requirements, you can try downloading and installing Scratch 1.4, which you can still use to share projects to the Scratch 2 website. We do not support Chromium.", "faq.offlineTitle":"Do you have a downloadable version so I can create and view projects offline?", "faq.offlineBody":"The Scratch 2 offline editor allows you to create Scratch projects without an internet connection. You can download Scratch 2 from the website. You can also still use Scratch 1.4. Note: You can have both Scratch 1.4 and 2 on your computer.", "faq.uploadOldTitle":"Can I still upload projects created with older versions of Scratch to the website?", From a837c05ac7a15a1d271d729632ad2481913043b5 Mon Sep 17 00:00:00 2001 From: The_Grits Date: Tue, 2 May 2017 15:29:00 -0400 Subject: [PATCH 19/32] Remove localized Microsoft links --- src/views/faq/l10n.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/faq/l10n.json b/src/views/faq/l10n.json index 4f2763cc6..8faec524e 100644 --- a/src/views/faq/l10n.json +++ b/src/views/faq/l10n.json @@ -13,7 +13,7 @@ "faq.makeGameTitle":"How do I make a game or animation with Scratch?", "faq.makeGameBody":"Check out the help page to see lots of ways to get started with Scratch. Or just dive in to the project editor.", "faq.requirementsTitle":"What are the system requirements for Scratch?", - "faq.requirementsBody":"To run Scratch 2, you need to be using (1) a Mac, Linux, or Windows computer; (2) a version of Adobe Flash Player released on or after June 15, 2016; (3) a relatively recent web browser: one of the latest two versions of Chrome (Mac, Windows, or Linux), Firefox (Mac or Windows only), Safari (Mac or Windows only), Edge (Windows only), or Internet Explorer 10+ (Windows only). If your computer doesn’t meet these requirements, you can try downloading and installing Scratch 1.4, which you can still use to share projects to the Scratch 2 website. We do not support Chromium.", + "faq.requirementsBody":"To run Scratch 2, you need to be using (1) a Mac, Linux, or Windows computer; (2) a version of Adobe Flash Player released on or after June 15, 2016; (3) a relatively recent web browser: one of the latest two versions of Chrome (Mac, Windows, or Linux), Firefox (Mac or Windows only), Safari (Mac or Windows only), Edge (Windows only), or Internet Explorer 10+ (Windows only). If your computer doesn’t meet these requirements, you can try downloading and installing Scratch 1.4, which you can still use to share projects to the Scratch 2 website. We do not support Chromium.", "faq.offlineTitle":"Do you have a downloadable version so I can create and view projects offline?", "faq.offlineBody":"The Scratch 2 offline editor allows you to create Scratch projects without an internet connection. You can download Scratch 2 from the website. You can also still use Scratch 1.4. Note: You can have both Scratch 1.4 and 2 on your computer.", "faq.uploadOldTitle":"Can I still upload projects created with older versions of Scratch to the website?", From 5083614f48d7e6587be6635056226aa92ca181a2 Mon Sep 17 00:00:00 2001 From: St19_Galla Date: Tue, 2 May 2017 15:33:31 -0400 Subject: [PATCH 20/32] add US to faq --- src/views/faq/l10n.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/faq/l10n.json b/src/views/faq/l10n.json index 37f05cef1..bf173b65e 100644 --- a/src/views/faq/l10n.json +++ b/src/views/faq/l10n.json @@ -136,7 +136,7 @@ "faq.edBody":"Scratch Teacher Accounts are special user accounts on Scratch that have access to additional features to facilitate the creation and management of student accounts. ScratchEd Accounts are accounts on the ScratchEd community, a separate website (managed by the Harvard Graduate School of Education) where educators share stories, exchange resources, ask questions, and meet other Scratch educators.", "faq.dataTitle":"What data does Scratch collect about students?", "faq.dataBody":"When a student first signs up on Scratch, we ask for basic demographic data including gender, age (birth month and year), country, and an email address for verification. This data is used (in aggregated form) in research studies intended to improve our understanding of how people learn with Scratch. When an educator uses a Scratch Teacher Account to create student accounts in bulk, students are not required to provide an email address for account setup.", - "faq.lawComplianceTitle":"Is Scratch 2.0 (online version) compliant with local and federal data privacy laws?", - "faq.lawComplianceBody":"Scratch cares deeply about the privacy of students and of all individuals who use our platform. We have in place physical and electronic procedures to protect the information we collect on the Scratch website. Although we are not in a position to offer contractual guarantees with each entity that uses our free educational product, we are in compliance with all federal laws that are applicable to MIT, a 501(c)(3) organization and the entity that created and maintains Scratch. We encourage you to read the Scratch Privacy Policy for more information.", + "faq.lawComplianceTitle":"Is Scratch 2.0 (online version) compliant with United States local and federal data privacy laws?", + "faq.lawComplianceBody":"Scratch cares deeply about the privacy of students and of all individuals who use our platform. We have in place physical and electronic procedures to protect the information we collect on the Scratch website. Although we are not in a position to offer contractual guarantees with each entity that uses our free educational product, we are in compliance with all United States federal laws that are applicable to MIT, a 501(c)(3) organization and the entity that created and maintains Scratch. We encourage you to read the Scratch Privacy Policy for more information.", "faq.schoolsMoreInfo":"For more more questions about Teacher Accounts, see the Teacher Account FAQ" } From f82d5f834112bf6fc0bfa7db48e080647ad617f7 Mon Sep 17 00:00:00 2001 From: St19_Galla Date: Tue, 2 May 2017 15:34:57 -0400 Subject: [PATCH 21/32] add US to educators --- src/views/teachers/faq/l10n.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/teachers/faq/l10n.json b/src/views/teachers/faq/l10n.json index 818e73ce2..24e0a293e 100644 --- a/src/views/teachers/faq/l10n.json +++ b/src/views/teachers/faq/l10n.json @@ -36,8 +36,8 @@ "teacherfaq.studentDiscussBody": "Yes, you can engage in discussions with other teachers at ScratchEd, an online community for Scratch educators. Check out their forums to join conversations about a number of topics, including but not limited to Teacher Accounts. ScratchEd is developed and supported by the Harvard Graduate School of Education.", "teacherfaq.studentDataTitle": "What data does Scratch collect about students?", "teacherfaq.studentDataBody": "When a student first signs up on Scratch, we ask for basic demographic data including gender, age (birth month and year), country, and an email address for verification. This data is used (in aggregated form) in research studies intended to improve our understanding of how people learn with Scratch. When an educator uses a Scratch Teacher Account to create student accounts in bulk, students are not required to provide an email address for account setup.", - "teacherfaq.studentPrivacyLawsTitle": "Is Scratch 2.0 (online version) compliant with local and federal data privacy laws?", - "teacherfaq.studentPrivacyLawsBody": "Scratch cares deeply about the privacy of students and of all individuals who use our platform. We have in place physical and electronic procedures to protect the information we collect on the Scratch website. Although we are not in a position to offer contractual guarantees with each entity that uses our free educational product, we are in compliance with all federal laws that are applicable to MIT, a 501(c)(3) organization and the entity that created and maintains Scratch. We encourage you to read the Scratch Privacy Policy for more information.", + "teacherfaq.studentPrivacyLawsTitle": "Is Scratch 2.0 (online version) compliant with United States local and federal data privacy laws?", + "teacherfaq.studentPrivacyLawsBody": "Scratch cares deeply about the privacy of students and of all individuals who use our platform. We have in place physical and electronic procedures to protect the information we collect on the Scratch website. Although we are not in a position to offer contractual guarantees with each entity that uses our free educational product, we are in compliance with all United States federal laws that are applicable to MIT, a 501(c)(3) organization and the entity that created and maintains Scratch. We encourage you to read the Scratch Privacy Policy for more information.", "teacherfaq.commTitle": "Community", "teacherfaq.commHiddenTitle": "Can I create a hidden class?", From 3a27b1d2e2b1712b42bf5d1fff638257acb74f07 Mon Sep 17 00:00:00 2001 From: Matthew Taylor Date: Wed, 3 May 2017 14:21:27 -0400 Subject: [PATCH 22/32] Apply english strings as default translations This fixes #1303 by ensuring that, if a language does not have a string, the default will be applied. --- bin/build-locales | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/bin/build-locales b/bin/build-locales index 96d2c2783..0e1d040e7 100755 --- a/bin/build-locales +++ b/bin/build-locales @@ -32,12 +32,13 @@ } ''' */ -var fs = require('fs'); -var path = require('path'); -var merge = require('lodash.merge'); var async = require('async'); +var fs = require('fs'); var languages = require('../languages.json'); var localizedUrls = require('./lib/localized-urls'); +var merge = require('lodash.merge'); +var defaults = require('lodash.defaults'); +var path = require('path'); var routes = require('../src/routes.json'); // ----------------------------------------------------------------------------- @@ -215,6 +216,7 @@ async.forEachLimit(views, 5, function (view, cb) { } try { viewLocales[isoCode] = merge({}, generalLocales[isoCode], JSON.parse(data)); + defaults(viewLocales[isoCode], viewLocales['en']); } catch (e) { return cb(e); } From 8ae401e8612ea3184f16dce0c20bae63354f89f1 Mon Sep 17 00:00:00 2001 From: The_Grits Date: Wed, 3 May 2017 15:58:14 -0400 Subject: [PATCH 23/32] Move mobile rule --- src/components/dropdown/dropdown.scss | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/dropdown/dropdown.scss b/src/components/dropdown/dropdown.scss index 6ac508d31..3fc351fc3 100644 --- a/src/components/dropdown/dropdown.scss +++ b/src/components/dropdown/dropdown.scss @@ -84,8 +84,8 @@ content: ""; } } -} - -@media only screen and (max-width: $mobile - 1) { - min-width: 160px; + + @media only screen and (max-width: $mobile - 1) { + min-width: 160px; + } } From f589ed0f4df468b567db079bd16e777f1542a119 Mon Sep 17 00:00:00 2001 From: The_Grits Date: Mon, 8 May 2017 10:10:24 -0400 Subject: [PATCH 24/32] Update dropdown.scss --- src/components/dropdown/dropdown.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/dropdown/dropdown.scss b/src/components/dropdown/dropdown.scss index 3fc351fc3..66af291ef 100644 --- a/src/components/dropdown/dropdown.scss +++ b/src/components/dropdown/dropdown.scss @@ -85,7 +85,7 @@ } } - @media only screen and (max-width: $mobile - 1) { + @media only screen and (max-width: $tablet - 1) { min-width: 160px; } } From 87da9f3d8a9970db40bb7b6161451ce1a02ec4f3 Mon Sep 17 00:00:00 2001 From: Matthew Taylor Date: Thu, 18 May 2017 13:57:49 -0400 Subject: [PATCH 25/32] Use regex in express patterns to limit scope This fixes https://github.com/LLK/scratchr2/issues/3798 by using regex in expressjs patterns, and then also parsing those regex patterns for fastly, such that we can limit the scope of the regex created for a specific view --- bin/lib/fastly-config-methods.js | 3 ++- src/routes.json | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/bin/lib/fastly-config-methods.js b/bin/lib/fastly-config-methods.js index f20091103..3a3d7581c 100644 --- a/bin/lib/fastly-config-methods.js +++ b/bin/lib/fastly-config-methods.js @@ -37,7 +37,8 @@ var FastlyConfigMethods = { * all :arguments become .+? */ expressPatternToRegex: function (pattern) { - return pattern.replace(/(:[^/]+)/gi, '.+?'); + pattern = pattern.replace(/(:\w+)(\([^\)]+\))/gi, '$2'); + return pattern.replace(/(:\w+)/gi, '.+?'); }, /* diff --git a/src/routes.json b/src/routes.json index ab771a933..b0ae23107 100644 --- a/src/routes.json +++ b/src/routes.json @@ -127,7 +127,7 @@ }, { "name": "explore", - "pattern": "^/explore/:projects/:all/?$", + "pattern": "^/explore/:projects(projects|studios)/:all/?$", "routeAlias": "/explore(?!/ajax)", "view": "explore/explore", "title": "Explore" From 1a83b343879820da0b3938e04147148942a50db0 Mon Sep 17 00:00:00 2001 From: Matthew Taylor Date: Mon, 22 May 2017 15:41:28 -0400 Subject: [PATCH 26/32] Change explore endpoint For scratch-api#250 --- src/views/explore/explore.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/explore/explore.jsx b/src/views/explore/explore.jsx index d41871d3a..1c6ac3ca0 100644 --- a/src/views/explore/explore.jsx +++ b/src/views/explore/explore.jsx @@ -70,7 +70,7 @@ var Explore = injectIntl(React.createClass({ var qText = '&q=' + this.props.acceptableTabs[this.props.category] || '*'; api({ - uri: '/search/' + this.props.itemType + + uri: '/explore/' + this.props.itemType + '?limit=' + this.props.loadNumber + '&offset=' + this.state.offset + '&language=' + this.props.intl.locale + From bab863cd14afc154b6d211017461e06455873761 Mon Sep 17 00:00:00 2001 From: Matthew Taylor Date: Tue, 23 May 2017 13:16:46 -0400 Subject: [PATCH 27/32] Apply defaults for untranslated pages too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit since we’ll need them for nav/footer strings --- bin/build-locales | 1 + 1 file changed, 1 insertion(+) diff --git a/bin/build-locales b/bin/build-locales index 0e1d040e7..9be275d33 100755 --- a/bin/build-locales +++ b/bin/build-locales @@ -208,6 +208,7 @@ async.forEachLimit(views, 5, function (view, cb) { process.stdout.write('No translations for ' + view + isoCode + ', using english\n'); } viewLocales[isoCode] = merge({}, generalLocales[isoCode], defaultLocales[view]); + defaults(viewLocales[isoCode], generalLocales['en']); } return cb(); } else { From 373c7784c6c69b91b754da2d05b6f9cf3ebe5b26 Mon Sep 17 00:00:00 2001 From: Matthew Taylor Date: Wed, 24 May 2017 08:42:25 -0400 Subject: [PATCH 28/32] Change conference date Fixes #1322 --- src/views/conference/2017/index/index.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/conference/2017/index/index.jsx b/src/views/conference/2017/index/index.jsx index e93e8627b..b1f0b3137 100644 --- a/src/views/conference/2017/index/index.jsx +++ b/src/views/conference/2017/index/index.jsx @@ -379,14 +379,14 @@ var ConferenceSplash = React.createClass({ {' - '} Date: Wed, 24 May 2017 08:51:28 -0400 Subject: [PATCH 29/32] make comment explanatory of new style Thanks @rschamp! --- bin/lib/fastly-config-methods.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bin/lib/fastly-config-methods.js b/bin/lib/fastly-config-methods.js index 3a3d7581c..d84756262 100644 --- a/bin/lib/fastly-config-methods.js +++ b/bin/lib/fastly-config-methods.js @@ -33,8 +33,10 @@ var FastlyConfigMethods = { }, /* - * Translate an express-style pattern e.g. /path/:arg/ to a regex - * all :arguments become .+? + * Translate an express-style pattern to regex one in two ways: + * + * 1. /path/:arg/ – all :arg's become .+? + * 2. /path/:arg([regex]) – :arg is removed, leaving just /path/([regex]) */ expressPatternToRegex: function (pattern) { pattern = pattern.replace(/(:\w+)(\([^\)]+\))/gi, '$2'); From bb5657587e3d35a3a8980f5e60d62ae39d2918da Mon Sep 17 00:00:00 2001 From: jwzimmer Date: Wed, 24 May 2017 10:11:07 -0700 Subject: [PATCH 30/32] Fix issue/gh 1313 by adding test to check for missing string ids (#1314) * test for missing string ids * Switched to multiple tests (1 per intl page) and changed comment style * Add back in comment I accidentally . Add comment for the function. * Change variable names, call function in-line --- test/localization/check_string_ids.js | 73 +++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 test/localization/check_string_ids.js diff --git a/test/localization/check_string_ids.js b/test/localization/check_string_ids.js new file mode 100644 index 000000000..de4491d21 --- /dev/null +++ b/test/localization/check_string_ids.js @@ -0,0 +1,73 @@ +/** + * Tests whether any page in www has any languages which are missing string IDs + * - checks every language against the list of english IDs for that page + * - test fails if the length of the list of languages missing any IDs is not 0 + * - if the test fails, you can see which pages/ languages/ IDs are causing the failure: + * - Object.keys(pagesWithLanguagesMissingIds) gives you a list + * of the pages which had languages with missing IDs + * - pagesWithLanguagesMissingIds['pageName.intl.js'] gives you an object + * with languages as keys and the missing IDs as values + */ + +var path = require('path'); +var fs = require('fs'); +var tap = require('tap'); + +/** + * To get the files (containing message IDs and localized strings for each page in www) + * from the intl directory + */ +var intlDirPath = path.resolve(__dirname, '../../intl/'); +var intlFiles = fs.readdirSync(intlDirPath); + +/** + * Tells tap whether the test should pass or fail for a given file. + * @param {string} fileName + * @param {Object} missingMessageId + * @param {Object} pagesMissingIds + */ +function noMissingStrings (fileName, missingMessageId, pagesMissingIds) { + if (Object.keys(missingMessageId).length == 0) { + tap.pass(); + } + else { + tap.fail(fileName + ' is missing string IDs'); + pagesMissingIds[fileName] = []; + pagesMissingIds[fileName].push(missingMessageId); + } +} + +var pagesWithLanguagesMissingIds = {}; + +for (var i in intlFiles) { + var file = intlFiles[i]; + var filePath = path.resolve(__dirname, '../../intl/' + file); + var pageMessagesString = fs.readFileSync(filePath,'utf8'); + + /** + * To make the string of the file of the page.intl.js back into useable objects + */ + var window = {}; + var pageMessages = eval(pageMessagesString); + + /** + * The goal is to compare the IDs for each language to the IDs for English, + * so we need the list of IDs for the given page in English + */ + var englishIdList = window._messages.en; + + var messageIdNotInLanguage = {}; + + for (var languageKey in pageMessages) { + var currentLanguageObject = pageMessages[languageKey]; + for (var messageId in englishIdList) { + if (! (messageId in currentLanguageObject)) { + if (typeof messageIdNotInLanguage[languageKey] == 'undefined') { + messageIdNotInLanguage[languageKey] = []; + } + messageIdNotInLanguage[languageKey].push(messageId); + } + } + } + noMissingStrings(file, messageIdNotInLanguage, pagesWithLanguagesMissingIds); +} From 56b46fd861dff3fccbd5f4afbbc535533d80bd25 Mon Sep 17 00:00:00 2001 From: jwzimmer Date: Wed, 24 May 2017 10:12:37 -0700 Subject: [PATCH 31/32] Issue/gh 1013 remove end to end testing package dependencies from repo (#1319) * remove selenium from package.json * Add package.json & README for the integration directory * add newline at end of file * add chromedriver dependency --- package.json | 1 - test/integration/README.md | 19 +++++++++++++++++++ test/integration/package.json | 6 ++++++ 3 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 test/integration/README.md create mode 100644 test/integration/package.json diff --git a/package.json b/package.json index 739184e40..3b59d4cc7 100644 --- a/package.json +++ b/package.json @@ -84,7 +84,6 @@ "sass-lint": "1.5.1", "sass-loader": "2.0.1", "scratchr2_translations": "git://github.com/LLK/scratchr2_translations.git#master", - "selenium-webdriver": "2.44.0", "slick-carousel": "1.5.8", "source-map-support": "0.3.2", "style-loader": "0.12.3", diff --git a/test/integration/README.md b/test/integration/README.md new file mode 100644 index 000000000..413bbdc6a --- /dev/null +++ b/test/integration/README.md @@ -0,0 +1,19 @@ +# Requirements + +* Selenium + * See this directory's package.json +* TAP + * In the scratch-www repo's package.json +* Currently, we also require that you download [Chromedriver](https://sites.google.com/a/chromium.org/chromedriver/), but this will be replaced by Saucelabs imminently + +# Running the tests + +* By default, tests run against our Staging instance, but you can pass in a different location if you want to run the tests against e.g. your local build + +## Using tap +* Run all tests in the smoke-testing directory from the command-line: `$ make smoke` +* To run a single file from the command-line: `$ node_modules/.bin/tap ./test/integration/smoke-testing/filename.js --timeout=3600` + * The timeout var is for the length of the entire tap test-suite; if you are getting a timeout error, you may need to adjust this value (some of the Selenium tests take a while to run) + +## Using sauce +* We're still working on setting this up; more info coming shortly \ No newline at end of file diff --git a/test/integration/package.json b/test/integration/package.json new file mode 100644 index 000000000..db2942f14 --- /dev/null +++ b/test/integration/package.json @@ -0,0 +1,6 @@ +{ + "testDependencies": { + "selenium-webdriver": "2.44.0", + "chromedriver": "2.27.0" + } +} From e7c04044ae7ea5ff0fb5c9531a9864e7992fc2ea Mon Sep 17 00:00:00 2001 From: Matthew Taylor Date: Wed, 24 May 2017 17:33:55 -0400 Subject: [PATCH 32/32] remove `?$` from search expressjs expression MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I don’t see a reason for this to be here, and it’s breaking the build with the explore change --- src/routes.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes.json b/src/routes.json index b0ae23107..966010e6a 100644 --- a/src/routes.json +++ b/src/routes.json @@ -196,7 +196,7 @@ }, { "name": "search", - "pattern": "^/search/:projects?$/?$", + "pattern": "^/search/:projects/?$", "routeAlias": "/search", "view": "search/search", "title": "Search"