From baea53826d74de53b51895015a7916e724e6aeeb Mon Sep 17 00:00:00 2001 From: Eddie Antonio Santos Date: Tue, 27 Nov 2018 18:55:38 -0700 Subject: [PATCH 01/10] Clean top-level generated JS. --- common/predictive-text/build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/predictive-text/build.sh b/common/predictive-text/build.sh index b9c7258f9d..d48a7f32fe 100755 --- a/common/predictive-text/build.sh +++ b/common/predictive-text/build.sh @@ -26,7 +26,7 @@ build-worker () { # A nice, extensible method for -clean operations. Add to this as necessary. clean ( ) { - rm -f "${EMBEDDED_WORKER}" + rm -f "${EMBEDDED_WORKER}" index.js index.js.map if [ $? -ne 0 ]; then fail "Failed to erase the prior build." fi From 8404ef3c1385e4049023b2e6bffc8734508f9998 Mon Sep 17 00:00:00 2001 From: Eddie Antonio Santos Date: Wed, 28 Nov 2018 14:20:09 -0700 Subject: [PATCH 02/10] Comment ALL THE THINGS. --- common/predictive-text/embedded_worker.d.ts | 4 +- common/predictive-text/index.ts | 43 +++++++++++++++++-- .../unit_tests/headless/lmlayer.js | 11 ++--- .../headless/worker-initialization.js | 12 +++++- .../in_browser/cases/lmlayer-integration.js | 11 +++-- common/predictive-text/worker/index.ts | 38 ++++++++-------- 6 files changed, 87 insertions(+), 32 deletions(-) diff --git a/common/predictive-text/embedded_worker.d.ts b/common/predictive-text/embedded_worker.d.ts index e2e69ee8c0..b5a1f01b5f 100644 --- a/common/predictive-text/embedded_worker.d.ts +++ b/common/predictive-text/embedded_worker.d.ts @@ -21,7 +21,9 @@ */ // Include the code intended to run WITHIN the Web Worker. -// The worker code must be compiled before this file is compiled. +// The worker code MUST be compiled before this file is compiled. +// If you see a 'File: embedded_worker.js not found.' error message, please +// compile the worker first (stage one). /// /** diff --git a/common/predictive-text/index.ts b/common/predictive-text/index.ts index 0e7de865ce..f8de3c81f2 100644 --- a/common/predictive-text/index.ts +++ b/common/predictive-text/index.ts @@ -32,20 +32,55 @@ */ type USVString = string; -// TODO: document +/** + * Top-level interface to the Language Modelling layer, or "LMLayer" for short. + * + * The Language Modelling layer provides a way for keyboards to offer prediction and + * correction functionalities. The LMLayer proper runs within a Web Worker, however, + * this class is intended to run in the main thread, and automatically spawn a Web + * Worker, capable of offering predictions. + * + * Since the Worker runs in a different thread, the public methods of this class are + * asynchronous. Methods of note include: + * + * - #initialize() -- initialize the LMLayer with a configuration and language model + * - #predict() -- ask the LMLayer to offer suggestions (predictions or corrections) for + * the input event + * + * The top-level LMLayer will automatically starts up its own Web Worker. + */ class LMLayer { /** * The underlying worker instance. By default, this is the LMLayerWorker. */ private _worker: Worker; + /** + * Construct the top-level LMLayer interface. This also starts the underlying Worker. + * Make sure to call .initialize() when using the default Worker. + * + * @param uri URI of the underlying LMLayer worker code. This will usually be a blob: + * or file: URI. If uri is not provided, this will start the default Worker. + */ constructor(uri?: string) { this._worker = new Worker(uri || LMLayer.asBlobURI(LMLayerWorkerCode)); } + // TODO: asynchronous initialize() method, based on + // https://github.com/eddieantonio/keyman-lmlayer-prototype/blob/f8e6268b03190d08cf5d35f9428cf9150d6d219e/index.ts#L42-L62 + + // TODO: asynchronous predict() method, based on + // https://github.com/eddieantonio/keyman-lmlayer-prototype/blob/f8e6268b03190d08cf5d35f9428cf9150d6d219e/index.ts#L64-L80 + + // TODO: asynchronous close() method. + // Worker code must recognize message and call self.close(). + /** - * Given a function, this utility returns the source code within it. - * @param fn + * Given a function, this utility returns the source code within it, as a string. + * This is intended to unwrap the "wrapped" source code created in the LMLayerWorker + * build process. + * + * @param fn The function whose body will be returned. */ static unwrap(fn: Function): string { let wrapper = fn.toString(); @@ -74,7 +109,7 @@ class LMLayer { } } -// Let LMLayer be available both in browser and in Node. +// Let LMLayer be available both in the browser and in Node. if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') { module.exports = LMLayer; } else { diff --git a/common/predictive-text/unit_tests/headless/lmlayer.js b/common/predictive-text/unit_tests/headless/lmlayer.js index 86ea2ce5fa..f58a471469 100644 --- a/common/predictive-text/unit_tests/headless/lmlayer.js +++ b/common/predictive-text/unit_tests/headless/lmlayer.js @@ -3,6 +3,8 @@ var sinon = require('sinon'); let LMLayer = require('../../'); +// Test the top-level LMLayer interface. +// Note: these tests can only be run after BOTH stages of compilation are completed. describe('LMLayer', function() { describe('[[constructor]]', function () { it.skip('should be take a URI to instantiate', function () { @@ -10,17 +12,16 @@ describe('LMLayer', function() { }); }); - /** - * .unwrap() is a static function that unwraps some function code. - */ + // Since the Blob API is browser-specific, look for those tests + // for .asBlobURI() in the in_browser tests. describe('.unwrap', function () { - // Since the Blob API is DOM-specific, look for those tests - // in the in_browser tests. it('should return the inner code of a function', function () { + // Create a multi-line function body we can match in a RegExp. let text = LMLayer.unwrap(function hello() { var hello; var world; }); + // Unwrap should give us back ONLY the body. Whitespace isn't really important. assert.match(text, /^\s*var\s+hello;\s*var\s+world;\s*$/); }); }); diff --git a/common/predictive-text/unit_tests/headless/worker-initialization.js b/common/predictive-text/unit_tests/headless/worker-initialization.js index e644e662ef..f5b7a3aa77 100644 --- a/common/predictive-text/unit_tests/headless/worker-initialization.js +++ b/common/predictive-text/unit_tests/headless/worker-initialization.js @@ -3,8 +3,12 @@ var sinon = require('sinon'); let LMLayerWorker = require('../../worker'); +// Unit tests for instantiating and initializing the LMLayer Worker in isolation. +// +// Although the LMLayerWorker expected to be used inside a DedicatedWorkerGlobalScope, +// these unit tests DO NOT run inside a Worker, and instead use Sinon fakes to assert +// behavior. describe('LMLayerWorker', function() { - describe('#constructor()', function() { it('should allow for the mocking of postMessage()', function () { var fakePostMessage = sinon.fake(); @@ -147,6 +151,7 @@ describe('LMLayerWorker', function() { }); }); + // TODO: move these tests to a different file. describe('Message: predict', function () { it.skip('should predict from a local model', function () { // will need import scripts figured out @@ -161,6 +166,11 @@ describe('LMLayerWorker', function() { return { data }; } + /** + * Deprecation warning: Soon, models will not be requierd to be passed as source code; + * when this happens, tests should refrain from sending source code for the model + * parameter. + */ function dummyModelCode() { return 'return {model: {}, configuration: {}}'; } diff --git a/common/predictive-text/unit_tests/in_browser/cases/lmlayer-integration.js b/common/predictive-text/unit_tests/in_browser/cases/lmlayer-integration.js index a37bfed7a4..f995fc1d20 100644 --- a/common/predictive-text/unit_tests/in_browser/cases/lmlayer-integration.js +++ b/common/predictive-text/unit_tests/in_browser/cases/lmlayer-integration.js @@ -8,12 +8,15 @@ describe('LMLayer', function () { }); }); - describe('#asBlobURI', function () { + describe('#asBlobURI()', function () { + // #asBlobURI() requires browser APIs, hence why it cannot be tested headless in Node. it('should take a function and convert it into a blob function', function (done) { let uri = LMLayer.asBlobURI(function dummyHandler() { - // Post something weird, so we can be reasonably certain it's not a fluke. - // WARNING: Do NOT factor out this string as a variable. - // It MUST remain a string in this function body, because it gets stringified! + // Post something weird, so we can be reasonably certain the Web Worker is... + // well, working. + // WARNING: Do NOT refactor this string as a variable. It **MUST** remain a string + // in this function body, because the code in this function's body gets + // stringified! postMessage('fhqwhgads'); }); assert.match(uri, /^blob:/); diff --git a/common/predictive-text/worker/index.ts b/common/predictive-text/worker/index.ts index 55473f4c14..33ca33151f 100644 --- a/common/predictive-text/worker/index.ts +++ b/common/predictive-text/worker/index.ts @@ -20,33 +20,37 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/** + * The signature of self.postMessage(), so that unit tests can mock it. + */ type PostMessage = typeof DedicatedWorkerGlobalScope.prototype.postMessage; + +/** + * The valid outgoing message types. + */ type OutgoingMessageKind = 'ready' | 'suggestions'; +/** + * The structure of an initialization message. It should include the model (either in + * source code or parameter form), as well as the keyboard's capabilities. + */ interface InitializeMessage { /** - * Source code of the model. - * TODO: write a description of what this source code should look like. + * The model, and its configuration. + * TODO: write a description of what this actually is! */ - model: string; - /** - * The configuration that the keyboard can offer to the model. - */ - configuration: RequestedConfiguration; -} - -interface InitializeMessage { - /** - * Source code of the model. - * TODO: write a description of what this source code should look like. - */ - model: string; + model: any; + /** * The configuration that the keyboard can offer to the model. */ + // TODO: rename to capabilities? They **are** the capabilities of the keyboard's platform. configuration: RequestedConfiguration; } +/** + * The structure of the message back to the keyboard. + */ interface ReadyMessage { configuration: ModelConfiguration; } @@ -104,7 +108,7 @@ interface ModelConfiguration { rightContextCodeUnits: number; }; -// TODO: +// TODO: define what valid values of the model are. interface Model {}; /** @@ -222,7 +226,7 @@ class LMLayerWorker { } } -// Let LMLayerWorker be available both in browser and in Node. +// Let LMLayerWorker be available both in the browser and in Node. if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') { module.exports = LMLayerWorker; } else if (typeof self !== 'undefined' && 'postMessage' in self) { From 61d2ea538100b4b7a5e57c40214c21e5bd1a97fe Mon Sep 17 00:00:00 2001 From: Eddie Antonio Santos Date: Wed, 28 Nov 2018 14:22:36 -0700 Subject: [PATCH 03/10] Remove test model for abandoned model specification. --- .../models/en-x-test-teapot.js | 55 ------------------- 1 file changed, 55 deletions(-) delete mode 100644 common/predictive-text/models/en-x-test-teapot.js diff --git a/common/predictive-text/models/en-x-test-teapot.js b/common/predictive-text/models/en-x-test-teapot.js deleted file mode 100644 index ac225e89f4..0000000000 --- a/common/predictive-text/models/en-x-test-teapot.js +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2018 National Research Council Canada (author: Eddie A. Santos) - * Copyright (c) 2018 SIL International - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/** - * This is a test model that simply returns "teapot" when given the context of - * «I'm a little ». - */ -registerModel(function () { - return { - predict(context, transform) { - return [ - { - transform: { - insert: 'teapot', - delete: transform.insert.length, - deleteRight: 0, - }, - displayAs: '🍵', - weight: 0.00, - } - ]; - - /* TODO: - if (context.wordsLeft === ["I'm", "a", "little"] && - transform.insert === 't') { - } - - return []; - */ - }, - - configuration: { - } - }; -}); -/*global registerModel*/ From ef452b8c5875da06982f1d6250f4c0d1230c8f30 Mon Sep 17 00:00:00 2001 From: Eddie Antonio Santos Date: Wed, 28 Nov 2018 14:23:14 -0700 Subject: [PATCH 04/10] Source the utilities in resources/ in the build.sh script. --- common/predictive-text/build.sh | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/common/predictive-text/build.sh b/common/predictive-text/build.sh index d48a7f32fe..207c79035a 100755 --- a/common/predictive-text/build.sh +++ b/common/predictive-text/build.sh @@ -6,6 +6,11 @@ EMBEDDED_WORKER=embedded_worker.js +# Include some helper functions from resources +# shellcheck source=../../resources/shellHelperFunctions.sh +. "$(dirname "$0")/../../resources/shellHelperFunctions.sh" + + # Build the worker and the main script. build ( ) { # Build worker first; the main file depends on it. @@ -42,21 +47,6 @@ display_usage ( ) { echo " -test runs unit and integration tests after building" } -# Prints a nice, common error message. -fail ( ) { - # TODO: source shellHelperFunctions.sh - local ERROR_RED - local NORMAL - ERROR_RED="$(tput setaf 1)" - NORMAL="$(tput sgr0)" - FAILURE_MSG="$1" - if [[ "$FAILURE_MSG" == "" ]]; then - FAILURE_MSG="Unknown failure." - fi - echo "$0: ${ERROR_RED}$FAILURE_MSG${NORMAL}" - exit 1 -} - unit-test ( ) { npm run mocha -- --recursive ./unit_tests/headless/*.js } From 8b7c606dd2c55abf1fdc543ced7e9f6590541e25 Mon Sep 17 00:00:00 2001 From: Eddie Antonio Santos Date: Wed, 28 Nov 2018 14:24:25 -0700 Subject: [PATCH 05/10] Remove TODOs from README. --- common/predictive-text/README.md | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/common/predictive-text/README.md b/common/predictive-text/README.md index 5f41cea69e..6518c4ed25 100644 --- a/common/predictive-text/README.md +++ b/common/predictive-text/README.md @@ -389,20 +389,3 @@ keyboard to acknowledge late suggestions, or for the LMLayer to avoid sending late `suggestions` messages. In either case, a `suggestions` message can be identified as appropriate or "late" via its `token` property. - - -TODO -==== - - - [x] Document `suggestions` - - [x] TypeScript! - - [ ] make simple `index.html` that demos a dummy model - - [x] Update class definitions in README from TypeScript sources. - - [ ] Do word segmentation - - [ ] Determine the exact arguments given to the model's `predict()` - method. - - [ ] Make an `error` initialization message. - - [ ] Make a `cancel` message. - - [ ] LOGLIKEIHOOD IN THE TRANSFORM! - - [ ] `possibleTransforms` where `transform` is an alias for `possibleTransforms[0]` - - [ ] Use [puppeteer](https://github.com/GoogleChrome/puppeteer)? From fa6c2f8e8f55f1e3afe1ab4c2feb3890e155b54b Mon Sep 17 00:00:00 2001 From: Eddie Antonio Santos Date: Wed, 28 Nov 2018 14:26:04 -0700 Subject: [PATCH 06/10] Move worker communications docs to the docs/ directory. --- .../{README.md => docs/worker-communication-protocol.md} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename common/predictive-text/{README.md => docs/worker-communication-protocol.md} (99%) diff --git a/common/predictive-text/README.md b/common/predictive-text/docs/worker-communication-protocol.md similarity index 99% rename from common/predictive-text/README.md rename to common/predictive-text/docs/worker-communication-protocol.md index 6518c4ed25..dd269e0dd0 100644 --- a/common/predictive-text/README.md +++ b/common/predictive-text/docs/worker-communication-protocol.md @@ -19,7 +19,7 @@ Note: I'm using the term **keyboard** as a synonym for **KeymanWeb**. Communication protocol between keyboard and asynchronous worker --------------------------------------------------------------- -![Sequence diagram of obtaining a prediction](./docs/predictive-text-sequence.png) +![Sequence diagram of obtaining a prediction](./predictive-text-sequence.png) We have decided that everything to the right of the `KeymanWeb` will be in a Web Worker. However, communication can happen only through From 8f28d07c37792e8ec40bf0f64d902de2b748fb8a Mon Sep 17 00:00:00 2001 From: Eddie Antonio Santos Date: Wed, 28 Nov 2018 14:46:04 -0700 Subject: [PATCH 07/10] Add a README describing how to build the code and vaguely what's going on here. --- common/predictive-text/README.md | 61 ++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 common/predictive-text/README.md diff --git a/common/predictive-text/README.md b/common/predictive-text/README.md new file mode 100644 index 0000000000..b46ea6756d --- /dev/null +++ b/common/predictive-text/README.md @@ -0,0 +1,61 @@ +Language Modelling Layer (LMLayer) +================================== + +Provide predictions and corrections while you type! + +See [Worker Communication Protocol](./docs/worker-communication-protocol.md) for a +semi-formal specification on how the Worker and the main thread communicate. + +System dependencies +------------------- + +You will need Bash and Node.js >= 6.0. + +Build +----- + +Run `build.sh`. This will also automatically install dependencies with `npm`. + +```sh +./build.sh +``` + +### Two-stage compilation process + +Since the primary LMLayer code runs within a [Web Worker][], the LMLayer must be +compiled in two stages: + + 1. Compile the inner worker code + a. Compile the TypeScript sources for _only_ the Worker code. + b. Wrap the Worker code as `embedded_worker.js` + + 2. Compile the top-level code + a. Include `embedded_worker.js` verbatim using a TypeScript directive. + b. Compile the top-level TypeScript code. + c. Unwrap the Worker code at runtime. + +[Web Worker]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers + +Test +---- + +This will run both headless unit tests, and in-browser unit tests and integration +tests: + +```sh +./build.sh -test +``` + +### Test-Driven Development + +I like to use [entr]() to automatically build and re-run the unit tests anytime I +change a source code file. Here's the command I run in separate window: + +```sh +git ls-files | entr -c ./build.sh -tdd +``` + +Importantly, this `./build.sh -tdd` skips running the in-browser tests, and skips +downloading/updating `npm` dependencies. + +[entr]: http://eradman.com/entrproject/ From 849089c9c6966ba7a196aaacb64efadc880f7c28 Mon Sep 17 00:00:00 2001 From: Eddie Antonio Santos Date: Wed, 28 Nov 2018 14:54:03 -0700 Subject: [PATCH 08/10] Slightly improve the README text. --- common/predictive-text/README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/common/predictive-text/README.md b/common/predictive-text/README.md index b46ea6756d..4b91416a6c 100644 --- a/common/predictive-text/README.md +++ b/common/predictive-text/README.md @@ -22,17 +22,17 @@ Run `build.sh`. This will also automatically install dependencies with `npm`. ### Two-stage compilation process -Since the primary LMLayer code runs within a [Web Worker][], the LMLayer must be -compiled in two stages: +Since the primary LMLayer code runs within a [Web Worker][], `build.sh` compiles the +LMLayer in two stages: 1. Compile the inner worker code - a. Compile the TypeScript sources for _only_ the Worker code. - b. Wrap the Worker code as `embedded_worker.js` + 1. Compile the TypeScript sources for _only_ the Worker code. + 2. Wrap the Worker code as `embedded_worker.js` 2. Compile the top-level code - a. Include `embedded_worker.js` verbatim using a TypeScript directive. - b. Compile the top-level TypeScript code. - c. Unwrap the Worker code at runtime. + 1. Include `embedded_worker.js` verbatim using a TypeScript directive. + 2. Compile the top-level TypeScript code. + 3. Unwrap the Worker code at runtime. [Web Worker]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers @@ -55,7 +55,7 @@ change a source code file. Here's the command I run in separate window: git ls-files | entr -c ./build.sh -tdd ``` -Importantly, this `./build.sh -tdd` skips running the in-browser tests, and skips +Importantly, `./build.sh -tdd` skips running the in-browser tests, and skips downloading/updating `npm` dependencies. [entr]: http://eradman.com/entrproject/ From a0c07dfcb99042c69242d2ba4014d6610f443366 Mon Sep 17 00:00:00 2001 From: Eddie Antonio Santos Date: Wed, 28 Nov 2018 15:23:20 -0700 Subject: [PATCH 09/10] Do not use dirname to resolve script; assume build.sh is always run in one directory. --- common/predictive-text/build.sh | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/common/predictive-text/build.sh b/common/predictive-text/build.sh index 207c79035a..a3123c5d92 100755 --- a/common/predictive-text/build.sh +++ b/common/predictive-text/build.sh @@ -4,11 +4,10 @@ # Designed for optimal compatibility with the Keyman Suite. # -EMBEDDED_WORKER=embedded_worker.js - # Include some helper functions from resources -# shellcheck source=../../resources/shellHelperFunctions.sh -. "$(dirname "$0")/../../resources/shellHelperFunctions.sh" +. ../../resources/shellHelperFunctions.sh + +EMBEDDED_WORKER=embedded_worker.js # Build the worker and the main script. From 96bc08342cdfff44a3fc35563b65a5b886109b6d Mon Sep 17 00:00:00 2001 From: Eddie Antonio Santos Date: Wed, 28 Nov 2018 15:27:14 -0700 Subject: [PATCH 10/10] Give more consistent, descriptive names to test files. --- .../unit_tests/headless/{lmlayer.js => top-level-lmlayer.js} | 2 +- .../cases/{lmlayer-integration.js => top-level-lmlayer.js} | 0 .../unit_tests/in_browser/cases/{basic.js => worker.js} | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename common/predictive-text/unit_tests/headless/{lmlayer.js => top-level-lmlayer.js} (96%) rename common/predictive-text/unit_tests/in_browser/cases/{lmlayer-integration.js => top-level-lmlayer.js} (100%) rename common/predictive-text/unit_tests/in_browser/cases/{basic.js => worker.js} (100%) diff --git a/common/predictive-text/unit_tests/headless/lmlayer.js b/common/predictive-text/unit_tests/headless/top-level-lmlayer.js similarity index 96% rename from common/predictive-text/unit_tests/headless/lmlayer.js rename to common/predictive-text/unit_tests/headless/top-level-lmlayer.js index f58a471469..90a12f3710 100644 --- a/common/predictive-text/unit_tests/headless/lmlayer.js +++ b/common/predictive-text/unit_tests/headless/top-level-lmlayer.js @@ -1,7 +1,7 @@ var assert = require('chai').assert; var sinon = require('sinon'); -let LMLayer = require('../../'); +let LMLayer = require('../..'); // Test the top-level LMLayer interface. // Note: these tests can only be run after BOTH stages of compilation are completed. diff --git a/common/predictive-text/unit_tests/in_browser/cases/lmlayer-integration.js b/common/predictive-text/unit_tests/in_browser/cases/top-level-lmlayer.js similarity index 100% rename from common/predictive-text/unit_tests/in_browser/cases/lmlayer-integration.js rename to common/predictive-text/unit_tests/in_browser/cases/top-level-lmlayer.js diff --git a/common/predictive-text/unit_tests/in_browser/cases/basic.js b/common/predictive-text/unit_tests/in_browser/cases/worker.js similarity index 100% rename from common/predictive-text/unit_tests/in_browser/cases/basic.js rename to common/predictive-text/unit_tests/in_browser/cases/worker.js