Commit 7de90169 authored by Aran Gilman's avatar Aran Gilman Committed by Commit Bot

Refactor Reader Mode's JavaScript tests to use Mocha.

The tests did not previously use an established test framework, making
tasks like executing certain code before or after every test and testing
asynchronous code more difficult. It should also be easier for
developers familiar with JavaScript testing in general and WebUI testing
in particular to read and write JavaScript tests for DOM Distiller and
Reader Mode.

Follow-up work includes replacing the custom assert methods with Chai,
as well as writing more tests to cover existing functionality.

Bug: 1027612
Change-Id: I39acc923a0424881d2694281d9aa0323c8f9a913
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1992504Reviewed-by: default avatarWei-Yin Chen (陳威尹) <wychen@chromium.org>
Commit-Queue: Aran Gilman <gilmanmh@google.com>
Cr-Commit-Position: refs/heads/master@{#762551}
parent 70c0a780
......@@ -561,6 +561,7 @@ if (!is_ios && !is_fuchsia) {
"security_state/content/testdata/",
"//content/test/data/",
"//third_party/dom_distiller_js/dist/test/data/",
"//third_party/mocha",
]
deps = [
......
......@@ -22,22 +22,6 @@
namespace dom_distiller {
namespace {
base::Value ExecuteJsScript(content::WebContents* web_contents,
const std::string& script) {
base::Value result;
base::RunLoop run_loop;
web_contents->GetMainFrame()->ExecuteJavaScriptForTests(
base::UTF8ToUTF16(script),
base::BindOnce(
[](base::Closure callback, base::Value* out, base::Value result) {
(*out) = std::move(result);
callback.Run();
},
run_loop.QuitClosure(), &result));
run_loop.Run();
return result;
}
class DistilledPageJsTest : public content::ContentBrowserTest {
protected:
explicit DistilledPageJsTest()
......@@ -53,14 +37,13 @@ class DistilledPageJsTest : public content::ContentBrowserTest {
distilled_page_ = SetUpTestServerWithDistilledPage(embedded_test_server());
}
void LoadAndExecuteTestScript(const std::string& file,
const std::string& fixture_name) {
void LoadAndExecuteTestScript(const std::string& file) {
distilled_page_->AppendScriptFile(file);
distilled_page_->Load(embedded_test_server(), shell()->web_contents());
const base::Value result = ExecuteJsScript(
shell()->web_contents(), base::StrCat({fixture_name, ".run()"}));
ASSERT_EQ(base::Value::Type::BOOLEAN, result.type());
EXPECT_TRUE(result.GetBool());
bool allTestsPassed;
ASSERT_TRUE(content::ExecuteScriptAndExtractBool(
shell()->web_contents(), "mocha.run()", &allTestsPassed));
EXPECT_TRUE(allTestsPassed);
}
std::unique_ptr<FakeDistilledPage> distilled_page_;
......@@ -72,7 +55,7 @@ class DistilledPageJsTest : public content::ContentBrowserTest {
#define MAYBE_Pinch Pinch
#endif
IN_PROC_BROWSER_TEST_F(DistilledPageJsTest, MAYBE_Pinch) {
LoadAndExecuteTestScript("pinch_tester.js", "pinchtest");
LoadAndExecuteTestScript("pinch_tester.js");
}
} // namespace
......
......@@ -39,10 +39,12 @@ const char* kDistilledPagePath = "/distilled_page.html";
void SetUpTestServerWithoutStarting(EmbeddedTestServer* server) {
FilePath root_dir;
PathService::Get(base::DIR_SOURCE_ROOT, &root_dir);
server->ServeFilesFromDirectory(
root_dir.AppendASCII("components/test/data/dom_distiller"));
server->ServeFilesFromDirectory(
root_dir.AppendASCII("components/dom_distiller/core/javascript"));
server->ServeFilesFromDirectory(
root_dir.AppendASCII("components/test/data/dom_distiller"));
server->ServeFilesFromDirectory(root_dir.AppendASCII("third_party/mocha"));
}
} // namespace
......@@ -58,6 +60,8 @@ FakeDistilledPage::FakeDistilledPage(EmbeddedTestServer* server)
// DomDistillerRequestViewBase::SendCommonJavaScript(); however, this method
// is impractical to use in testing.
AppendScriptFile("dom_distiller_viewer.js");
AppendScriptFile("mocha.js");
AppendScriptFile("test_util.js");
}
FakeDistilledPage::~FakeDistilledPage() = default;
......
......@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// TODO(gilmanmh): Consider replacing this with dispatched touch events.
class Touch {
constructor() {
this.points = {};
......@@ -44,28 +45,25 @@ class Touch {
}
}
class PinchTest {
/** @private */
assertTrue_(condition, message) {
// TODO(gilmanmh): Replace these helpers with Chai's equivalents.
function assertTrue(condition, message) {
if (!condition) {
message = message || 'Assertion failed';
console.trace();
throw new Error(message);
}
}
}
/** @private */
assertClose_(a, b, message) {
function assertClose(a, b, message) {
if (Math.abs(a - b) > 1e-5) {
message = message || 'Assertion failed';
console.log('"', a, '" and "', b, '" are not close.');
console.trace();
throw new Error(message);
}
}
}
/** @private */
isEquivalent_(a, b) {
function isEquivalent(a, b) {
// Create arrays of property names
const aProps = Object.getOwnPropertyNames(a);
const bProps = Object.getOwnPropertyNames(b);
......@@ -89,19 +87,19 @@ class PinchTest {
// If we made it this far, objects
// are considered equivalent
return true;
}
}
/** @private */
assertEqual_(a, b, message) {
if (!this.isEquivalent_(a, b)) {
function assertEqual(a, b, message) {
if (!isEquivalent(a, b)) {
message = message || 'Assertion failed';
console.log('"', a, '" and "', b, '" are not equal');
console.trace();
throw new Error(message);
}
}
}
testZoomOut() {
suite('Pincher', function() {
test('Zoom Out', function() {
pincher.reset();
const t = new Touch();
......@@ -109,57 +107,57 @@ class PinchTest {
let oldState = pincher.status();
t.addTouchPoint(100, 100);
pincher.handleTouchStart(t.events());
this.assertEqual_(oldState, pincher.status());
assertEqual(oldState, pincher.status());
t.addTouchPoint(300, 300);
pincher.handleTouchStart(t.events());
this.assertEqual_(oldState, pincher.status());
assertEqual(oldState, pincher.status());
// Make sure extra move event doesn't change state
pincher.handleTouchMove(t.events());
this.assertEqual_(oldState, pincher.status());
assertEqual(oldState, pincher.status());
t.updateTouchPoint(0, 150, 150);
t.updateTouchPoint(1, 250, 250);
pincher.handleTouchMove(t.events());
this.assertTrue_(pincher.status().clampedScale < 0.9);
assertTrue(pincher.status().clampedScale < 0.9);
// Make sure end event doesn't change state
oldState = pincher.status();
t.releaseTouchPoint(1);
pincher.handleTouchEnd(t.events());
this.assertEqual_(oldState, pincher.status());
assertEqual(oldState, pincher.status());
t.releaseTouchPoint(0);
pincher.handleTouchEnd(t.events());
this.assertEqual_(oldState, pincher.status());
}
assertEqual(oldState, pincher.status());
});
testZoomIn() {
test('Zoom In', function() {
pincher.reset();
const t = new Touch();
let oldState = pincher.status();
t.addTouchPoint(150, 150);
pincher.handleTouchStart(t.events());
this.assertEqual_(oldState, pincher.status());
assertEqual(oldState, pincher.status());
t.addTouchPoint(250, 250);
pincher.handleTouchStart(t.events());
this.assertEqual_(oldState, pincher.status());
assertEqual(oldState, pincher.status());
t.updateTouchPoint(0, 100, 100);
t.updateTouchPoint(1, 300, 300);
pincher.handleTouchMove(t.events());
this.assertTrue_(pincher.status().clampedScale > 1.1);
assertTrue(pincher.status().clampedScale > 1.1);
oldState = pincher.status();
t.releaseTouchPoint(1);
pincher.handleTouchEnd(t.events());
this.assertEqual_(oldState, pincher.status());
assertEqual(oldState, pincher.status());
t.releaseTouchPoint(0);
pincher.handleTouchEnd(t.events());
this.assertEqual_(oldState, pincher.status());
}
assertEqual(oldState, pincher.status());
});
testZoomOutAndPan() {
test('Zomm Out And Pan', function() {
pincher.reset();
const t = new Touch();
t.addTouchPoint(100, 100);
......@@ -177,12 +175,12 @@ class PinchTest {
t.releaseTouchPoint(0);
pincher.handleTouchEnd(t.events());
this.assertClose_(pincher.status().shiftX, 10);
this.assertClose_(pincher.status().shiftY, -5);
this.assertTrue_(pincher.status().clampedScale < 0.9);
}
assertClose(pincher.status().shiftX, 10);
assertClose(pincher.status().shiftY, -5);
assertTrue(pincher.status().clampedScale < 0.9);
});
testReversible() {
test('Reversible', function() {
pincher.reset();
const t = new Touch();
t.addTouchPoint(100, 100);
......@@ -207,26 +205,26 @@ class PinchTest {
pincher.handleTouchEnd(t.events());
t.releaseTouchPoint(0);
pincher.handleTouchEnd(t.events());
this.assertClose_(pincher.status().clampedScale, 1);
}
assertClose(pincher.status().clampedScale, 1);
});
testMultitouchZoomOut() {
test('Multitouch Zoom Out', function() {
pincher.reset();
const t = new Touch();
let oldState = pincher.status();
t.addTouchPoint(100, 100);
pincher.handleTouchStart(t.events());
this.assertEqual_(oldState, pincher.status());
assertEqual(oldState, pincher.status());
t.addTouchPoint(300, 300);
pincher.handleTouchStart(t.events());
this.assertEqual_(oldState, pincher.status());
assertEqual(oldState, pincher.status());
t.addTouchPoint(100, 300);
pincher.handleTouchStart(t.events());
this.assertEqual_(oldState, pincher.status());
assertEqual(oldState, pincher.status());
t.addTouchPoint(300, 100);
pincher.handleTouchStart(t.events());
this.assertEqual_(oldState, pincher.status());
assertEqual(oldState, pincher.status());
// Multi-touch zoom out.
t.updateTouchPoint(0, 150, 150);
......@@ -238,60 +236,60 @@ class PinchTest {
oldState = pincher.status();
t.releaseTouchPoint(3);
pincher.handleTouchEnd(t.events());
this.assertEqual_(oldState, pincher.status());
assertEqual(oldState, pincher.status());
t.releaseTouchPoint(2);
pincher.handleTouchEnd(t.events());
this.assertEqual_(oldState, pincher.status());
assertEqual(oldState, pincher.status());
t.releaseTouchPoint(1);
pincher.handleTouchEnd(t.events());
this.assertEqual_(oldState, pincher.status());
assertEqual(oldState, pincher.status());
t.releaseTouchPoint(0);
pincher.handleTouchEnd(t.events());
this.assertEqual_(oldState, pincher.status());
assertEqual(oldState, pincher.status());
this.assertTrue_(pincher.status().clampedScale < 0.9);
}
assertTrue(pincher.status().clampedScale < 0.9);
});
testZoomOutThenMulti() {
test('Zoom Out Then Multi', function() {
pincher.reset();
const t = new Touch();
let oldState = pincher.status();
t.addTouchPoint(100, 100);
pincher.handleTouchStart(t.events());
this.assertEqual_(oldState, pincher.status());
assertEqual(oldState, pincher.status());
t.addTouchPoint(300, 300);
pincher.handleTouchStart(t.events());
this.assertEqual_(oldState, pincher.status());
assertEqual(oldState, pincher.status());
// Zoom out.
t.updateTouchPoint(0, 150, 150);
t.updateTouchPoint(1, 250, 250);
pincher.handleTouchMove(t.events());
this.assertTrue_(pincher.status().clampedScale < 0.9);
assertTrue(pincher.status().clampedScale < 0.9);
// Make sure adding and removing more point doesn't change state
oldState = pincher.status();
t.addTouchPoint(600, 600);
pincher.handleTouchStart(t.events());
this.assertEqual_(oldState, pincher.status());
assertEqual(oldState, pincher.status());
t.releaseTouchPoint(2);
pincher.handleTouchEnd(t.events());
this.assertEqual_(oldState, pincher.status());
assertEqual(oldState, pincher.status());
// More than two fingers.
t.addTouchPoint(150, 250);
pincher.handleTouchStart(t.events());
t.addTouchPoint(250, 150);
pincher.handleTouchStart(t.events());
this.assertEqual_(oldState, pincher.status());
assertEqual(oldState, pincher.status());
t.updateTouchPoint(0, 100, 100);
t.updateTouchPoint(1, 300, 300);
t.updateTouchPoint(2, 100, 300);
t.updateTouchPoint(3, 300, 100);
pincher.handleTouchMove(t.events());
this.assertClose_(pincher.status().scale, 1);
assertClose(pincher.status().scale, 1);
oldState = pincher.status();
t.releaseTouchPoint(3);
......@@ -299,10 +297,10 @@ class PinchTest {
t.releaseTouchPoint(1);
t.releaseTouchPoint(0);
pincher.handleTouchEnd(t.events());
this.assertEqual_(oldState, pincher.status());
}
assertEqual(oldState, pincher.status());
});
testCancel() {
test('Cancel', function() {
pincher.reset();
const t = new Touch();
......@@ -313,13 +311,13 @@ class PinchTest {
t.updateTouchPoint(0, 150, 150);
t.updateTouchPoint(1, 250, 250);
pincher.handleTouchMove(t.events());
this.assertTrue_(pincher.status().clampedScale < 0.9);
assertTrue(pincher.status().clampedScale < 0.9);
const oldState = pincher.status();
t.releaseTouchPoint(1);
t.releaseTouchPoint(0);
pincher.handleTouchCancel(t.events());
this.assertEqual_(oldState, pincher.status());
assertEqual(oldState, pincher.status());
t.addTouchPoint(150, 150);
pincher.handleTouchStart(t.events());
......@@ -328,10 +326,10 @@ class PinchTest {
t.updateTouchPoint(0, 100, 100);
t.updateTouchPoint(1, 300, 300);
pincher.handleTouchMove(t.events());
this.assertClose_(pincher.status().clampedScale, 1);
}
assertClose(pincher.status().clampedScale, 1);
});
testSingularity() {
test('Singularity', function() {
pincher.reset();
const t = new Touch();
......@@ -342,14 +340,14 @@ class PinchTest {
t.updateTouchPoint(0, 150, 150);
t.updateTouchPoint(1, 50, 50);
pincher.handleTouchMove(t.events());
this.assertTrue_(pincher.status().clampedScale > 1.1);
this.assertTrue_(pincher.status().clampedScale < 100);
this.assertTrue_(pincher.status().scale < 100);
assertTrue(pincher.status().clampedScale > 1.1);
assertTrue(pincher.status().clampedScale < 100);
assertTrue(pincher.status().scale < 100);
pincher.handleTouchCancel();
}
});
testMinSpan() {
test('Min Span', function() {
pincher.reset();
const t = new Touch();
......@@ -360,17 +358,17 @@ class PinchTest {
t.updateTouchPoint(0, 100, 100);
t.updateTouchPoint(1, 100, 100);
pincher.handleTouchMove(t.events());
this.assertTrue_(pincher.status().clampedScale < 0.9);
this.assertTrue_(pincher.status().clampedScale > 0);
this.assertTrue_(pincher.status().scale > 0);
assertTrue(pincher.status().clampedScale < 0.9);
assertTrue(pincher.status().clampedScale > 0);
assertTrue(pincher.status().scale > 0);
pincher.handleTouchCancel();
}
});
testFontScaling() {
test('Font Scaling', function() {
pincher.reset();
useFontScaling(1.5);
this.assertClose_(pincher.status().clampedScale, 1.5);
assertClose(pincher.status().clampedScale, 1.5);
let t = new Touch();
......@@ -387,12 +385,11 @@ class PinchTest {
pincher.handleTouchMove(t.events());
// Verify scale is smaller.
this.assertTrue_(
pincher.status().clampedScale < 0.9 * oldState.clampedScale);
assertTrue(pincher.status().clampedScale < 0.9 * oldState.clampedScale);
pincher.handleTouchCancel();
useFontScaling(0.8);
this.assertClose_(pincher.status().clampedScale, 0.8);
assertClose(pincher.status().clampedScale, 0.8);
// Start touch.
t = new Touch();
......@@ -408,25 +405,7 @@ class PinchTest {
pincher.handleTouchMove(t.events());
// Verify scale is larger.
this.assertTrue_(
pincher.status().clampedScale > 1.1 * oldState.clampedScale);
assertTrue(pincher.status().clampedScale > 1.1 * oldState.clampedScale);
pincher.handleTouchCancel();
}
run() {
this.testZoomOut();
this.testZoomIn();
this.testZoomOutAndPan();
this.testReversible();
this.testMultitouchZoomOut();
this.testZoomOutThenMulti();
this.testCancel();
this.testSingularity();
this.testMinSpan();
this.testFontScaling();
pincher.reset();
return true;
}
}
const pinchtest = new PinchTest;
});
});
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Based on BrowserTestReporter() in //chrome/test/data/webui/mocha_adapter.js
// TODO(crbug.com/1027612): Look into using that class directly.
function TestReporter(runner) {
let passes = 0;
let failures = 0;
runner.on('pass', function(test) {
passes++;
});
runner.on('fail', function(test, err) {
failures++;
let message = 'Mocha test failed: ' + test.fullTitle() + '\n';
// Remove unhelpful mocha lines from stack trace.
if (err.stack) {
const stack = err.stack.split('\n');
for (let i = 0; i < stack.length; i++) {
if (stack[i].indexOf('mocha.js:') == -1) {
message += stack[i] + '\n';
}
}
} else {
message += err.toString();
}
console.error(message);
});
runner.on('end', function() {
window.domAutomationController.send(failures === 0 && passes > 0);
});
}
mocha.setup({
ui: 'tdd',
reporter: TestReporter,
enableTimeouts: false,
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment