Commit fde83d85 authored by S Anand's avatar S Anand

ENH: add browser tests for Firefox, Edge, Chrome. Fixes #130

parent b8e3e44b
Pipeline #72746 passed with stage
in 2 minutes and 16 seconds
......@@ -3,6 +3,12 @@ module.exports = {
"ecmaVersion": 6,
"sourceType": "module",
},
"overrides": [{
"files": ["test/server.js"],
"parserOptions": {
"ecmaVersion": 8
}
}],
"env": {
"node": true, // Include node globals
"browser": true, // Include browser globals
......
......@@ -7,11 +7,17 @@ To set up locally, clone this repo and run:
Here is a list of npm commands available:
npm test # Run all unit tests
npm test # Run all unit tests on puppeteer
npm run lint # Check for basic errors using eslint
npm run server # Start a HTTP server at the current folder for manual testing
npm run dev # Start a rollup watch that re-builds dist/ if files change
To test unit tests on other browsers using Selenium:
- `npm run test-chrome`. Requires [ChromeDriver](https://sites.google.com/a/chromium.org/chromedriver/downloads) in your PATH
- `npm run test-edge`. Requires [WebDriver](https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/) in your PATH
- `npm run test-firefox`. Requires [GeckoDriver](https://github.com/mozilla/geckodriver/releases/) in your PATH
# File structure
- `dist/` has output files. It is re-created via `npm run build`. It has:
......@@ -65,6 +71,9 @@ To publish a new version on npm:
git checkout dev
npm run lint
npm test
npm test-chrome
npm test-edge
npm test-firefox
# Update package.json version
# Update CHANGELOG.md
......
......@@ -66,6 +66,11 @@ or one of the individual libraries below:
For debugging, use [g.js](dist/g.js) -- an un-minified version.
## Browser support
Every release is tested on the current versions of Chrome, Edge and Firefox.
Internet Explorer is not explicitly tested, but many components work on IE.
## Contributing
- [Add an issue](https://code.gramener.com/cto/g1/issues)
......
......@@ -15,7 +15,10 @@
"dev": "rimraf dist && json2module package.json > src/package.js && rollup -c -w",
"pretest": "npm run build && browserify -s tape -r tape -o test/tape.js",
"server": "npm run pretest && npm run lint && node test/server.js",
"test": "tape test/test-*.js | faucet && node test/server.js run | tap-merge | faucet",
"test": "tape test/test-*.js | faucet && node test/server.js puppeteer | tap-merge | faucet",
"test-chrome": "node test/server.js chrome | tap-merge | faucet",
"test-edge": "node test/server.js MicrosoftEdge | tap-merge | faucet",
"test-firefox": "node test/server.js firefox | tap-merge | faucet",
"prepublishOnly": "npm test"
},
"devDependencies": {
......@@ -58,6 +61,7 @@
"rollup-plugin-uglify": "2",
"rollup-pluginutils": "2",
"rollup-watch": "4",
"selenium-webdriver": "3",
"tap-merge": "0.3",
"tape": "4",
"topojson": "3",
......
["bengaluru \"", "hyderabad", "mumbai", "delhi"]
<!DOCTYPE html>
<html>
<head>
<title>Editable formhandler tests</title>
<link rel="stylesheet" href="../node_modules/bootstrap/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="../node_modules/font-awesome/css/font-awesome.min.css">
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.6-rc.0/css/select2.min.css" rel="stylesheet" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.8.0/css/bootstrap-datepicker.css" />
<style>
.position-relative {
position: relative;
}
.pos-cc {
position: absolute;
top: 45%;
left: 45%;
}
.d-none {
display: none !important;
}
</style>
<script src="../node_modules/jquery/dist/jquery.min.js"></script>
<script src="../node_modules/popper.js/dist/umd/popper.min.js"></script>
<script src="../node_modules/bootstrap/dist/js/bootstrap.min.js"></script>
<script src="../node_modules/lodash/lodash.min.js"></script>
<script src="../node_modules/moment/min/moment.min.js"></script>
<script src="../node_modules/numeral/min/numeral.min.js"></script>
<script src="../dist/formhandler.min.js"></script>
<script src="../dist/scale.min.js"></script>
<script src="../node_modules/d3/build/d3.js"></script>
<script src="../node_modules/d3-scale-chromatic/dist/d3-scale-chromatic.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.6-rc.0/js/select2.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.8.0/js/bootstrap-datepicker.js"></script>
<script src="tape.js"></script>
</head>
<body>
<script>
tape.onFinish(function () { window.renderComplete = true })
</script>
<div class="edit-fh"></div>
<script>
tape('$().formhandler() test edit to save rows triggered on "input" event rather "click: event', function (test) {
test.plan(12)
$('.edit-fh').formhandler({
src: '/formhandler-data',
columns: [
{
name: 'ID',
editable: false
},
{
name: 'Continent',
editable: {
input: 'text',
attrs: {
minlength: 2,
class: 'bg-warning'
}
}
},
{
name: 'c1',
editable: {
input: 'number',
attrs: {
min: 10,
max: 100,
placeholder: '0 - 100'
}
}
},
{
name: 'Stripes',
editable: {
input: 'select',
options: [
'Vertical',
'Horizontal',
'Diagonal'
]
}
},
{
name: 'Shapes',
editable: {
input: 'select',
options: [
'Circle',
'Crescent',
'Triangle',
'Stars'
],
attrs: {
class: 'select-example-basic',
name: 'shapes'
}
}
},
{
name: 'date_col',
editable: {
input: 'text',
attrs: {
class: 'datepicker-example form-control form-control-sm'
}
}
},
{
name: 'delete',
template: function(row) {
return "<td><button data-action='delete'><i class='fa fa-trash'></i></button></td>"
},
}
],
edit: {
done: function() {
test.ok(true, 'must call twice')
}
},
add: true
})
.on('editmode', function () {
test.equals($('.select-example-basic').length, 100)
test.equals($('.datepicker-example').length, 100)
$('.select-example-basic').select2()
$('.datepicker-example').datepicker({
format: 'dd-mm-yyyy',
todayHighlight: true,
autoclose: true
})
})
.on('load', function () {
// editing rows with no click must also update values
var init_cell_value = $(".edit-fh table > tbody > tr:nth-child(1) > td:nth-child(2) > a.urlfilter").text().trim()
var stripe_cell_value = $('.edit-fh table tbody tr:nth-child(1) td:nth-child(4) a').text().trim()
var c1_cell_value = $('.edit-fh table tbody tr:nth-child(1) td:nth-child(3) a').text().trim()
$('.edit button').click()
// make sure the initial value is Europe
test.equals($(".edit-fh table > tbody > tr:nth-child(1) > td:nth-child(2) > input").val().trim(), init_cell_value)
// modify cell value inside <input>
$(".edit-fh table > tbody > tr:nth-child(1) > td:nth-child(2) > input").val('Edited')
$(".edit-fh table > tbody > tr:nth-child(1) > td:nth-child(2) > input").trigger('change')
test.equals($(".edit-fh table > tbody > tr:nth-child(1) > td:nth-child(2) > input").val().trim(), 'Edited')
// save row
$('.edit button').click()
test.equals($(".edit-fh table > tbody > tr:nth-child(1) > td:nth-child(2) > a.urlfilter").text().trim(), 'Edited')
// reset values
$('.edit button').click()
$(".edit-fh table > tbody > tr:nth-child(1) > td:nth-child(2) > input").val(init_cell_value)
$(".edit-fh table > tbody > tr:nth-child(1) > td:nth-child(2) > input").trigger('change')
$('.edit button').click()
test.equals($(".edit-fh table > tbody > tr:nth-child(1) > td:nth-child(2) > a.urlfilter").text().trim(), init_cell_value)
// make sure all other field values are restored
test.equals($('.edit-fh table tbody tr:nth-child(1) td:nth-child(4) a').text().trim(), stripe_cell_value)
test.equals($('.edit-fh table tbody tr:nth-child(1) td:nth-child(3) a').text().trim(), c1_cell_value)
})
})
</script>
</body>
</html>
/* eslint no-console: 0 */
const path = require('path')
const express = require('express')
const glob = require('glob')
const http = require("http")
var router = express.Router()
var g1 = require('./../dist/g1.js')
var fs = require('fs')
const port = process.argv.length <= 2 ? 1112 : 1113
var data = JSON.parse(fs.readFileSync('./test/formhandler_csv.json', { encoding: "utf8" }))
const app = express()
.use(express.static(path.resolve(__dirname, '..')))
var formhandler_json_data = JSON.parse(fs.readFileSync('./test/formhandler_csv.json', { encoding: "utf8" }));
app.route('/formhandler-data').get(function(req, res, next) {
app.route('/formhandler-data').get(function(req, res) {
var url = req.protocol + '://' + req.get('host') + req.originalUrl
res.send(g1.datafilter(formhandler_json_data, g1.url.parse(url).searchList, 'fhname13'))
res.send(g1.datafilter(data, g1.url.parse(url).searchList, 'fhname13'))
})
app.route('/data-list').get(function(req, res, next) {
app.route('/data-list').get(function(req, res) {
res.send(['bengaluru "', 'hyderabad', 'mumbai', 'delhi'])
})
async function run_puppeteer() {
const puppeteer = require('puppeteer')
const browser = await puppeteer.launch({
// On Gitlab CI, running as root without --no-sandbox is not supported
args: ['--no-sandbox']
})
const page = await browser.newPage()
page.on('console', msg => console.log(msg.text)) // eslint-disable-line no-console
const paths = glob.sync('test/test-*.html')
for (let i = 0; i < paths.length; i++) {
let url = 'http://localhost:' + port + '/' + paths[i]
await page.goto(url)
try {
await page.waitForFunction('window.renderComplete')
} catch (e) {
console.log('not ok ' + paths[i])
}
}
await browser.close()
server.close()
}
async function run_selenium(browser) {
const { Builder } = require('selenium-webdriver')
const driver = await new Builder().forBrowser(browser).build()
const paths = glob.sync('test/test-*.html')
for (let i = 0; i < paths.length; i++) {
let url = 'http://localhost:' + port + '/' + paths[i]
try {
await driver.get(url)
let logs = await driver.wait((driver) => driver.executeScript('return window.renderComplete'), 30000)
console.log(logs.join(''))
} catch (e) {
console.log('not ok ' + paths[i])
}
}
await driver.quit()
server.close()
}
const server = app.listen(port, function () {
// If run as "node server.js", start the HTTP server for manual testing
if (process.argv.length <= 2)
console.log('Server running on port ' + port) // eslint-disable-line no-console
// If run as "node server.js file1 file2", run puppetteer on each and show console log
// If run as "node server.js <browser>", run browser on each and show console log
else {
(async () => {
const puppeteer = require('puppeteer')
const browser = await puppeteer.launch({
args: ['--no-sandbox'] // On Gitlab CI, running as root without --no-sandbox is not supported
})
const page = await browser.newPage()
page.on('console', msg => console.log(msg.text))
const paths = glob.sync('test/test-*.html')
for (let i = 0; i < paths.length; i++) {
let url = 'http://localhost:' + port + '/' + paths[i]
await page.goto(url)
await page.waitForFunction('window.renderComplete');
}
await browser.close()
server.close()
})()
let browser = process.argv[2].toLowerCase()
if (browser == 'puppeteer')
run_puppeteer()
else
run_selenium(browser)
}
})
/* globals tape */
/* eslint no-console: 0 */
// geckodriver does not support accessing logs
// https://github.com/mozilla/geckodriver/issues/330
// So pipe console.log to a buffer that is retrieved via renderComplete
var _tape_buffer = []
tape.createStream().on('data', function(v) {
console.log(v)
_tape_buffer.push(v)
})
tape.onFinish(function () {
window.renderComplete = _tape_buffer
})
......@@ -5,12 +5,10 @@
<script src="../node_modules/jquery/dist/jquery.min.js"></script>
<script src="../node_modules/lodash/lodash.min.js"></script>
<script src="../dist/ajax.min.js"></script>
<script src="tape.js"></script>
<script src="tape-stream.js"></script>
</head>
<body>
<script src="tape.js"></script>
<script>
tape.onFinish(function () { window.renderComplete = true })
</script>
<script>
var urls = ["ajaxchain/a1.json", "ajaxchain/a2.json", "ajaxchain/a3.json"]
tape('$.ajaxchain fetches chained URLs based on list of files', function (t) {
......
......@@ -5,6 +5,10 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>DataFilter test</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="../dist/g1.min.js"></script>
<script src="../node_modules/jquery/dist/jquery.min.js"></script>
<script src="tape.js"></script>
<script src="tape-stream.js"></script>
<style>
pre { white-space: pre-wrap; }
</style>
......@@ -19,8 +23,6 @@
<h5>Small Dataset</h5>
<pre id="result_ds2"></pre>
<script src="../dist/g1.min.js"></script>
<script src="../node_modules/jquery/dist/jquery.min.js"></script>
<script>
// reading filters from url
var filter = g1.url.parse(window.location.href).searchList
......@@ -34,9 +36,5 @@
$('#result_ds2').text(JSON.stringify(g1.datafilter(data, filter, 'ds2')))
})
</script>
<script src="tape.js"></script>
<script>
tape.onFinish(function () { window.renderComplete = true })
</script>
</body>
</html>
......@@ -12,12 +12,9 @@
<script src="../dist/dropdown.min.js"></script>
<script src="../dist/g1.min.js"></script>
<script src="tape.js"></script>
<script src="tape-stream.js"></script>
</head>
<body>
<script>
tape.onFinish(function () { window.renderComplete = true })
</script>
<div class="m-4">
<div class="container1 w-25 m-3"></div>
<div class="container2 w-25 m-3"></div>
......@@ -72,7 +69,7 @@
t.end()
})
.dropdown({
url: '/data-list',
url: 'data-list.json',
target: '#',
key: 'loc'
})
......
......@@ -5,12 +5,9 @@
<script src="../node_modules/jquery/dist/jquery.min.js"></script>
<script src="../dist/event.min.js"></script>
<script src="tape.js"></script>
<script src="tape-stream.js"></script>
</head>
<body>
<script>
tape.onFinish(function () { window.renderComplete = true })
</script>
<p>
<code>$el.dispatch('event')</code> fires a native event on a target.
</p>
......
......@@ -30,13 +30,10 @@
<script src="../node_modules/d3-scale-chromatic/dist/d3-scale-chromatic.js"></script>
<script src="../dist/formhandler.min.js"></script>
<script src="tape.js"></script>
<script src="tape-stream.js"></script>
</head>
<body>
<script>
tape.onFinish(function () { window.renderComplete = true })
</script>
<div class="external-api"></div>
<div class="external-api2"></div>
......
......@@ -31,13 +31,10 @@
<script src="../node_modules/d3/build/d3.js"></script>
<script src="../node_modules/d3-scale-chromatic/dist/d3-scale-chromatic.js"></script>
<script src="tape.js"></script>
<script src="tape-stream.js"></script>
</head>
<body>
<script>
tape.onFinish(function () { window.renderComplete = true })
</script>
<div id="city_table"> </div>
<div class="icon-test" data-table="grid" data-src="/formhandler-data"></div>
<div class="icon-test2" data-table="grid" data-src="/formhandler-data"></div>
......
......@@ -30,14 +30,10 @@
<script src="../node_modules/d3-scale-chromatic/dist/d3-scale-chromatic.js"></script>
<script src="../dist/formhandler.min.js"></script>
<script src="tape.js"></script>
<script src="tape-stream.js"></script>
</head>
<body>
<script>
tape.onFinish(function () { window.renderComplete = true })
</script>
<div class="delete_btn" data-src="/formhandler-data"></div>
<div class="test-star-without-title" data-src="/formhandler-data"></div>
<div class="test-star-with-title" data-src="/formhandler-data"></div>
......
......@@ -30,13 +30,10 @@
<script src="../node_modules/d3-scale-chromatic/dist/d3-scale-chromatic.js"></script>
<script src="../dist/g1.min.js"></script>
<script src="tape.js"></script>
<script src="tape-stream.js"></script>
</head>
<body>
<script>
tape.onFinish(function () { window.renderComplete = true })
</script>
<div class="hide_col3" data-src="/test/formhandler_csv.json"></div>
<div class="hide_col2" data-src="/test/formhandler_csv.json"></div>
<div class="hide_col" data-src="/test/formhandler_csv.json"></div>
......
......@@ -6,12 +6,9 @@
<script src="../node_modules/leaflet/dist/leaflet.js"></script>
<script src="../dist/g1.min.js"></script>
<script src="tape.js"></script>
<script src="tape-stream.js"></script>
</head>
<body>
<script>
tape.onFinish(function () { window.renderComplete = true })
</script>
<script>
tape('g1 attributes exist', function(t) {
t.ok(typeof g1 == 'object', 'g1')
......
......@@ -5,16 +5,13 @@
<script src="../node_modules/jquery/dist/jquery.min.js"></script>
<script src="../dist/highlight.min.js"></script>
<script src="tape.js"></script>
<script src="tape-stream.js"></script>
<style>
.active { color: red; }
.blue { color: blue; }
</style>
</head>
<body>
<script>
tape.onFinish(function () { window.renderComplete = true })
</script>
<div class="container1">
<div data-toggle="highlight" class="trigger1" data-target=".target1">trigger1</div>
<em class="target1">target1</em>
......
......@@ -7,6 +7,7 @@
<title>MapViewer</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="tape.js"></script>
<script src="tape-stream.js"></script>
<link rel="stylesheet" href="../node_modules/leaflet/dist/leaflet.css">
<script src="../node_modules/jquery/dist/jquery.min.js"></script>
<script src="../node_modules/leaflet/dist/leaflet.js"></script>
......@@ -24,12 +25,6 @@
</head>
<body>
<script>
tape.onFinish(function () {
window.renderComplete = true
})
</script>
<div id="choropleth" class="map"></div>
<div id="choropleth_topojson" class="map"></div>
......
......@@ -7,6 +7,7 @@
<title>Drilldown MapViewer tests</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="tape.js"></script>
<script src="tape-stream.js"></script>
<link rel="stylesheet" href="../node_modules/leaflet/dist/leaflet.css">
<script src="../node_modules/jquery/dist/jquery.min.js"></script>
<script src="../node_modules/leaflet/dist/leaflet.js"></script>
......@@ -17,10 +18,6 @@
</head>
<body>
<script>
tape.onFinish(function () { window.renderComplete = true })
</script>
<div id="drilldown-map" style="height:400px"></div>
<script>
var drilldown_map = g1.mapviewer({
......
......@@ -7,6 +7,7 @@
<title>MapViewer</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="tape.js"></script>
<script src="tape-stream.js"></script>
<link rel="stylesheet" href="../node_modules/leaflet/dist/leaflet.css">
<script src="../node_modules/jquery/dist/jquery.min.js"></script>
<script src="../node_modules/leaflet/dist/leaflet.js"></script>
......@@ -22,9 +23,6 @@
</head>
<body>
<script>
tape.onFinish(function () { window.renderComplete = true })
</script>
<div id="ml-1" class="map"></div>
<div id="ml-2" class="map"></div>
......
......@@ -7,6 +7,7 @@
<title>MapViewer</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="tape.js"></script>
<script src="tape-stream.js"></script>
<link rel="stylesheet" href="../node_modules/leaflet/dist/leaflet.css">
<script src="../node_modules/jquery/dist/jquery.min.js"></script>
<script src="../node_modules/leaflet/dist/leaflet.js"></script>
......@@ -16,9 +17,6 @@
</head>
<body>
<script>
tape.onFinish(function () { window.renderComplete = true })
</script>
<div id="popup-map" style="height:300px"></div>
<script>
......
......@@ -7,6 +7,7 @@
<title>MapViewer</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="tape.js"></script>
<script src="tape-stream.js"></script>