TPS¶
Table of Contents¶
The majority of this document is targeting the Firefox developer that would like to understand TPS and/or write TPS tests. If you’d like to simply run TPS, see the How to run TPS section.
What is TPS¶
TPS is a test automation framework for Firefox Sync. TPS takes in test configuration as JavaScript files defined in a configuration file and runs all tests in sequence.
Each TPS test is made of one or more phases that each have their own assertions and run in sequence. If any phase fails, then the test fails and the remaining phases are not run. However, other tests will still run.
Why TPS exists¶
TPS runs against the real Mozilla Accounts server and Sync servers and thus is a good way to test Firefox Sync end-to-end without mocking the servers.
Architecture¶
High level Diagram¶
The following diagram describes the flow when running TPS.
flowchart TD TPS[TPS Runner]-->T[Run Test Suite] T-->T1[Run Next Single Test] T1-->S[Setup] S-->P[Run Next Phase] P-->LP[Launch Test Profile] LP-->Q{Phase Success?} Q-->|No| CL[Clean up] Q-->|Yes| Q2{Any more phases?} Q2-->|Yes|P Q2-->|No| CL CL-->CR[Collect Results] CR-->Q3{Any more tests?} Q3-->|Yes|T1 Q3-->|No| D[Done]
Single Test Sequence Diagram¶
The following sequence diagram describes the involved entities executing a single TPS test
sequenceDiagram actor U as User participant TPS as TPS Runner participant F as Firefox Profile participant TE as TPS Extension U->>TPS: Start test TPS->>TPS: Parse Test file loop Every Phase TPS->>F: Launch Phase w/prefs to install TPS extension F->>TE: Install Extension TE->>F: Read prefs to get test file F->>TE: Test file and TPS Runner configuration loop Every instruction TE->>F: Execute test instructions and assertions F->>TE: Result end TE->>F: Phase Done F->>TPS: Done TPS->>F: Read logs to get test results end TPS->>U: Print test results
Phases¶
Each Phase is mapped to a Firefox profile, phases may re-use profiles if they are mapped to them, see the phases object for details on the mapping. All phases are signed in to the same Mozilla Account.
Example¶
For example, a simple test that defines two phases, one that uploads bookmarks and another that downloads them can be described as follows, see the section on Test Format for details on the format of the tests.
EnableEngines(["bookmarks"]); // Enables the bookmark engine across all profiles(phases) that this test runs
/*
* The list of phases mapped to their corresponding profiles. The object
* here must be in JSON format as it will get parsed by the Python
* testrunner. It is parsed by the YAML package, so it relatively flexible.
*/
var phases = {
phase1: "profile1",
phase2: "profile2",
};
// the initial list of bookmarks to add to the browser
var bookmarksInitial = {
menu: [
{ folder: "foldera" },
{ folder: "folderb" },
],
"menu/foldera": [{ uri: "http://www.cnn.com", title: "CNN" }], // One bookmark in menu/foldera pointing to CNN
"menu/folderb": [{ uri: "http://www.apple.com", title: "Apple"}], // One bookmark in menu/folderb pointing to apple
};
// Add bookmarks to profile1 and sync.
Phase("phase1", [
[Bookmarks.add, bookmarksInitial],
[Bookmarks.verify, bookmarksInitial],
[Sync],
[Bookmarks.verify, bookmarksInitial],
]);
// Sync, then verify that bookmarks added by phase 1 are present.
Phase("phase2", [
[Sync],
[Bookmarks.verify, bookmarksInitial],
]);
The TPS Extension¶
When the Firefox profile representing the phase loads, it first installs an extension. The extension is what executes the tests by instructing Firefox and reading from Firefox to assert that Sync is working properly.
The test files execute in the extension, and the extension defines all the functions that the test files may use. For instance, in the above example the Phase
function is defined here
Test Format¶
Test group configuration¶
The tests are referenced by a json
file that references all the tests TPS will run. By default TPS will run the configuration in services/sync/tests/tps/all_tests.json
.
The test group configuration is a json
object with one key named tests
that is itself a json
object. The tests
object has a key for every test to run, and the key should be the name of the test file. The value for each test file is the empty object {}
if the test should run, or
{"disabled": "Bug <BUG NUMBER>"}
where <BUG NUMBER>
is a Bugzilla bug number referencing why the test was disabled.
Test Files¶
The phases object¶
Test Files are JavaScript files that will be run by the TPS extension once the Firefox profile is loaded. However, before that is done the TPS framework will load the first object defined in test file as yaml
. In other words, for the following example:
var phases = {
phase1: "profile1",
phase2: "profile2",
phase3: "profile1", // phase 1 and 3 reuse the same profile, "profile1"
}
// ... rest of the test file
The inside of the curly brackets will be parsed as yaml to identify the profiles that TPS will run and map each phase to a profile. In the example above, phase1
and phase3
reuse the same profile, but phase2
uses it’s own profile. The rest of the file is regular JavaScript that will be loaded in the TPS Extension.
Test File Capabilities¶
The TPS Extension exports a set of functions and objects that test files may use. See the tps.sys.mjs
for up-to-date details, the following is the list of export capabilities as of April 10th, 2024:
Enabling Sync Engines¶
To enable sync engines, which you should do before executing a test that depends on an engine, use the EnableEngines
function. The function takes an array of strings representing the engine names. Eg:
EnableEngines(["bookmarks", "history"]);
Start a phase¶
Phases are run on their assigned profiles profiles in the order they are declared in the phases object. To define what a phase does use the Phase
function that takes in a phase name as the first argument - this should be the same name defined in the phases object - and a 2D array of actions as a second argument. Each action array in the 2D is run in sequence. The inner array defines what the action is, and any arguments to that action. For example:
Phase("phase1", [[Sync], [Bookmarks.add, bookmarkInitial]]);
Note that the example assumes that the phases object and bookmarkInitial
are defined in test file.
In the above example:
"phase1"
is the name of the phase, which should match exactly the phase name as defined in the phases object.The 2D array passed as the second argument has:
[Sync]
as its first array which means that the phase will first Sync[Bookmarks.add, bookmarkInitial]
as its second member which means that after the Sync, a Bookmarks.add operation will run, and it will add thebookmarkInitial
.
Actions¶
Actions are run inside phases as arguments to the Phase
function.
General Actions¶
There are few general actions that aren’t tied to a data type:
Sync
: Will trigger a syncLogin
: Logs in to Mozilla Account. You shouldn’t need to do this in most cases as it is done automaticallyWipeServer
: Will wipe all data from the serverEnsureTracking
: Will wait until sync tracking has started. You shouldn’t need to do this in most cases
Data Type specific Actions¶
Some actions are supported for individual data types. Those actions are triggered by using <DataType>.<Action>
. The following is a list of all possible <DataType>
values:
For more details on which actions are supported for which data type, see the following sections.
Bookmarks
Tabs
Formdata
History
Passwords
Addons
Addresses
CreditCards
ExtStorage
Prefs
Windows
Bookmarks Actions
Example in test_existing_bookmarks.js
add
: To add a bookmark treemodify
: To modify the existing bookmark treedelete
: To remove a bookmark node from the treeverify
: To verify the bookmark tree matches exactly the tree given, otherwise fails the phaseverifyNot
: The inverse of verify, fails the phase if the bookmark tree matches the given treeskipValidation
: To tell Firefox to stop validating bookmark trees.
Addresses Actions
Example in test_addresses.js
add
: Adds an array of addressesmodify
: Modifies the list of addresses to what was givendelete
: Deletes the given addressesverify
: Verifies that the addresses match exactly the given list, otherwise fails the phaseverifyNot
: The inverse of verify, fails the phase if the list of addresses matches the given list.
CreditCard Actions
Example in test_creditcards.js
add
: Adds an array of credit cardsmodify
: Modifies the list of credit cards to what was givendelete
: Deletes the given credit cardsverify
: Verifies that the credit cards match exactly the given list, otherwise fails the phaseverifyNot
: The inverse of verify, fails the phase if the list of credit cards matches the given list.
Addons Actions
Example in test_addon_reconciling.js
installs
: installs a list of addonssetEnabled
: Enables or disables a list of addonsuninstall
: Uninstalls a list of addonsverify
: Verifies that the addons match exactly the given list, otherwise fails the phaseverifyNot
: The inverse of verify, fails the phase if the list of addons matches the given list.skipValidation
: Tells Firefox to stop validating Addons.
Formdata Actions
Example in test_formdata.js
add
: Adds an array of form datadelete
: Deletes the given form dataverify
: Verifies that form data match exactly the given list, otherwise fails the phaseverifyNot
: The inverse of verify, fails the phase if the form data matches the given list.
History Actions
Example in test_history.js
add
: Adds an array of history itemsdelete
: Deletes the given history itemsverify
: Verifies that form data match exactly the given list, otherwise fails the phaseverifyNot
: The inverse of verify, fails the phase if the form data matches the given list.
Passwords Actions
Example in test_passwords.js
add
: To add a list of loginsmodify
: To modify the existing list of logins based on changes in the given listdelete
: To remove a list of loginsverify
: To verify the list of logins matches exactly the list given, otherwise fails the phaseverifyNot
: The inverse of verify, fails the phase if the logins matches the given listskipValidation
: To tell Firefox to stop validating logins
Prefs Actions
Example in test_prefs.js
modify
: To modify the existing prefs based on changes in the given listverify
: To verify the values of the given prefs match the values given, otherwise fails the phase
Tabs Actions
Example in test_tabs.js
add
: To add a new list of remote tabs, each annotated with which profile it belongs toverify
: To verify the values of remote tabs matches the given list, otherwise fails the phase
Windows Actions
Example in test_privbrw_tabs.js
add
: Adds a new window with the given configuration
ExtStorage Actions
Example in test_extstorage.js
set
: Sets the value of given key to the given valueverify
: Verifies that the value of the given key is the given value, fails the phase otherwise.
Writing new TPS tests¶
To write new TPS tests follow the following instructions:
Create a new
.js
file inservices/sync/tests/tps
namedtest_<my_test_name>.js
Link to the file in the
services/sync/tests/tps/all_tests.json
Follow the format described in Test Files
Make sure to test the file by running TPS
How to run TPS¶
Installation¶
TPS requires several packages to operate properly. To install TPS and
required packages, use the create_venv.py script provided in the testing/tps
directory:
python3 create_venv.py /path/to/create/virtualenv
This script will create a virtalenv and install TPS into it.
You must then activate the virtualenv by executing:
(mac/linux):
source /path/to/virtualenv/Scripts/activate
(win):
/path/to/virtualenv/Scripts/activate.bat
TPS can then be run by executing:
runtps --binary=/path/to/firefox
Note: You can run the tps tests in headless mode by using
MOZ_HEADLESS=1
. This will make your computer somewhat useable while the tests are running.
When you are done with TPS, you can deactivate the virtualenv by executing
deactivate
Configuration¶
To edit the TPS configuration, do not edit config/config.json.in in the tree.
Instead, edit config.json inside your virtualenv; it will be located at the
top level of where you specified the virtualenv be created - eg, for the
example above, it will be /path/to/create/virtualenv/config.json
Setting Up Test Accounts¶
Mozilla Accounts¶
To create a test account for using the Mozilla Account authentication perform the following steps:
Note: Currently, the TPS tests rely on how restmail returns the verification code You should use restmail or something very similar. Gmail and other providers might give a
The request was blocked for security reasons
Go to the URL: http://restmail.net/mail/%account_prefix%@restmail.net
Replace
%account_prefix%
with your own test name
Go to https://accounts.firefox.com/signup?service=sync&context=fx_desktop_v1
Sign in with the previous chosen email address and a password
Go back to the Restmail URL, reload the page
Search for the verification link and open that page
Now you will be able to use this account for TPS. Note that these steps can be done in either a test profile or in a private browsing window - you might want to avoid doing that in a “real” profile that’s already connected to Sync.