mirror of
https://github.com/scratchfoundation/scratch-www.git
synced 2024-11-22 15:17:53 -05:00
Move tap tests into legacy folders
This commit is contained in:
parent
5e868575c6
commit
dd6b7ba4e2
28 changed files with 2377 additions and 6 deletions
14
package.json
14
package.json
|
@ -4,13 +4,13 @@
|
|||
"description": "Standalone WWW client for Scratch",
|
||||
"scripts": {
|
||||
"start": "node ./dev-server/index.js",
|
||||
"test": "npm run test:lint && npm run build && npm run test:tap",
|
||||
"test": "npm run test:lint && npm run build && npm run test:unit:tap",
|
||||
"test:lint": "eslint . --ext .js,.jsx,.json",
|
||||
"test:smoke": "tap ./test/integration/smoke-testing/*.js --timeout=3600 --no-coverage -R classic",
|
||||
"test:smoke:verbose": "tap ./test/integration/smoke-testing/*.js --timeout=3600 --no-coverage -R spec",
|
||||
"test:smoke:sauce": "SMOKE_REMOTE=true tap ./test/integration/smoke-testing/*.js --timeout=60000 --no-coverage -R classic",
|
||||
"test:tap": "tap ./test/{unit,localization}/*.js --no-coverage -R classic",
|
||||
"test:coverage": "tap ./test/{unit,localization}/*.js --coverage --coverage-report=lcov",
|
||||
"test:smoke": "tap ./test/integration-legacy/smoke-testing/*.js --timeout=3600 --no-coverage -R classic",
|
||||
"test:smoke:verbose": "tap ./test/integration-legacy/smoke-testing/*.js --timeout=3600 --no-coverage -R spec",
|
||||
"test:smoke:sauce": "SMOKE_REMOTE=true tap ./test/integration-legacy/smoke-testing/*.js --timeout=60000 --no-coverage -R classic",
|
||||
"test:unit:tap": "tap ./test/{unit-legacy,localization-legacy}/*.js --no-coverage -R classic",
|
||||
"test:coverage": "tap ./test/{unit-legacy,localization-legacy}/*.js --coverage --coverage-report=lcov",
|
||||
"build": "npm run clean && npm run translate && webpack --bail",
|
||||
"clean": "rm -rf ./build && rm -rf ./intl && mkdir -p build && mkdir -p intl",
|
||||
"deploy": "npm run deploy:s3 && npm run deploy:fastly",
|
||||
|
@ -60,6 +60,7 @@
|
|||
"babel-preset-react": "6.22.0",
|
||||
"bowser": "1.9.4",
|
||||
"cheerio": "1.0.0-rc.2",
|
||||
"chromedriver": "75.1.0",
|
||||
"classnames": "2.2.5",
|
||||
"cookie": "0.2.2",
|
||||
"copy-webpack-plugin": "0.2.0",
|
||||
|
@ -115,6 +116,7 @@
|
|||
"sass-loader": "6.0.6",
|
||||
"scratch-gui": "0.1.0-prerelease.20190711011201",
|
||||
"scratch-l10n": "latest",
|
||||
"selenium-webdriver": "3.6.0",
|
||||
"slick-carousel": "1.6.0",
|
||||
"source-map-support": "0.3.2",
|
||||
"style-loader": "0.12.3",
|
||||
|
|
35
test/integration-legacy/README.md
Normal file
35
test/integration-legacy/README.md
Normal file
|
@ -0,0 +1,35 @@
|
|||
# Requirements
|
||||
|
||||
* Selenium
|
||||
* See this directory's package.json
|
||||
* TAP
|
||||
* In the scratch-www repo's package.json
|
||||
* [Chromedriver](https://sites.google.com/a/chromium.org/chromedriver/)
|
||||
|
||||
# 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
|
||||
* Tests can be run using Saucelabs, an online service that can test browser/os combinations remotely. Currently all tests are written for use for chrome on mac.
|
||||
|
||||
## Using tap
|
||||
* Run all tests in the smoke-testing directory from the command-line: `$ SMOKE_USERNAME=username SMOKE_PASSWORD=password ROOT_URL=https://scratch.mit.edu npm run smoke`
|
||||
* To run a single file from the command-line: `$ SMOKE_USERNAME=username SMOKE_PASSWORD=password ROOT_URL=https://scratch.mit.edu 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)
|
||||
* To run tests using saucelabs run this command `$ SMOKE_USERNAME=username SMOKE_PASSWORD=password SAUCE_USERNAME=saucelabsUsername SAUCE_ACCESS_KEY=saucelabsAccessKey ROOT_URL=https://scratch.mit.edu npm run smoke-sauce`
|
||||
|
||||
|
||||
### Configuration
|
||||
|
||||
| Variable | Default | Description |
|
||||
| --------------------- | --------------------- | --------------------------------------------------------- |
|
||||
| `ROOT_URL` | `scratch.ly` | Location you want to run the tests against |
|
||||
| `SMOKE_USERNAME` | `None` | Username for Scratch user you're signing in with to test |
|
||||
| `SMOKE_PASSWORD` | `None` | Password for Scratch user you're signing in with to test |
|
||||
| `SMOKE_REMOTE` | `false` | Tests with Sauce Labs or not. True if running smoke-sauce |
|
||||
| `SMOKE_HEADLESS` | `false` | Run browser in headless mode. Flaky at the moment |
|
||||
| `SAUCE_USERNAME` | `None` | Username for your Sauce Labs account |
|
||||
| `SAUCE_ACCESS_KEY` | `None` | Access Key for Sauce Labs found under User Settings |
|
||||
|
||||
|
||||
## Using Saucelabs
|
||||
* You will need a Saucelabs account in order to use it for testing. To find the Access Key, click your username and select User Settings from the dropdown menu. Near the bottom of the page is your access key that you can copy and use in the command line.
|
168
test/integration-legacy/selenium-helpers.js
Normal file
168
test/integration-legacy/selenium-helpers.js
Normal file
|
@ -0,0 +1,168 @@
|
|||
const webdriver = require('selenium-webdriver');
|
||||
const bindAll = require('lodash.bindall');
|
||||
require('chromedriver');
|
||||
|
||||
const headless = process.env.SMOKE_HEADLESS || false;
|
||||
const remote = process.env.SMOKE_REMOTE || false;
|
||||
const ci = process.env.CI || false;
|
||||
const buildID = process.env.TRAVIS_BUILD_NUMBER;
|
||||
const {SAUCE_USERNAME, SAUCE_ACCESS_KEY} = process.env;
|
||||
const {By, Key, until} = webdriver;
|
||||
|
||||
const DEFAULT_TIMEOUT_MILLISECONDS = 20 * 1000;
|
||||
|
||||
class SeleniumHelper {
|
||||
constructor () {
|
||||
bindAll(this, [
|
||||
'buildDriver',
|
||||
'clickButton',
|
||||
'clickCss',
|
||||
'clickText',
|
||||
'clickXpath',
|
||||
'dragFromXpathToXpath',
|
||||
'findByCss',
|
||||
'findByXpath',
|
||||
'findText',
|
||||
'getKey',
|
||||
'getDriver',
|
||||
'getLogs',
|
||||
'getSauceDriver',
|
||||
'urlMatches',
|
||||
'waitUntilGone'
|
||||
]);
|
||||
}
|
||||
buildDriver (name) {
|
||||
if (remote === 'true'){
|
||||
let nameToUse;
|
||||
if (ci === 'true'){
|
||||
nameToUse = 'travis ' + buildID + ' : ' + name;
|
||||
} else {
|
||||
nameToUse = name;
|
||||
}
|
||||
this.driver = this.getSauceDriver(SAUCE_USERNAME, SAUCE_ACCESS_KEY, nameToUse);
|
||||
} else {
|
||||
this.driver = this.getDriver();
|
||||
}
|
||||
return this.driver;
|
||||
}
|
||||
|
||||
getDriver () {
|
||||
const chromeCapabilities = webdriver.Capabilities.chrome();
|
||||
let args = [];
|
||||
if (headless) {
|
||||
args.push('--headless');
|
||||
args.push('window-size=1024,1680');
|
||||
args.push('--no-sandbox');
|
||||
}
|
||||
chromeCapabilities.set('chromeOptions', {args});
|
||||
let driver = new webdriver.Builder()
|
||||
.forBrowser('chrome')
|
||||
.withCapabilities(chromeCapabilities)
|
||||
.build();
|
||||
return driver;
|
||||
}
|
||||
|
||||
getSauceDriver (username, accessKey, name) {
|
||||
// Driver configs can be generated with the Sauce Platform Configurator
|
||||
// https://wiki.saucelabs.com/display/DOCS/Platform+Configurator
|
||||
let driverConfig = {
|
||||
browserName: 'chrome',
|
||||
platform: 'macOS 10.14',
|
||||
version: '75.0'
|
||||
};
|
||||
var driver = new webdriver.Builder()
|
||||
.withCapabilities({
|
||||
browserName: driverConfig.browserName,
|
||||
platform: driverConfig.platform,
|
||||
version: driverConfig.version,
|
||||
username: username,
|
||||
accessKey: accessKey,
|
||||
name: name
|
||||
})
|
||||
.usingServer(`http://${username}:${accessKey
|
||||
}@ondemand.saucelabs.com:80/wd/hub`)
|
||||
.build();
|
||||
return driver;
|
||||
}
|
||||
|
||||
getKey (keyName) {
|
||||
return Key[keyName];
|
||||
}
|
||||
|
||||
findByXpath (xpath, timeoutMessage = `findByXpath timed out for path: ${xpath}`) {
|
||||
return this.driver.wait(until.elementLocated(By.xpath(xpath)), DEFAULT_TIMEOUT_MILLISECONDS, timeoutMessage)
|
||||
.then(el => (
|
||||
this.driver.wait(el.isDisplayed(), DEFAULT_TIMEOUT_MILLISECONDS, `${xpath} is not visible`)
|
||||
.then(() => el)
|
||||
));
|
||||
}
|
||||
|
||||
waitUntilGone (element) {
|
||||
return this.driver.wait(until.stalenessOf(element));
|
||||
}
|
||||
|
||||
clickXpath (xpath) {
|
||||
return this.findByXpath(xpath).then(el => el.click());
|
||||
}
|
||||
|
||||
clickText (text) {
|
||||
return this.clickXpath(`//*[contains(text(), '${text}')]`);
|
||||
}
|
||||
|
||||
findText (text) {
|
||||
return this.driver.wait(until.elementLocated(By.xpath(`//*[contains(text(), '${text}')]`), 5 * 1000));
|
||||
}
|
||||
|
||||
clickButton (text) {
|
||||
return this.clickXpath(`//button[contains(text(), '${text}')]`);
|
||||
}
|
||||
|
||||
findByCss (css) {
|
||||
return this.driver.wait(until.elementLocated(By.css(css), 1000 * 5));
|
||||
}
|
||||
|
||||
clickCss (css) {
|
||||
return this.findByCss(css).then(el => el.click());
|
||||
}
|
||||
|
||||
dragFromXpathToXpath (startXpath, endXpath) {
|
||||
return this.findByXpath(startXpath).then(startEl => {
|
||||
return this.findByXpath(endXpath).then(endEl => {
|
||||
return this.driver.actions()
|
||||
.dragAndDrop(startEl, endEl)
|
||||
.perform();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
urlMatches (regex) {
|
||||
return this.driver.wait(until.urlMatches(regex), 1000 * 5);
|
||||
}
|
||||
|
||||
getLogs (whitelist) {
|
||||
return this.driver.manage()
|
||||
.logs()
|
||||
.get('browser')
|
||||
.then((entries) => {
|
||||
return entries.filter((entry) => {
|
||||
const message = entry.message;
|
||||
for (let i = 0; i < whitelist.length; i++) {
|
||||
if (message.indexOf(whitelist[i]) !== -1) {
|
||||
// eslint-disable-next-line no-console
|
||||
// console.warn('Ignoring whitelisted error: ' + whitelist[i]);
|
||||
return false;
|
||||
} else if (entry.level !== 'SEVERE') {
|
||||
// eslint-disable-next-line no-console
|
||||
// console.warn('Ignoring non-SEVERE entry: ' + message);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = SeleniumHelper;
|
30
test/integration-legacy/smoke-testing/test-join.js
Normal file
30
test/integration-legacy/smoke-testing/test-join.js
Normal file
|
@ -0,0 +1,30 @@
|
|||
const SeleniumHelper = require('../selenium-helpers.js');
|
||||
const helper = new SeleniumHelper();
|
||||
|
||||
var tap = require('tap');
|
||||
const test = tap.test;
|
||||
|
||||
const driver = helper.buildDriver('www-smoke test-login-failures');
|
||||
|
||||
const {
|
||||
clickText,
|
||||
findByXpath
|
||||
} = helper;
|
||||
|
||||
var rootUrl = process.env.ROOT_URL || 'https://scratch.ly';
|
||||
|
||||
tap.plan(1);
|
||||
|
||||
tap.tearDown(function () {
|
||||
driver.quit();
|
||||
});
|
||||
|
||||
tap.beforeEach(function () {
|
||||
return driver.get(rootUrl);
|
||||
});
|
||||
|
||||
test('Clicking Join Scratch opens scratchr2 iframe', t => {
|
||||
clickText('Join Scratch')
|
||||
.then(() => findByXpath('//iframe[contains(@class, "mod-registration")]'))
|
||||
.then(() => t.end());
|
||||
});
|
89
test/integration-legacy/smoke-testing/test-login-failures.js
Normal file
89
test/integration-legacy/smoke-testing/test-login-failures.js
Normal file
|
@ -0,0 +1,89 @@
|
|||
const SeleniumHelper = require('../selenium-helpers.js');
|
||||
const helper = new SeleniumHelper();
|
||||
|
||||
var tap = require('tap');
|
||||
const test = tap.test;
|
||||
|
||||
const webdriver = require('selenium-webdriver');
|
||||
const driver = helper.buildDriver('www-smoke test-login-failures');
|
||||
|
||||
const {
|
||||
findByCss,
|
||||
clickCss
|
||||
} = helper;
|
||||
|
||||
var until = webdriver.until;
|
||||
|
||||
var username = process.env.SMOKE_USERNAME;
|
||||
var password = process.env.SMOKE_PASSWORD;
|
||||
|
||||
var rootUrl = process.env.ROOT_URL || 'https://scratch.ly';
|
||||
var url = rootUrl + '/users/' + username;
|
||||
|
||||
tap.plan(3);
|
||||
|
||||
tap.tearDown(function () {
|
||||
driver.quit();
|
||||
});
|
||||
|
||||
tap.beforeEach(function () {
|
||||
return driver.get(url);
|
||||
});
|
||||
|
||||
test('Trying to sign in with no password using scratchr2 navbar', t => {
|
||||
var nonsenseusername = Math.random().toString(36)
|
||||
.replace(/[^a-z]+/g, '')
|
||||
.substr(0, 5);
|
||||
clickCss('.dropdown-toggle')
|
||||
.then(() => findByCss('form#login input#login_dropdown_username'))
|
||||
.then((element) => element.sendKeys(nonsenseusername))
|
||||
.then(() => clickCss('form#login button'))
|
||||
.then(() => findByCss('form#login .error'))
|
||||
.then((element) => {
|
||||
driver.wait(until.elementIsVisible(element));
|
||||
return element;
|
||||
})
|
||||
.then((element) => element.getText())
|
||||
.then((text) => t.match(text, 'This field is required',
|
||||
'"This field is required" error should be displayed'))
|
||||
.then(() => t.end());
|
||||
});
|
||||
|
||||
test('Trying to sign in with the wrong username using scratchr2 navbar', t => {
|
||||
var nonsenseusername = Math.random().toString(36)
|
||||
.replace(/[^a-z]+/g, '')
|
||||
.substr(0, 5);
|
||||
clickCss('.dropdown-toggle')
|
||||
.then(() => findByCss('form#login input#login_dropdown_username'))
|
||||
.then((element) => element.sendKeys(nonsenseusername))
|
||||
.then(() => findByCss('form#login input.wide.password'))
|
||||
.then((element) => element.sendKeys(password))
|
||||
.then(() => clickCss('form#login button'))
|
||||
.then(() => findByCss('form#login .error'))
|
||||
.then((element) => {
|
||||
driver.wait(until.elementIsVisible(element));
|
||||
return element;
|
||||
})
|
||||
.then((element) => element.getText())
|
||||
.then((text) => t.match(text, 'Incorrect username or password.',
|
||||
'"Incorrect username or password" error should be displayed'))
|
||||
.then(() => t.end());
|
||||
});
|
||||
|
||||
test('Trying to sign in with the wrong password using scratchr2 navbar', t => {
|
||||
clickCss('.dropdown-toggle')
|
||||
.then(() => findByCss('form#login input#login_dropdown_username'))
|
||||
.then((element) => element.sendKeys(username))
|
||||
.then(() => findByCss('form#login input.wide.password'))
|
||||
.then((element) => element.sendKeys('nonsensepassword'))
|
||||
.then(() => clickCss('form#login button'))
|
||||
.then(() => findByCss('form#login .error'))
|
||||
.then((element) => {
|
||||
driver.wait(until.elementIsVisible(element));
|
||||
return element;
|
||||
})
|
||||
.then((element) => element.getText())
|
||||
.then((text) => t.match(text, 'Incorrect username or password.',
|
||||
'"Incorrect username or password" error should be displayed'))
|
||||
.then(() => t.end());
|
||||
});
|
151
test/integration-legacy/smoke-testing/test-my-stuff.js
Normal file
151
test/integration-legacy/smoke-testing/test-my-stuff.js
Normal file
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
* Tests signing in & My Stuff according to smoke-tests at:
|
||||
*
|
||||
* https://github.com/LLK/scratchr2/wiki/Smoke-Testing-Test-Cases
|
||||
*
|
||||
*/
|
||||
|
||||
const SeleniumHelper = require('../selenium-helpers.js');
|
||||
const helper = new SeleniumHelper();
|
||||
|
||||
var tap = require('tap');
|
||||
const test = tap.test;
|
||||
|
||||
const driver = helper.buildDriver('www-smoke test-my-stuff');
|
||||
|
||||
const {
|
||||
clickText,
|
||||
findByXpath,
|
||||
clickXpath,
|
||||
clickButton
|
||||
} = helper;
|
||||
|
||||
var username = process.env.SMOKE_USERNAME;
|
||||
var password = process.env.SMOKE_PASSWORD;
|
||||
|
||||
var rootUrl = process.env.ROOT_URL || 'https://scratch.ly';
|
||||
var url = rootUrl + '/users/' + username;
|
||||
|
||||
tap.plan(7);
|
||||
|
||||
tap.tearDown(function () {
|
||||
driver.quit();
|
||||
});
|
||||
|
||||
tap.beforeEach(function () {
|
||||
return driver.get(url)
|
||||
.then(() => clickText('Sign in'))
|
||||
.then(() => findByXpath('//input[@id="login_dropdown_username"]'))
|
||||
.then((element) => element.sendKeys(username))
|
||||
.then(() => findByXpath('//input[@name="password"]'))
|
||||
.then((element) => element.sendKeys(password))
|
||||
.then(() => clickButton('Sign in'));
|
||||
});
|
||||
|
||||
tap.afterEach(function () {
|
||||
return clickXpath('//span[@class="user-name dropdown-toggle"]')
|
||||
.then(() => clickXpath('//li[@id="logout"] '))
|
||||
.then(() => findByXpath('//div[@class="title-banner intro-banner"]'));
|
||||
});
|
||||
|
||||
test('Sign in to Scratch using scratchr2 navbar', t => {
|
||||
findByXpath('//li[contains(@class, "logged-in-user")' +
|
||||
'and contains(@class, "dropdown")]/span')
|
||||
.then((element) => element.getText('span'))
|
||||
.then((text) => t.match(text.toLowerCase(), username.substring(0, 10).toLowerCase(),
|
||||
'first part of username should be displayed in navbar'))
|
||||
.then(() => t.end());
|
||||
});
|
||||
|
||||
test('Sign in to Scratch & verify My Stuff structure (tabs, title)', t => {
|
||||
clickXpath('//a[contains(@class, "mystuff-icon")]')
|
||||
.then(() => findByXpath('//div[@class="box-head"]/h2'))
|
||||
.then((element) => element.getText('h2'))
|
||||
.then((text) => t.equal('My Stuff', text, 'title should be My Stuff'))
|
||||
.then(() => findByXpath('//li[@data-tab="projects"]/a'))
|
||||
.then((element) => element.getText('a'))
|
||||
.then((text) => t.match(text, 'All Projects', 'All Projects tab should be present'))
|
||||
.then(() => findByXpath('//li[@data-tab="shared"]/a'))
|
||||
.then((element) => element.getText('a'))
|
||||
.then((text) => t.match(text, 'Shared Projects', 'Shared Projects tab should be present'))
|
||||
.then(() => findByXpath('//li[@data-tab="unshared"]/a'))
|
||||
.then((element) => element.getText('a'))
|
||||
.then((text) => t.match(text, 'Unshared Projects', 'Unshared Projects tab should be present'))
|
||||
.then(() => findByXpath('//li[@data-tab="galleries"]/a'))
|
||||
.then((element) => element.getText('a'))
|
||||
.then((text) => t.match(text, 'My Studios', 'My Studios tab should be present'))
|
||||
.then(() => findByXpath('//li[@data-tab="trash"]/a'))
|
||||
.then((element) => element.getText('a'))
|
||||
.then((text) => t.match(text, 'Trash', 'Trash tab should be present'))
|
||||
.then(() => t.end());
|
||||
});
|
||||
|
||||
test('clicking See Inside should take you to the editor', t => {
|
||||
clickXpath('//a[contains(@class, "mystuff-icon")]')
|
||||
.then(() => findByXpath('//a[@data-control="edit"]'))
|
||||
.then((element) => element.getText('span'))
|
||||
.then((text) => t.equal(text, 'See inside', 'there should be a "See inside" button'))
|
||||
.then(() => clickXpath('//a[@data-control="edit"]'))
|
||||
.then(() => driver.getCurrentUrl())
|
||||
.then(function (u) {
|
||||
var expectedUrl = '/editor';
|
||||
t.equal(u.substr(-expectedUrl.length), expectedUrl, 'after clicking, the URL should end in #editor');
|
||||
})
|
||||
.then(() => driver.get(url))
|
||||
.then(() => t.end());
|
||||
});
|
||||
|
||||
test('clicking a project title should take you to the project page', t => {
|
||||
clickXpath('//a[contains(@class, "mystuff-icon")]')
|
||||
.then(() => clickXpath('//a[@data-control="edit"]'))
|
||||
.then(() => driver.getCurrentUrl())
|
||||
.then(function (u) {
|
||||
var expectedUrlRegExp = new RegExp('/projects/.*[0-9].*/?');
|
||||
t.match(u, expectedUrlRegExp, 'after clicking, the URL should end in projects/PROJECT_ID/');
|
||||
})
|
||||
.then(() => driver.get(url))
|
||||
.then(() => t.end());
|
||||
});
|
||||
|
||||
test('Add To button should bring up a list of studios', t => {
|
||||
clickXpath('//a[contains(@class, "mystuff-icon")]')
|
||||
.then(() => clickXpath('//div[@id="sidebar"]/ul/li[@data-tab="shared"]'))
|
||||
.then(() => findByXpath('//div[@data-control="add-to"]'))
|
||||
.then((element) => element.getText('span'))
|
||||
.then((text) => t.equal(text, 'Add to', 'there should be an "Add to" button'))
|
||||
.then(() => clickXpath('//div[@data-control="add-to"]'))
|
||||
.then(() => findByXpath('//div[@class="dropdown-menu"]/ul/li'))
|
||||
.then((element) => element.getText('span'))
|
||||
.then(function (text) {
|
||||
var expectedRegExp = new RegExp('.+');
|
||||
t.match(text, expectedRegExp, 'the dropdown menu should have at least 1 text item in it');
|
||||
})
|
||||
.then(() => t.end());
|
||||
});
|
||||
|
||||
test('+ New Studio button should take you to the studio page', {skip: true}, t => {
|
||||
clickXpath('//a[contains(@class, "mystuff-icon")]')
|
||||
.then(() => clickXpath('//form[@id="new_studio"]/button[@type="submit"]'))
|
||||
.then(() => findByXpath('//div[@id="show-add-project"]'))
|
||||
.then((element) => element.getText('span'))
|
||||
.then((text) => t.equal(text, 'Add projects', 'there should be an "Add projects" button'))
|
||||
.then(() => driver.getCurrentUrl())
|
||||
.then(function (u) {
|
||||
var expectedUrlRegExp = new RegExp('/studios/.*[0-9].*/?');
|
||||
t.match(u, expectedUrlRegExp,
|
||||
'after clicking the + New Studio, the URL should end in studios/STUDIO_ID');
|
||||
})
|
||||
.then(() => t.end());
|
||||
});
|
||||
|
||||
test('+ New Project button should open the editor', {skip: true}, t => {
|
||||
clickXpath('//a[contains(@class, "mystuff-icon")]')
|
||||
.then(() => clickText('+ New Project'))
|
||||
.then(() => driver.getCurrentUrl())
|
||||
.then(function (u) {
|
||||
var expectedUrlRegExp = new RegExp('/projects/editor');
|
||||
t.match(u, expectedUrlRegExp,
|
||||
'after clicking, the URL should end in projects/editor');
|
||||
})
|
||||
.then(() => t.end());
|
||||
});
|
305
test/integration-legacy/smoke-testing/test_footer_links.js
Normal file
305
test/integration-legacy/smoke-testing/test_footer_links.js
Normal file
|
@ -0,0 +1,305 @@
|
|||
/*
|
||||
* Checks that the links in the footer on the homepage have the right URLs to redirect to
|
||||
*
|
||||
* Test cases: https://github.com/LLK/scratch-www/wiki/Most-Important-Workflows
|
||||
*/
|
||||
|
||||
const SeleniumHelper = require('../selenium-helpers.js');
|
||||
const helper = new SeleniumHelper();
|
||||
|
||||
const tap = require('tap');
|
||||
|
||||
const webdriver = require('selenium-webdriver');
|
||||
const driver = helper.buildDriver('www-smoke test_footer_links');
|
||||
|
||||
const rootUrl = process.env.ROOT_URL || 'https://scratch.ly';
|
||||
|
||||
// timeout for each test; timeout for suite set at command line level
|
||||
const options = {timeout: 30000};
|
||||
|
||||
// number of tests in the plan
|
||||
tap.plan(24);
|
||||
|
||||
tap.tearDown(function () {
|
||||
// quit the instance of the browser
|
||||
driver.quit();
|
||||
});
|
||||
|
||||
tap.beforeEach(function () {
|
||||
// load the page with the driver
|
||||
return driver.get(rootUrl);
|
||||
});
|
||||
|
||||
// Function clicks the link and returns the url of the resulting page
|
||||
|
||||
const clickFooterLinks = function (linkText) {
|
||||
return driver.wait(webdriver.until.elementLocated(webdriver.By.id('footer')))
|
||||
.then(function (element) {
|
||||
return element.findElement(webdriver.By.linkText(linkText));
|
||||
})
|
||||
.then(function (element) {
|
||||
return element.click();
|
||||
})
|
||||
.then(function () {
|
||||
return driver.getCurrentUrl();
|
||||
});
|
||||
};
|
||||
|
||||
// ==== ABOUT SCRATCH column ====
|
||||
|
||||
// ABOUT SCRATCH
|
||||
tap.test('clickAboutScratchLink', options, t => {
|
||||
const linkText = 'About Scratch';
|
||||
const expectedHref = '/about';
|
||||
clickFooterLinks(linkText).then(url => {
|
||||
// the href should be at the end of the URL
|
||||
t.equal(url.substr(-expectedHref.length), expectedHref);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
// FOR PARENTS
|
||||
tap.test('clickForParentsLink', options, t => {
|
||||
const linkText = 'For Parents';
|
||||
const expectedHref = '/parents/';
|
||||
clickFooterLinks(linkText).then(url => {
|
||||
t.equal(url.substr(-expectedHref.length), expectedHref);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
// FOR EDUCATORS
|
||||
tap.test('clickForEducatorsLink', options, t => {
|
||||
const linkText = 'For Educators';
|
||||
const expectedHref = '/educators';
|
||||
clickFooterLinks(linkText).then(url => {
|
||||
t.equal(url.substr(-expectedHref.length), expectedHref);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
// FOR DEVELOPERS
|
||||
tap.test('clickForDevelopersScratchLink', options, t => {
|
||||
const linkText = 'For Developers';
|
||||
const expectedHref = '/developers';
|
||||
clickFooterLinks(linkText).then(url => {
|
||||
t.equal(url.substr(-expectedHref.length), expectedHref);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
// CREDITS
|
||||
tap.test('clickCreditsLink', options, t => {
|
||||
const linkText = 'Credits';
|
||||
const expectedHref = '/credits';
|
||||
clickFooterLinks(linkText).then(url => {
|
||||
t.equal(url.substr(-expectedHref.length), expectedHref);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
// JOBS
|
||||
tap.test('clickJobsLink', options, t => {
|
||||
const linkText = 'Jobs';
|
||||
const expectedHref = '/jobs';
|
||||
clickFooterLinks(linkText).then(url => {
|
||||
t.equal(url.substr(-expectedHref.length), expectedHref);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
// PRESS
|
||||
tap.test('clickPressLink', options, t => {
|
||||
const linkText = 'Press';
|
||||
const expectedUrl = 'https://www.scratchfoundation.org/media-kit/';
|
||||
clickFooterLinks(linkText).then(url => {
|
||||
t.equal(url, expectedUrl);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
// ==== COMMUNITY column ====
|
||||
|
||||
// COMMUNITY GUIDELINES
|
||||
tap.test('clickCommunityGuidelinesLink', options, t => {
|
||||
const linkText = 'Community Guidelines';
|
||||
const expectedHref = '/community_guidelines';
|
||||
clickFooterLinks(linkText).then(url => {
|
||||
t.equal(url.substr(-expectedHref.length), expectedHref);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
// DISCUSSION FORUMS
|
||||
tap.test('clickDiscussionForumsLink', options, t => {
|
||||
const linkText = 'Discussion Forums';
|
||||
const expectedHref = '/discuss/';
|
||||
clickFooterLinks(linkText).then(url => {
|
||||
t.equal(url.substr(-expectedHref.length), expectedHref);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
// SCRATCH WIKI test has been removed.
|
||||
|
||||
// STATISTICS
|
||||
tap.test('clickStatisticsLink', options, t => {
|
||||
const linkText = 'Statistics';
|
||||
const expectedHref = '/statistics/';
|
||||
clickFooterLinks(linkText).then(url => {
|
||||
t.equal(url.substr(-expectedHref.length), expectedHref);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
// ==== SUPPORT column ====
|
||||
|
||||
// IDEAS PAGE
|
||||
tap.test('clickIdeasPageLink', options, t => {
|
||||
const linkText = 'Ideas';
|
||||
const expectedHref = '/ideas';
|
||||
clickFooterLinks(linkText).then(url => {
|
||||
t.equal(url.substr(-expectedHref.length), expectedHref);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
// FAQ
|
||||
tap.test('clickFAQLink', options, t => {
|
||||
const linkText = 'FAQ';
|
||||
const expectedHref = '/info/faq';
|
||||
clickFooterLinks(linkText).then(url => {
|
||||
t.equal(url.substr(-expectedHref.length), expectedHref);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
// OFFLINE EDITOR
|
||||
tap.test('clickOfflineEditorLink', options, t => {
|
||||
const linkText = 'Offline Editor';
|
||||
const expectedHref = '/download';
|
||||
clickFooterLinks(linkText).then(url => {
|
||||
t.equal(url.substr(-expectedHref.length), expectedHref);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
// CONTACT US
|
||||
tap.test('clickContactUsLink', options, t => {
|
||||
const linkText = 'Contact Us';
|
||||
const expectedHref = '/contact-us/';
|
||||
clickFooterLinks(linkText).then(url => {
|
||||
t.equal(url.substr(-expectedHref.length), expectedHref);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
// skip this test since it points to an external site
|
||||
// SCRATCH STORE
|
||||
tap.test('clickScratchStoreLink', {skip: true}, t => {
|
||||
const linkText = 'Scratch Store';
|
||||
const expectedUrl = 'https://scratch-foundation.myshopify.com/';
|
||||
clickFooterLinks(linkText).then(url => {
|
||||
t.equal(url, expectedUrl);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
// skip this test since it points to an external site
|
||||
// DONATE
|
||||
tap.test('clickDonateLink', {skip: true}, t => {
|
||||
const linkText = 'Donate';
|
||||
const expectedUrl = 'https://secure.donationpay.org/scratchfoundation/';
|
||||
clickFooterLinks(linkText).then(url => {
|
||||
t.equal(url, expectedUrl);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
// ==== LEGAL column ====
|
||||
|
||||
// TERMS OF USE
|
||||
tap.test('clickTermsOfUseLink', options, t => {
|
||||
const linkText = 'Terms of Use';
|
||||
const expectedHref = '/terms_of_use';
|
||||
clickFooterLinks(linkText).then(url => {
|
||||
t.equal(url.substr(-expectedHref.length), expectedHref);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
// PRIVACY POLICY
|
||||
tap.test('clickPrivacyPolicyLink', options, t => {
|
||||
const linkText = 'Privacy Policy';
|
||||
const expectedHref = '/privacy_policy';
|
||||
clickFooterLinks(linkText).then(url => {
|
||||
t.equal(url.substr(-expectedHref.length), expectedHref);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
// DMCA
|
||||
tap.test('clickDMCALink', options, t => {
|
||||
const linkText = 'DMCA';
|
||||
const expectedHref = '/DMCA';
|
||||
clickFooterLinks(linkText).then(url => {
|
||||
t.equal(url.substr(-expectedHref.length), expectedHref);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
// ==== SCRATCH FAMILY column ====
|
||||
|
||||
// skip this test since it points to an external site
|
||||
// SCRATCH ED (SCRATCHED)
|
||||
tap.test('clickScratchEdLink', {skip: true}, t => {
|
||||
const linkText = 'ScratchEd';
|
||||
const expectedUrl = 'http://scratched.gse.harvard.edu/';
|
||||
clickFooterLinks(linkText).then(url => {
|
||||
t.equal(url, expectedUrl);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
// skip this test since it points to an external site
|
||||
// SCRATCH JR (SCRATCHJR)
|
||||
tap.test('clickScratchJrLink', {skip: true}, t => {
|
||||
const linkText = 'ScratchJr';
|
||||
const expectedUrl = 'https://www.scratchjr.org/';
|
||||
clickFooterLinks(linkText).then(url => {
|
||||
t.equal(url, expectedUrl);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
// skip this test since it points to an external site
|
||||
// SCRATCH DAY
|
||||
tap.test('clickScratchDayLink', {skip: true}, t => {
|
||||
const linkText = 'Scratch Day';
|
||||
const expectedUrl = 'https://day.scratch.mit.edu/';
|
||||
clickFooterLinks(linkText).then(url => {
|
||||
t.equal(url, expectedUrl);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
// SCRATCH CONFERENCE
|
||||
tap.test('clickScratchConferenceLink', options, t => {
|
||||
const linkText = 'Scratch Conference';
|
||||
const expectedHref = '/conference/20';
|
||||
clickFooterLinks(linkText).then(url => {
|
||||
t.match(url.substr(-(expectedHref.length + 2)), expectedHref);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
// skip this test since it points to an external site
|
||||
// SCRATCH FOUNDATION
|
||||
tap.test('clickScratchFoundationLink', {skip: true}, t => {
|
||||
const linkText = 'Scratch Foundation';
|
||||
const expectedUrl = 'https://www.scratchfoundation.org/';
|
||||
clickFooterLinks(linkText).then(url => {
|
||||
t.equal(url, expectedUrl);
|
||||
t.end();
|
||||
});
|
||||
});
|
123
test/integration-legacy/smoke-testing/test_navbar_links.js
Normal file
123
test/integration-legacy/smoke-testing/test_navbar_links.js
Normal file
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* Checks that the links in the navbar on the homepage have the right URLs to redirect to
|
||||
*
|
||||
* Test cases: https://github.com/LLK/scratch-www/wiki/Most-Important-Workflows
|
||||
*/
|
||||
|
||||
const SeleniumHelper = require('../selenium-helpers.js');
|
||||
const helper = new SeleniumHelper();
|
||||
|
||||
var tap = require('tap');
|
||||
|
||||
const webdriver = require('selenium-webdriver');
|
||||
const driver = helper.buildDriver('www-smoke test_navbar_links');
|
||||
|
||||
// Set test url through environment variable
|
||||
var rootUrl = process.env.ROOT_URL || 'https://scratch.ly';
|
||||
|
||||
// number of tests in the plan
|
||||
tap.plan(7);
|
||||
|
||||
tap.tearDown(function () {
|
||||
// quit the instance of the browser
|
||||
driver.quit();
|
||||
});
|
||||
|
||||
tap.beforeEach(function () {
|
||||
// load the page with the driver
|
||||
return driver.get(rootUrl);
|
||||
});
|
||||
|
||||
// ==== Links in navbar ====
|
||||
|
||||
// the create link changes depending on whether the user is signed in or not (tips window opens)
|
||||
tap.test('checkCreateLinkWhenSignedOut', function (t) {
|
||||
var xPathLink = '//li[contains(@class, "link") and contains(@class, "create")]/a';
|
||||
var expectedHref = '/projects/editor/?tutorial=getStarted';
|
||||
driver.findElement(webdriver.By.xpath(xPathLink))
|
||||
.then(function (element) {
|
||||
return element.getAttribute('href');
|
||||
})
|
||||
.then(function (url) {
|
||||
t.equal(url.substr(-expectedHref.length), expectedHref);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
tap.test('checkExploreLinkWhenSignedOut', function (t) {
|
||||
var xPathLink = '//li[contains(@class, "link") and contains(@class, "explore")]/a';
|
||||
var expectedHref = '/explore/projects/all';
|
||||
driver.findElement(webdriver.By.xpath(xPathLink))
|
||||
.then(function (element) {
|
||||
return element.getAttribute('href');
|
||||
})
|
||||
.then(function (url) {
|
||||
t.equal(url.substr(-expectedHref.length), expectedHref);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
tap.test('checkIdeasLinkWhenSignedOut', function (t) {
|
||||
var xPathLink = '//li[contains(@class, "link") and contains(@class, "ideas")]/a';
|
||||
var expectedHref = '/ideas';
|
||||
driver.findElement(webdriver.By.xpath(xPathLink))
|
||||
.then(function (element) {
|
||||
return element.getAttribute('href');
|
||||
})
|
||||
.then(function (url) {
|
||||
t.equal(url.substr(-expectedHref.length), expectedHref);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
tap.test('checkAboutLinkWhenSignedOut', function (t) {
|
||||
var xPathLink = '//li[contains(@class, "link") and contains(@class, "about")]/a';
|
||||
var expectedHref = '/about';
|
||||
driver.findElement(webdriver.By.xpath(xPathLink))
|
||||
.then(function (element) {
|
||||
return element.getAttribute('href');
|
||||
})
|
||||
.then(function (url) {
|
||||
t.equal(url.substr(-expectedHref.length), expectedHref);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
// ==== Search bar ====
|
||||
|
||||
tap.test('checkSearchBar', function (t) {
|
||||
var xPathLink = '//input[@id="frc-q-1088"]';
|
||||
// search bar should exist
|
||||
driver.findElement(webdriver.By.xpath(xPathLink)).then(function (element) {
|
||||
t.ok(element);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
// ==== Join Scratch & Sign In ====
|
||||
|
||||
tap.test('checkJoinScratchLinkWhenSignedOut', function (t) {
|
||||
var xPathLink = '//li[contains(@class, "link") and contains(@class, "right") and contains(@class, "join")]/a';
|
||||
var expectedText = 'Join Scratch';
|
||||
driver.findElement(webdriver.By.xpath(xPathLink))
|
||||
.then(function (element) {
|
||||
return element.getText('a');
|
||||
})
|
||||
.then(function (text) {
|
||||
t.equal(text, expectedText);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
tap.test('checkSignInLinkWhenSignedOut', function (t) {
|
||||
var xPathLink = '//li[contains(@class, "link") and contains(@class, "right") and contains(@class, "login-item")]/a';
|
||||
var expectedText = 'Sign in';
|
||||
driver.findElement(webdriver.By.xpath(xPathLink))
|
||||
.then(function (element) {
|
||||
return element.getText('a');
|
||||
})
|
||||
.then(function (text) {
|
||||
t.equal(text, expectedText);
|
||||
t.end();
|
||||
});
|
||||
});
|
61
test/integration-legacy/smoke-testing/test_project_page.js
Normal file
61
test/integration-legacy/smoke-testing/test_project_page.js
Normal file
|
@ -0,0 +1,61 @@
|
|||
const SeleniumHelper = require('../selenium-helpers.js');
|
||||
const helper = new SeleniumHelper();
|
||||
|
||||
var tap = require('tap');
|
||||
const test = tap.test;
|
||||
|
||||
const driver = helper.buildDriver('www-smoke test_sign_in_out_homepage');
|
||||
|
||||
const {
|
||||
clickText,
|
||||
clickXpath,
|
||||
dragFromXpathToXpath,
|
||||
findByXpath,
|
||||
waitUntilGone
|
||||
} = helper;
|
||||
|
||||
const rootUrl = process.env.ROOT_URL || 'https://scratch.ly';
|
||||
const projectId = 1;
|
||||
const projectUrl = `${rootUrl}/projects/${projectId}`;
|
||||
|
||||
tap.plan(3);
|
||||
|
||||
tap.tearDown(function () {
|
||||
driver.quit();
|
||||
});
|
||||
|
||||
tap.beforeEach(function () {
|
||||
return driver.get(projectUrl);
|
||||
});
|
||||
|
||||
test('Find fullscreen button', {skip: true}, t => {
|
||||
findByXpath('//div[starts-with(@class, "loader_background")]')
|
||||
.then(el => waitUntilGone(el))
|
||||
.then(() => clickXpath('//div[starts-with(@class, "stage_green-flag-overlay")]'))
|
||||
.then(() => clickXpath('//img[contains(@alt, "Enter full screen mode")]'))
|
||||
.then(() => t.end());
|
||||
});
|
||||
|
||||
test('Open and close Copy Link modal', {skip: true}, t => {
|
||||
findByXpath('//div[starts-with(@class, "loader_background")]')
|
||||
.then(el => waitUntilGone(el))
|
||||
.then(() => clickText('Copy Link'))
|
||||
.then(() => clickXpath('//div[contains(@class, "social-label-title")]'))
|
||||
.then(() => clickXpath('//img[contains(@alt, "close-icon")]'))
|
||||
.then(() => clickXpath('//img[contains(@alt, "Enter full screen mode")]'))
|
||||
.then(() => t.end());
|
||||
});
|
||||
|
||||
test('Dragging out of modal should not close modal', {skip: true}, t => {
|
||||
findByXpath('//div[starts-with(@class, "loader_background")]')
|
||||
.then(el => waitUntilGone(el))
|
||||
.then(() => clickXpath('//div[starts-with(@class, "stage_green-flag-overlay")]'))
|
||||
.then(() => clickText('Copy Link'))
|
||||
.then(() => clickXpath('//div[contains(@class, "social-label-title")]'))
|
||||
.then(() => dragFromXpathToXpath(
|
||||
'//div[contains(@class, "social-label-title")]',
|
||||
'//li[contains(@class, "logo")]'
|
||||
))
|
||||
.then(() => clickXpath('//div[contains(@class, "social-label-title")]'))
|
||||
.then(() => t.end());
|
||||
});
|
93
test/integration-legacy/smoke-testing/test_project_rows.js
Normal file
93
test/integration-legacy/smoke-testing/test_project_rows.js
Normal file
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* Checks that the some of the homepage rows on the homepage are displayed and
|
||||
* contents have the right URLs to redirect to
|
||||
*
|
||||
* Test cases: https://github.com/LLK/scratch-www/wiki/Most-Important-Workflows
|
||||
*/
|
||||
|
||||
const SeleniumHelper = require('../selenium-helpers.js');
|
||||
const helper = new SeleniumHelper();
|
||||
|
||||
var tap = require('tap');
|
||||
|
||||
const webdriver = require('selenium-webdriver');
|
||||
const driver = helper.buildDriver('www-smoke test_project_rows');
|
||||
|
||||
var rootUrl = process.env.ROOT_URL || 'https://scratch.ly';
|
||||
|
||||
// number of tests in the plan
|
||||
tap.plan(4);
|
||||
|
||||
tap.tearDown(function () {
|
||||
// quit the instance of the browser
|
||||
driver.quit();
|
||||
});
|
||||
|
||||
tap.beforeEach(function () {
|
||||
// load the page with the driver
|
||||
return driver.get(rootUrl);
|
||||
});
|
||||
|
||||
// checks that the title of the first row is Featured Projects
|
||||
tap.test('checkFeaturedProjectsRowTitleWhenSignedOut', function (t) {
|
||||
var xPathLink = '//div[@class="box"]/div[@class="box-header"]/h4';
|
||||
driver.findElement(webdriver.By.xpath(xPathLink))
|
||||
.then(function (element) {
|
||||
element.getText('h4')
|
||||
.then(function (text) {
|
||||
// expected value of the title text
|
||||
var expectedText = 'Featured Projects';
|
||||
t.equal(text, expectedText);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// checks that the link for a project makes sense
|
||||
tap.test('checkFeaturedProjectsRowLinkWhenSignedOut', function (t) {
|
||||
var xPathLink = '//div[contains(@class, "thumbnail") ' +
|
||||
'and contains(@class, "project") and contains(@class, "slick-slide") ' +
|
||||
'and contains(@class, "slick-active")]/a[@class="thumbnail-image"]';
|
||||
driver.wait(webdriver.until
|
||||
.elementLocated(webdriver.By.xpath(xPathLink)))
|
||||
.then(function (element) {
|
||||
element.getAttribute('href')
|
||||
.then(function (url) {
|
||||
// expected pattern for the project URL
|
||||
// since I don't know the length of the project ID number
|
||||
var expectedUrlRegExp = new RegExp('/projects/.*[0-9].*/?');
|
||||
t.match(url, expectedUrlRegExp);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// checks that the title of the 2nd row is Featured Studios
|
||||
tap.test('checkFeaturedStudiosRowWhenSignedOut', function (t) {
|
||||
var xPathLink = '//div[@class="box"][2]/div[@class="box-header"]/h4';
|
||||
driver.findElement(webdriver.By.xpath(xPathLink))
|
||||
.then(function (element) {
|
||||
element.getText('h4')
|
||||
.then(function (text) {
|
||||
var expectedText = 'Featured Studios';
|
||||
t.equal(text, expectedText);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// checks that the link for a studio makes sense
|
||||
tap.test('checkFeaturedStudiosRowLinkWhenSignedOut', function (t) {
|
||||
var xPathLink = '//div[contains(@class, "thumbnail") and contains(@class, "gallery") ' +
|
||||
'and contains(@class, "slick-slide") ' +
|
||||
'and contains(@class, "slick-active")]/a[@class="thumbnail-image"]';
|
||||
driver.findElement(webdriver.By.xpath(xPathLink))
|
||||
.then(function (element) {
|
||||
element.getAttribute('href')
|
||||
.then(function (url) {
|
||||
var expectedUrlRegExp = new RegExp('/studios/.*[0-9].*/?');
|
||||
t.match(url, expectedUrlRegExp);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
});
|
58
test/integration-legacy/smoke-testing/test_search.js
Normal file
58
test/integration-legacy/smoke-testing/test_search.js
Normal file
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Checks the behavior of the search interface
|
||||
*/
|
||||
require('chromedriver');
|
||||
const seleniumWebdriver = require('selenium-webdriver');
|
||||
const SeleniumHelper = require('../selenium-helpers.js');
|
||||
const helper = new SeleniumHelper();
|
||||
const {
|
||||
urlMatches
|
||||
} = helper;
|
||||
|
||||
const tap = require('tap');
|
||||
const test = tap.test;
|
||||
|
||||
// Set test url through environment variable
|
||||
const rootUrl = process.env.ROOT_URL || 'https://scratch.ly';
|
||||
const searchBaseUrl = `${rootUrl}/search/`;
|
||||
|
||||
// chrome driver
|
||||
const driver = helper.buildDriver('www-search test_search');
|
||||
|
||||
tap.plan(3);
|
||||
|
||||
tap.tearDown(function () {
|
||||
driver.quit();
|
||||
});
|
||||
|
||||
tap.beforeEach(function () {
|
||||
return driver.get(searchBaseUrl);
|
||||
});
|
||||
|
||||
test('Search escapes spaces', function (t) {
|
||||
const searchInput = driver.findElement(seleniumWebdriver.By.name('q'));
|
||||
searchInput.sendKeys('Test search string', helper.getKey('ENTER')).then(function () {
|
||||
urlMatches(/^.*\?q=Test%20search%20string$/)
|
||||
.then(() => t.end());
|
||||
});
|
||||
});
|
||||
|
||||
test('Search escapes symbols', function (t) {
|
||||
const searchInput = driver.findElement(seleniumWebdriver.By.name('q'));
|
||||
searchInput.sendKeys('100% pen', helper.getKey('ENTER')).then(function () {
|
||||
urlMatches(/^.*\?q=100%25%20pen$/)
|
||||
.then(() => t.end());
|
||||
});
|
||||
});
|
||||
|
||||
test('Switching to studios maintains search string', function (t) {
|
||||
const searchInput = driver.findElement(seleniumWebdriver.By.name('q'));
|
||||
searchInput.sendKeys('100% pen', helper.getKey('ENTER')).then(function () {
|
||||
const studiosTab = driver.findElement(seleniumWebdriver.By.xpath(
|
||||
'//a/li/span[contains(text(),"Studios")]'));
|
||||
studiosTab.click().then(function () {
|
||||
urlMatches(/^.*\?q=100%25%20pen$/)
|
||||
.then(() => t.end());
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Tests from:
|
||||
*
|
||||
* https://github.com/LLK/scratchr2/wiki/Smoke-Testing-Test-Cases
|
||||
*
|
||||
*/
|
||||
|
||||
const SeleniumHelper = require('../selenium-helpers.js');
|
||||
const helper = new SeleniumHelper();
|
||||
|
||||
var tap = require('tap');
|
||||
const test = tap.test;
|
||||
|
||||
const driver = helper.buildDriver('www-smoke test_sign_in_out_discuss');
|
||||
|
||||
const {
|
||||
clickText,
|
||||
findByXpath,
|
||||
findText,
|
||||
clickXpath,
|
||||
clickButton
|
||||
} = helper;
|
||||
|
||||
var username = process.env.SMOKE_USERNAME;
|
||||
var password = process.env.SMOKE_PASSWORD;
|
||||
|
||||
var rootUrl = process.env.ROOT_URL || 'https://scratch.ly';
|
||||
var url = rootUrl + '/discuss';
|
||||
|
||||
tap.plan(2);
|
||||
|
||||
tap.tearDown(function () {
|
||||
driver.quit();
|
||||
});
|
||||
|
||||
tap.beforeEach(function () {
|
||||
return driver.get(url);
|
||||
});
|
||||
|
||||
test('Sign in to Scratch using scratchr2 navbar', t => {
|
||||
clickText('Sign in')
|
||||
.then(() => findByXpath('//input[@id="login_dropdown_username"]'))
|
||||
.then((element) => element.sendKeys(username))
|
||||
.then(() => findByXpath('//input[@name="password"]'))
|
||||
.then((element) => element.sendKeys(password))
|
||||
.then(() => clickButton('Sign in'))
|
||||
.then(() => findByXpath('//li[contains(@class, "logged-in-user")' +
|
||||
'and contains(@class, "dropdown")]/span'))
|
||||
.then((element) => element.getText('span'))
|
||||
.then((text) => t.match(text.toLowerCase(), username.substring(0, 10).toLowerCase(),
|
||||
'first part of username should be displayed in navbar'))
|
||||
.then(() => t.end());
|
||||
});
|
||||
|
||||
test('Sign out of Scratch using scratchr2 navbar', t => {
|
||||
clickXpath('//span[contains(@class, "user-name")' +
|
||||
' and contains(@class, "dropdown-toggle")]/img[contains(@class, "user-icon")]')
|
||||
.then(() => clickXpath('//input[@value="Sign out"]'))
|
||||
.then(() => findText('Sign in'))
|
||||
.then((element) => t.ok(element, 'Sign in reappeared on the page after signing out'))
|
||||
.then(() => t.end());
|
||||
});
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Tests from:
|
||||
*
|
||||
* https://github.com/LLK/scratchr2/wiki/Smoke-Testing-Test-Cases
|
||||
*
|
||||
*/
|
||||
|
||||
const SeleniumHelper = require('../selenium-helpers.js');
|
||||
const helper = new SeleniumHelper();
|
||||
|
||||
var tap = require('tap');
|
||||
const test = tap.test;
|
||||
|
||||
const driver = helper.buildDriver('www-smoke test_sign_in_out_homepage');
|
||||
|
||||
const {
|
||||
clickText,
|
||||
findText,
|
||||
findByXpath,
|
||||
clickXpath
|
||||
} = helper;
|
||||
|
||||
var username = process.env.SMOKE_USERNAME;
|
||||
var password = process.env.SMOKE_PASSWORD;
|
||||
|
||||
var rootUrl = process.env.ROOT_URL || 'https://scratch.ly';
|
||||
|
||||
tap.plan(2);
|
||||
|
||||
tap.tearDown(function () {
|
||||
driver.quit();
|
||||
});
|
||||
|
||||
tap.beforeEach(function () {
|
||||
return driver.get(rootUrl);
|
||||
});
|
||||
|
||||
test('Sign in to Scratch using scratch-www navbar', {skip: true}, t => {
|
||||
clickText('Sign in')
|
||||
.then(() => findByXpath('//input[@id="frc-username-1088"]'))
|
||||
.then((element) => element.sendKeys(username))
|
||||
.then(() => findByXpath('//input[@id="frc-password-1088"]'))
|
||||
.then((element) => element.sendKeys(password))
|
||||
.then(() => clickXpath('//button[contains(@class, "button") and ' +
|
||||
'contains(@class, "submit-button") and contains(@class, "white")]'))
|
||||
.then(() => findByXpath('//span[contains(@class, "profile-name")]'))
|
||||
.then((element) => element.getText())
|
||||
.then((text) => t.match(text.toLowerCase(), username.substring(0, 10).toLowerCase(),
|
||||
'first part of username should be displayed in navbar'))
|
||||
.then(() => t.end());
|
||||
});
|
||||
|
||||
test('Sign out of Scratch using scratch-www navbar', {skip: true}, t => {
|
||||
clickXpath('//a[contains(@class, "user-info")]')
|
||||
.then(() => clickText('Sign out'))
|
||||
.then(() => findText('Sign in'))
|
||||
.then((element) => t.ok(element, 'Sign in reappeared on the page after signing out'))
|
||||
.then(() => t.end());
|
||||
});
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Tests stats page according to smoke-tests at:
|
||||
*
|
||||
* https://github.com/LLK/scratchr2/wiki/Smoke-Testing-Test-Cases
|
||||
*
|
||||
*/
|
||||
|
||||
const SeleniumHelper = require('../selenium-helpers.js');
|
||||
const helper = new SeleniumHelper();
|
||||
|
||||
var tap = require('tap');
|
||||
const test = tap.test;
|
||||
|
||||
const driver = helper.buildDriver('www-smoke test_statistics_page');
|
||||
|
||||
const {
|
||||
clickText,
|
||||
findByXpath,
|
||||
findByCss
|
||||
} = helper;
|
||||
|
||||
tap.plan(2);
|
||||
|
||||
tap.tearDown(function () {
|
||||
driver.quit();
|
||||
});
|
||||
|
||||
tap.beforeEach(function () {
|
||||
/*
|
||||
* load the page with the driver
|
||||
* note that for now this is not testable on Staging,
|
||||
* so I left it pointing to Production -
|
||||
* we can at least use it post-deploy.
|
||||
*
|
||||
* var stagingURL = 'https://scratch.ly/statistics';
|
||||
*/
|
||||
var productionURL = 'https://scratch.mit.edu/statistics';
|
||||
return driver.get(productionURL);
|
||||
});
|
||||
|
||||
test('check that Monthly Activity Trends title is present & correct', t => {
|
||||
var chartTitle = 'Monthly Activity Trends';
|
||||
findByCss('div.box-head h3')
|
||||
.then((element) => element.getText('h3'))
|
||||
.then((text) => t.equal(text, chartTitle, 'chart title should be Monthly Activity Trends'))
|
||||
.then(() => t.end());
|
||||
});
|
||||
|
||||
test('check that Monthly Activity Trends chart > New Projects label is toggleable', t => {
|
||||
var classXpath = `(//div[@id="activity_chart"]/*[name()='svg']/*[name()='g']/*[name()='g']/*` +
|
||||
`[name()='g'])[4]/*[name()='g']/*[name()='g']/*[name()='g']`;
|
||||
findByXpath(classXpath)
|
||||
.then((element) => element.getAttribute('class'))
|
||||
.then((classtext) => t.equal(classtext, 'nv-series', 'by default, New Projects should be enabled'))
|
||||
.then(() => clickText('New Projects'))
|
||||
.then(() => findByXpath(classXpath))
|
||||
.then((element) => element.getAttribute('class'))
|
||||
.then((classtext) => t.equal(
|
||||
classtext,
|
||||
'nv-series nv-disabled',
|
||||
'when clicked, New Projects should be disabled'
|
||||
))
|
||||
.then(() => clickText('New Projects'))
|
||||
.then(() => findByXpath(classXpath))
|
||||
.then((element) => element.getAttribute('class'))
|
||||
.then((classtext) => t.equal(classtext, 'nv-series', 'when clicked again, New Projects should be enabled'))
|
||||
.then(() => t.end());
|
||||
});
|
|
@ -0,0 +1,99 @@
|
|||
module.exports.constants = {
|
||||
nextStepXpath: '//button[span[contains(text(), "Next Step")]]',
|
||||
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' +
|
||||
'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) {
|
||||
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(module.exports.constants.nextStepXpath));
|
||||
return Promise.all([usernamePromise, passwordPromise]).then(function () { // eslint-disable-line no-undef
|
||||
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[@value="us"]')).click();
|
||||
var nextStepButton = driver.findElement(seleniumWebdriver.By.xpath(module.exports.constants.nextStepXpath));
|
||||
return Promise.all([clickMaleInput, selectCountry]).then(function () { // eslint-disable-line no-undef
|
||||
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 () { // eslint-disable-line no-undef
|
||||
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 () { // eslint-disable-line no-undef
|
||||
nextStepButton.click().then(function () {
|
||||
driver.wait(seleniumWebdriver.until
|
||||
.elementLocated(seleniumWebdriver.By.className('organization-step')));
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
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]) // eslint-disable-line no-undef
|
||||
.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]) // eslint-disable-line no-undef
|
||||
.then(function () {
|
||||
nextStepButton.click().then(function () {
|
||||
driver.wait(seleniumWebdriver.until
|
||||
.elementLocated(seleniumWebdriver.By.className('usescratch-step')));
|
||||
});
|
||||
});
|
||||
};
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* 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();
|
||||
|
||||
tap.plan(2);
|
||||
|
||||
tap.tearDown(function () {
|
||||
driver.quit();
|
||||
});
|
||||
|
||||
tap.beforeEach(function () {
|
||||
driver.get(rootUrl + '/educators/register');
|
||||
return utils.fillUsernameSlide(driver, seleniumWebdriver)
|
||||
.then(utils.fillDemographicsSlide.bind(this, driver, seleniumWebdriver)) // eslint-disable-line no-invalid-this
|
||||
.then(utils.fillNameSlide.bind(this, driver, seleniumWebdriver)) // eslint-disable-line no-invalid-this
|
||||
.then(utils.fillPhoneSlide.bind(this, driver, seleniumWebdriver)) // eslint-disable-line no-invalid-this
|
||||
.then(utils.fillOrganizationSlide.bind(this, driver, seleniumWebdriver)); // eslint-disable-line no-invalid-this
|
||||
});
|
||||
|
||||
// Selects Vatican City as the country, and checks that the state dropdown disappears
|
||||
tap.test('checkStateDropdownOnlyPresentWhenNeeded', function (t) {
|
||||
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();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
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"]/span[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();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* 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');
|
||||
|
||||
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();
|
||||
|
||||
tap.plan(2);
|
||||
|
||||
tap.tearDown(function () {
|
||||
driver.quit();
|
||||
});
|
||||
|
||||
tap.beforeEach(function () {
|
||||
driver.get(rootUrl + '/educators/register');
|
||||
return utils.fillUsernameSlide(driver, seleniumWebdriver);
|
||||
});
|
||||
|
||||
// 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(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(constants.generalErrorMessageXpath))
|
||||
.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(constants.nextStepXpath));
|
||||
driver.findElement(seleniumWebdriver.By.xpath('//select[@name="user.country"]/option[2]')).click();
|
||||
nextStepButton.click().then(function () {
|
||||
driver.findElements(seleniumWebdriver.By.xpath(constants.generalErrorMessageXpath))
|
||||
.then(function (validationMessages) {
|
||||
t.equal(validationMessages.length, 1);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* 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');
|
||||
|
||||
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();
|
||||
|
||||
tap.plan(2);
|
||||
|
||||
tap.tearDown(function () {
|
||||
driver.quit();
|
||||
});
|
||||
|
||||
tap.beforeEach(function () {
|
||||
driver.get(rootUrl + '/educators/register');
|
||||
return utils.fillUsernameSlide(driver, seleniumWebdriver)
|
||||
.then(utils.fillDemographicsSlide.bind(this, driver, seleniumWebdriver)); // eslint-disable-line no-invalid-this
|
||||
});
|
||||
|
||||
// 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"]/span[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();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// 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"]/span[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();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* 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
|
||||
*/
|
||||
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();
|
||||
|
||||
tap.plan(4);
|
||||
|
||||
tap.tearDown(function () {
|
||||
driver.quit();
|
||||
});
|
||||
|
||||
tap.beforeEach(function () {
|
||||
driver.get(rootUrl + '/educators/register');
|
||||
return utils.fillUsernameSlide(driver, seleniumWebdriver)
|
||||
.then(utils.fillDemographicsSlide.bind(this, driver, seleniumWebdriver)) // eslint-disable-line no-invalid-this
|
||||
.then(utils.fillNameSlide.bind(this, driver, seleniumWebdriver)) // eslint-disable-line no-invalid-this
|
||||
.then(utils.fillPhoneSlide.bind(this, driver, seleniumWebdriver)); // eslint-disable-line no-invalid-this
|
||||
});
|
||||
|
||||
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"]/span[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"]/span[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('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();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* 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 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();
|
||||
|
||||
tap.plan(1);
|
||||
|
||||
tap.tearDown(function () {
|
||||
driver.quit();
|
||||
});
|
||||
|
||||
tap.beforeEach(function () {
|
||||
driver.get(rootUrl + '/educators/register');
|
||||
return utils.fillUsernameSlide(driver, seleniumWebdriver)
|
||||
.then(utils.fillDemographicsSlide.bind(this, driver, seleniumWebdriver)) // eslint-disable-line no-invalid-this
|
||||
.then(utils.fillNameSlide.bind(this, driver, seleniumWebdriver)); // eslint-disable-line no-invalid-this
|
||||
});
|
||||
|
||||
// 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();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* 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');
|
||||
|
||||
// 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();
|
||||
|
||||
tap.plan(5);
|
||||
|
||||
tap.tearDown(function () {
|
||||
driver.quit();
|
||||
});
|
||||
|
||||
tap.beforeEach(function () {
|
||||
return driver.get(rootUrl + '/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 () { // eslint-disable-line no-undef
|
||||
var errorBubble = driver.wait(seleniumWebdriver.until
|
||||
.elementLocated(seleniumWebdriver.By.xpath(errorMessageXPath)), 10000);
|
||||
t.notEqual(errorBubble, undefined); // eslint-disable-line no-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 () { // eslint-disable-line no-undef
|
||||
driver.findElements(seleniumWebdriver.By.xpath(errorMessageXPath)).then(function (validationMessages) {
|
||||
// there should be only one validation message
|
||||
t.equal(validationMessages.length, 1);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* 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();
|
||||
|
||||
tap.plan(3);
|
||||
|
||||
tap.tearDown(function () {
|
||||
driver.quit();
|
||||
});
|
||||
|
||||
tap.beforeEach(function () {
|
||||
driver.get(rootUrl + '/educators/register');
|
||||
return utils.fillUsernameSlide(driver, seleniumWebdriver)
|
||||
.then(utils.fillDemographicsSlide.bind(this, driver, seleniumWebdriver)) // eslint-disable-line no-invalid-this
|
||||
.then(utils.fillNameSlide.bind(this, driver, seleniumWebdriver)) // eslint-disable-line no-invalid-this
|
||||
.then(utils.fillPhoneSlide.bind(this, driver, seleniumWebdriver)) // eslint-disable-line no-invalid-this
|
||||
.then(utils.fillOrganizationSlide.bind(this, driver, seleniumWebdriver)) // eslint-disable-line no-invalid-this
|
||||
.then(utils.fillAddressSlide.bind(this, driver, seleniumWebdriver)); // eslint-disable-line no-invalid-this
|
||||
});
|
||||
|
||||
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();
|
||||
});
|
||||
});
|
||||
});
|
52
test/localization-legacy/check_duplicate_strings.js
Normal file
52
test/localization-legacy/check_duplicate_strings.js
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Check that there are no duplicate strings in any individual l10n json file.
|
||||
*/
|
||||
var path = require('path');
|
||||
var fs = require('fs');
|
||||
var tap = require('tap');
|
||||
|
||||
var routes = require('../../src/routes.json');
|
||||
|
||||
const noDuplicateValues = (idsToCheck, name) => {
|
||||
let values = {};
|
||||
for (const key in idsToCheck) {
|
||||
if (values.hasOwnProperty(idsToCheck[key])) {
|
||||
// duplicate values
|
||||
// return false;
|
||||
tap.fail(`${name}.${idsToCheck[key]} has duplicates`);
|
||||
} else {
|
||||
values[idsToCheck[key]] = key;
|
||||
}
|
||||
}
|
||||
tap.pass();
|
||||
// return true;
|
||||
};
|
||||
|
||||
tap.test('generalCheckForDuplicates', function (t) {
|
||||
const ids = require(path.resolve(__dirname, '../../src/l10n.json')); // eslint-disable-line global-require
|
||||
noDuplicateValues(ids, 'general');
|
||||
t.end();
|
||||
});
|
||||
|
||||
for (var v in routes) {
|
||||
if (typeof routes[v].redirect !== 'undefined') {
|
||||
continue;
|
||||
}
|
||||
var subdir = routes[v].view.split('/');
|
||||
subdir.pop();
|
||||
var name = routes[v].name;
|
||||
var uri = path.resolve(__dirname, '../../src/views/' + subdir.join('/') + '/l10n.json');
|
||||
try {
|
||||
var file = fs.readFileSync(uri, 'utf8');
|
||||
var ids = JSON.parse(file);
|
||||
tap.test(name + 'CheckForDuplicates', function (t) { // eslint-disable-line no-loop-func
|
||||
noDuplicateValues(ids, name);
|
||||
t.end();
|
||||
});
|
||||
} catch (err) {
|
||||
if (err.code !== 'ENOENT') {
|
||||
// ignore if ENOENT for routes with no l10n file, throw error for anything else
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
72
test/localization-legacy/check_string_ids.js
Normal file
72
test/localization-legacy/check_string_ids.js
Normal file
|
@ -0,0 +1,72 @@
|
|||
/**
|
||||
* 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
|
||||
*/
|
||||
const 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); // eslint-disable-line no-eval
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
23
test/localization-legacy/check_valid_json.js
Normal file
23
test/localization-legacy/check_valid_json.js
Normal file
|
@ -0,0 +1,23 @@
|
|||
var fs = require('fs');
|
||||
var glob = require('glob');
|
||||
var tap = require('tap');
|
||||
|
||||
var TRANSLATIONS_PATTERN = './node_modules/scratch-l10n/www/**/*.json';
|
||||
var files = glob.sync(TRANSLATIONS_PATTERN);
|
||||
|
||||
const checkJson = (data, name) => {
|
||||
try {
|
||||
JSON.parse(data);
|
||||
} catch (e) {
|
||||
tap.fail(name + ' has invalid Json.\n');
|
||||
}
|
||||
};
|
||||
|
||||
tap.test('check valid json', function (t) {
|
||||
files.forEach(function (f) {
|
||||
const data = fs.readFileSync(f);
|
||||
checkJson(data, f);
|
||||
});
|
||||
t.pass();
|
||||
t.end();
|
||||
});
|
66
test/unit-legacy/lib/validate.js
Normal file
66
test/unit-legacy/lib/validate.js
Normal file
|
@ -0,0 +1,66 @@
|
|||
const tap = require('tap');
|
||||
const validate = require('../../../src/lib/validate');
|
||||
|
||||
tap.tearDown(() => process.nextTick(process.exit));
|
||||
|
||||
tap.test('validate username locally', t => {
|
||||
let response;
|
||||
t.type(validate.validateUsernameLocally, 'function');
|
||||
response = validate.validateUsernameLocally('abc');
|
||||
t.deepEqual(response, {valid: true});
|
||||
response = validate.validateUsernameLocally('abcdefghijklmnopqrst');
|
||||
t.deepEqual(response, {valid: true});
|
||||
response = validate.validateUsernameLocally('abc-def-ghi');
|
||||
t.deepEqual(response, {valid: true});
|
||||
response = validate.validateUsernameLocally('');
|
||||
t.deepEqual(response, {valid: false, errMsgId: 'form.validationRequired'});
|
||||
response = validate.validateUsernameLocally('ab');
|
||||
t.deepEqual(response, {valid: false, errMsgId: 'form.validationUsernameMinLength'});
|
||||
response = validate.validateUsernameLocally('abcdefghijklmnopqrstu');
|
||||
t.deepEqual(response, {valid: false, errMsgId: 'form.validationUsernameMaxLength'});
|
||||
response = validate.validateUsernameLocally('abc def');
|
||||
t.deepEqual(response, {valid: false, errMsgId: 'form.validationUsernameRegexp'});
|
||||
response = validate.validateUsernameLocally('abc!def');
|
||||
t.deepEqual(response, {valid: false, errMsgId: 'form.validationUsernameRegexp'});
|
||||
response = validate.validateUsernameLocally('abc😄def');
|
||||
t.deepEqual(response, {valid: false, errMsgId: 'form.validationUsernameRegexp'});
|
||||
t.end();
|
||||
});
|
||||
|
||||
tap.test('validate password', t => {
|
||||
let response;
|
||||
t.type(validate.validatePassword, 'function');
|
||||
response = validate.validatePassword('abcdef');
|
||||
t.deepEqual(response, {valid: true});
|
||||
response = validate.validatePassword('abcdefghijklmnopqrst');
|
||||
t.deepEqual(response, {valid: true});
|
||||
response = validate.validatePassword('passwo');
|
||||
t.deepEqual(response, {valid: true});
|
||||
response = validate.validatePassword('');
|
||||
t.deepEqual(response, {valid: false, errMsgId: 'form.validationRequired'});
|
||||
response = validate.validatePassword('abcde');
|
||||
t.deepEqual(response, {valid: false, errMsgId: 'registration.validationPasswordLength'});
|
||||
response = validate.validatePassword('password');
|
||||
t.deepEqual(response, {valid: false, errMsgId: 'registration.validationPasswordNotEquals'});
|
||||
t.end();
|
||||
});
|
||||
|
||||
tap.test('validate password confirm', t => {
|
||||
let response;
|
||||
t.type(validate.validatePasswordConfirm, 'function');
|
||||
response = validate.validatePasswordConfirm('abcdef', 'abcdef');
|
||||
t.deepEqual(response, {valid: true});
|
||||
response = validate.validatePasswordConfirm('abcdefghijklmnopqrst', 'abcdefghijklmnopqrst');
|
||||
t.deepEqual(response, {valid: true});
|
||||
response = validate.validatePasswordConfirm('passwo', 'passwo');
|
||||
t.deepEqual(response, {valid: true});
|
||||
response = validate.validatePasswordConfirm('', '');
|
||||
t.deepEqual(response, {valid: false, errMsgId: 'form.validationRequired'});
|
||||
response = validate.validatePasswordConfirm('abcdef', 'abcdefg');
|
||||
t.deepEqual(response, {valid: false, errMsgId: 'general.error'});
|
||||
response = validate.validatePasswordConfirm('abcdef', '123456');
|
||||
t.deepEqual(response, {valid: false, errMsgId: 'general.error'});
|
||||
response = validate.validatePasswordConfirm('', 'abcdefg');
|
||||
t.deepEqual(response, {valid: false, errMsgId: 'general.error'});
|
||||
t.end();
|
||||
});
|
176
test/unit-legacy/redux/preview-test.js
Normal file
176
test/unit-legacy/redux/preview-test.js
Normal file
|
@ -0,0 +1,176 @@
|
|||
const tap = require('tap');
|
||||
const Preview = require('../../../src/redux/preview');
|
||||
const initialState = Preview.getInitialState();
|
||||
const reducer = Preview.previewReducer;
|
||||
|
||||
let state;
|
||||
|
||||
tap.tearDown(() => process.nextTick(process.exit));
|
||||
|
||||
tap.test('Reducer', t => {
|
||||
t.type(reducer, 'function');
|
||||
t.type(initialState, 'object');
|
||||
|
||||
// Reducers should return their default state when called without state
|
||||
let undefinedState;
|
||||
t.deepEqual(initialState, reducer(undefinedState, {type: 'fake action'}));
|
||||
t.end();
|
||||
});
|
||||
|
||||
tap.test('resetProject', t => {
|
||||
state = reducer({some: 'garbage'}, Preview.resetProject());
|
||||
t.deepEqual(state, initialState);
|
||||
t.end();
|
||||
});
|
||||
|
||||
tap.test('setProjectInfo', t => {
|
||||
// Initial values
|
||||
t.equal(initialState.projectNotAvailable, false);
|
||||
t.deepEqual(initialState.projectInfo, {});
|
||||
|
||||
// setProjectInfo action with an `info` value sets the projectInfo
|
||||
state = reducer(initialState, Preview.setProjectInfo('a value'));
|
||||
t.equal(state.projectInfo, 'a value');
|
||||
t.equal(state.projectNotAvailable, false);
|
||||
|
||||
// setProjectInfo action with null info sets projectNotAvailable to true
|
||||
// and resets the project info back to default state
|
||||
state = reducer(initialState, Preview.setProjectInfo(null));
|
||||
t.deepEqual(state.projectInfo, initialState.projectInfo);
|
||||
t.equal(state.projectNotAvailable, true);
|
||||
t.end();
|
||||
});
|
||||
|
||||
tap.test('updateProjectInfo', t => {
|
||||
const info = {a: 'value a', b: 'value b'};
|
||||
state = reducer({projectInfo: info}, Preview.updateProjectInfo({
|
||||
b: 'new value b',
|
||||
c: 'new value c'
|
||||
}));
|
||||
t.deepEqual(state.projectInfo, {
|
||||
a: 'value a',
|
||||
b: 'new value b',
|
||||
c: 'new value c'
|
||||
});
|
||||
t.end();
|
||||
});
|
||||
|
||||
tap.test('setComments', t => {
|
||||
// Initial value
|
||||
t.deepEqual(initialState.comments, []);
|
||||
|
||||
state = reducer(initialState, Preview.setComments([{id: 1}, {id: 2}]));
|
||||
state = reducer(state, Preview.setComments([{id: 3}, {id: 4}]));
|
||||
t.deepEqual(state.comments, [{id: 1}, {id: 2}, {id: 3}, {id: 4}]);
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
const commentState = {
|
||||
comments: [
|
||||
{id: 'id1', visibility: 'visible'},
|
||||
{id: 'id2', visibility: 'visible'},
|
||||
{id: 'id3', visibility: 'visible'}
|
||||
],
|
||||
replies: {
|
||||
id1: [
|
||||
{id: 'id4', visibility: 'visible'},
|
||||
{id: 'id5', visibility: 'visible'}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
tap.test('setComments, discards duplicates', t => {
|
||||
state = reducer(commentState, Preview.setComments([{id: 'id1'}]));
|
||||
// Does not increase the number of comments, still 3
|
||||
t.equal(state.comments.length, 3);
|
||||
t.end();
|
||||
});
|
||||
|
||||
tap.test('setCommentDeleted, top level comment', t => {
|
||||
state = reducer(commentState, Preview.setCommentDeleted('id2'));
|
||||
t.equal(state.comments[1].visibility, 'deleted');
|
||||
t.end();
|
||||
});
|
||||
|
||||
tap.test('setCommentDeleted, reply comment', t => {
|
||||
state = reducer(commentState, Preview.setCommentDeleted('id4', 'id1'));
|
||||
t.equal(state.replies.id1[0].visibility, 'deleted');
|
||||
t.end();
|
||||
});
|
||||
|
||||
tap.test('setRepliesDeleted/Restored', t => {
|
||||
state = reducer(commentState, Preview.setRepliesDeleted('id1'));
|
||||
t.equal(state.replies.id1[0].visibility, 'deleted');
|
||||
t.equal(state.replies.id1[1].visibility, 'deleted');
|
||||
|
||||
state = reducer(state, Preview.setRepliesRestored('id1'));
|
||||
t.equal(state.replies.id1[0].visibility, 'visible');
|
||||
t.equal(state.replies.id1[1].visibility, 'visible');
|
||||
t.end();
|
||||
});
|
||||
|
||||
tap.test('setCommentReported, top level comment', t => {
|
||||
state = reducer(commentState, Preview.setCommentReported('id2'));
|
||||
t.equal(state.comments[1].visibility, 'reported');
|
||||
t.end();
|
||||
});
|
||||
|
||||
tap.test('setCommentReported, reply comment', t => {
|
||||
state = reducer(commentState, Preview.setCommentReported('id4', 'id1'));
|
||||
t.equal(state.replies.id1[0].visibility, 'reported');
|
||||
t.end();
|
||||
});
|
||||
|
||||
tap.test('addNewComment, top level comment', t => {
|
||||
state = reducer(commentState, Preview.addNewComment({id: 'new comment'}));
|
||||
// Adds comment to beginning of list
|
||||
t.equal(state.comments[0].id, 'new comment');
|
||||
t.end();
|
||||
});
|
||||
|
||||
tap.test('addNewComment, reply comment', t => {
|
||||
state = reducer(commentState, Preview.addNewComment({id: 'new comment'}, 'id1'));
|
||||
// Adds replies to the end of the replies list
|
||||
t.equal(state.replies.id1[2].id, 'new comment');
|
||||
t.end();
|
||||
});
|
||||
|
||||
tap.test('setReplies', t => {
|
||||
// setReplies should append new replies
|
||||
state = reducer(commentState, Preview.setReplies({
|
||||
id1: {id: 'id6'}
|
||||
}));
|
||||
t.equal(state.replies.id1[2].id, 'id6');
|
||||
t.equal(state.comments[0].moreRepliesToLoad, false);
|
||||
|
||||
// setReplies should ignore duplicates, do the same as above again
|
||||
t.equal(state.replies.id1.length, 3);
|
||||
state = reducer(state, Preview.setReplies({id1: {id: 'id6'}}));
|
||||
t.equal(state.replies.id1.length, 3);
|
||||
|
||||
// setReplies can add replies to a comment that didn't have any
|
||||
state = reducer(state, Preview.setReplies({
|
||||
id2: {id: 'id7'}
|
||||
}));
|
||||
t.equal(state.replies.id1.length, 3);
|
||||
t.equal(state.replies.id2.length, 1);
|
||||
t.equal(state.replies.id2[0].id, 'id7');
|
||||
t.equal(state.comments[0].moreRepliesToLoad, false);
|
||||
t.equal(state.comments[1].moreRepliesToLoad, false);
|
||||
|
||||
// Getting 20 (COMMENT_LIMIT) replies sets moreRepliesToLoad to true
|
||||
state = reducer(state, Preview.setReplies({
|
||||
id3: (new Array(20)).map((_, i) => ({id: `id${i + 1}`}))
|
||||
}));
|
||||
t.equal(state.comments[0].moreRepliesToLoad, false);
|
||||
t.equal(state.comments[1].moreRepliesToLoad, false);
|
||||
t.equal(state.comments[2].moreRepliesToLoad, true);
|
||||
|
||||
// Getting one more reply sets moreRepliesToLoad back to false
|
||||
state = reducer(state, Preview.setReplies({
|
||||
id3: {id: 'id21'}
|
||||
}));
|
||||
t.equal(state.comments[2].moreRepliesToLoad, false);
|
||||
t.end();
|
||||
});
|
79
test/unit-legacy/test_fastly_config_methods.js
Normal file
79
test/unit-legacy/test_fastly_config_methods.js
Normal file
|
@ -0,0 +1,79 @@
|
|||
var defaults = require('lodash.defaults');
|
||||
var fastlyConfig = require('../../bin/lib/fastly-config-methods');
|
||||
var routeJson = require('../../src/routes.json');
|
||||
var tap = require('tap');
|
||||
|
||||
var testRoutes = [
|
||||
{
|
||||
name: 'less-traveled',
|
||||
pattern: '^/?$',
|
||||
routeAlias: '/?$',
|
||||
view: 'less-traveled/less-traveled',
|
||||
title: 'Robert Frost Goes Here'
|
||||
},
|
||||
{
|
||||
name: 'more-traveled',
|
||||
pattern: '^/more?$',
|
||||
routeAlias: '/more?$',
|
||||
view: 'more-traveled/more-traveled',
|
||||
title: 'Robert Frost Does Not Go Here'
|
||||
}
|
||||
];
|
||||
|
||||
var routes = routeJson.map(function (route) {
|
||||
return defaults({}, {pattern: fastlyConfig.expressPatternToRegex(route.pattern)}, route);
|
||||
});
|
||||
var extraAppRoutes = [
|
||||
// Homepage with querystring.
|
||||
// TODO: Should this be added for every route?
|
||||
'/\\?',
|
||||
// View html
|
||||
'/[^/]*.html$'
|
||||
];
|
||||
|
||||
|
||||
tap.test('getStaticPaths', function (t) {
|
||||
var staticPaths = fastlyConfig.getStaticPaths(__dirname, '../../build/*');
|
||||
t.type(staticPaths, 'object');
|
||||
t.end();
|
||||
});
|
||||
|
||||
tap.test('getViewPaths', function (t) {
|
||||
var viewPaths = fastlyConfig.getViewPaths(testRoutes);
|
||||
t.type(viewPaths, 'object');
|
||||
t.equal(viewPaths[0], '/?$');
|
||||
t.equal(viewPaths[1], '/more?$');
|
||||
t.end();
|
||||
});
|
||||
|
||||
tap.test('pathsToCondition', function (t) {
|
||||
var condition = fastlyConfig.pathsToCondition(['/?$', '/more?$']);
|
||||
t.type(condition, 'string');
|
||||
t.equal(condition, 'req.url~"^(/?$|/more?$)"');
|
||||
t.end();
|
||||
});
|
||||
|
||||
tap.test('getAppRouteCondition', function (t) {
|
||||
var condition = fastlyConfig.getAppRouteCondition('../../build/*', routes, extraAppRoutes, __dirname);
|
||||
t.type(condition, 'string');
|
||||
t.end();
|
||||
});
|
||||
|
||||
tap.test('testSetTTL', function (t) {
|
||||
var ttl = fastlyConfig.setResponseTTL('itsactuallyttyl');
|
||||
t.equal(ttl, '' +
|
||||
'if (itsactuallyttyl) {\n' +
|
||||
' if (req.url ~ "^(/projects/|/fragment/account-nav.json|/session/)" && ' +
|
||||
'!req.http.Cookie:scratchsessionsid) {\n' +
|
||||
' set beresp.http.Vary = "Accept-Encoding, Accept-Language";\n' +
|
||||
' unset beresp.http.set-cookie;\n' +
|
||||
' return(deliver);\n' +
|
||||
' } else {\n' +
|
||||
' set beresp.ttl = 0s;\n' +
|
||||
' set beresp.grace = 0s;\n' +
|
||||
' return(pass);\n' +
|
||||
' }\n' +
|
||||
'}\n'
|
||||
);
|
||||
t.end();
|
||||
});
|
Loading…
Reference in a new issue