Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Resolves #207 - Adding automated tests to CI #213

Merged
merged 13 commits into from
May 29, 2024
52 changes: 52 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
on:
push:
branches:
- master
- main
pull_request:

name: CI
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version-file: 'package.json'
cache: 'npm'

- name: Print environment
run: |
node --version
npm --version

- name: Install Node dependencies
run: npm ci

- name: Code Style
run: npm run cs-check

- name: Lint
run: npm run lint


test:
runs-on: ubuntu-latest
env:
TEST_TAG: user/app:test
steps:
- uses: actions/checkout@v4

- name: Build container
uses: docker/build-push-action@v5
with:
tags: ${{ env.TEST_TAG }}
file: Dockerfile
load: true
context: .

- name: Run container
run: |
docker run --rm ${{ env.TEST_TAG }} && sleep 5
21 changes: 21 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Only used for testing
FROM node:18-bookworm

WORKDIR /opt

# pull latest nightly
RUN wget -O nightly.tar.bz2 "https://download.mozilla.org/?product=firefox-nightly-latest-ssl&os=linux64&lang=en-US"
RUN tar -xf nightly.tar.bz2

# install firefox dependencies
RUN apt update && apt install -y libasound2 libgtk-3-0 libx11-xcb1

# copy files over
COPY . ./

# install node dependencies
RUN npm ci

ENV NIGHTLY_PATH="/opt/firefox/firefox"

CMD npm run tcs:test
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@ npx run start:linux -- --firefox-profile rs-devtools --profile-create-if-missing

More information about the temporary loaded addon can be found in `about:debugging#/runtime/this-firefox`

## Running tests locally
alexcottner marked this conversation as resolved.
Show resolved Hide resolved

Automated tests can be run locally or in docker to verify changes.
- `npm run tcs:docker` - will build and run the automated tests within a container using the latest firefox nightly package.
- `npm run tcs:test` to run automated tests locally using your firefox nightly package.
- Setting the `NIGHTLY_PATH` environment parameter will allow you to run tests against an arbitrary firefox nightly binary. Ex: `NIGHTLY_PATH=/opt/custom/nightly npm run tcs:test`

# Release

### Prerequisites (get access to Ship-It)
Expand Down
54 changes: 24 additions & 30 deletions extension/content/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ function showLoading(state) {
} else {
document
.querySelectorAll(".loading")
.forEach(el => (el.className = el.className.replace(" loading", "")));
.forEach((el) => (el.className = el.className.replace(" loading", "")));
}
}

Expand Down Expand Up @@ -73,25 +73,26 @@ async function refreshUI(state) {

showLoading(false);

const environmentElt = document.getElementById("environment")
const environmentElt = document.getElementById("environment");
environmentElt.value = environment;
document.getElementById("environment-error").style.display = serverSettingIgnored ? "block" : "none";
document.getElementById("environment-error").style.display =
serverSettingIgnored ? "block" : "none";
if (serverSettingIgnored) {
environmentElt.setAttribute("disabled", "disabled");
}

document.getElementById("polling-url").textContent = new URL(pollingEndpoint).origin;
document.getElementById("polling-url").textContent = new URL(
pollingEndpoint,
).origin;
document.getElementById("polling-url").setAttribute("href", pollingEndpoint);
document.getElementById("local-timestamp").textContent = localTimestamp;
document.getElementById("server-timestamp").textContent = serverTimestamp;
document.getElementById("human-local-timestamp").className =
localTimestamp == serverTimestamp ? " up-to-date" : " unsync";
document.getElementById("human-local-timestamp").textContent = humanDate(
localTimestamp,
);
document.getElementById("human-server-timestamp").textContent = humanDate(
serverTimestamp,
);
document.getElementById("human-local-timestamp").textContent =
humanDate(localTimestamp);
document.getElementById("human-server-timestamp").textContent =
humanDate(serverTimestamp);
document.getElementById("last-check").textContent = humanDate(
lastCheck * 1000,
);
Expand All @@ -100,7 +101,7 @@ async function refreshUI(state) {
const historyTpl = document.getElementById("sync-history-entry-tpl");
const historyList = document.querySelector("#sync-history > ul");
historyList.innerHTML = "";
history["settings-sync"].forEach(entry => {
history["settings-sync"].forEach((entry) => {
const entryRow = historyTpl.content.cloneNode(true);
entryRow.querySelector(".datetime").textContent = humanDate(
entry.timestamp,
Expand All @@ -115,14 +116,9 @@ async function refreshUI(state) {
const statusTable = document.querySelector("#status table tbody");

statusTable.innerHTML = "";
collections.forEach(status => {
const {
bucket,
collection,
lastCheck,
localTimestamp,
serverTimestamp,
} = status;
collections.forEach((status) => {
const { bucket, collection, lastCheck, localTimestamp, serverTimestamp } =
status;
const url = `${serverURL}/buckets/${bucket}/collections/${collection}/changeset?_expected=${serverTimestamp}`;
const identifier = `${bucket}/${collection}`;

Expand All @@ -131,15 +127,13 @@ async function refreshUI(state) {
tableRow.querySelector("tr").setAttribute("id", tableRowId);
tableRow.querySelector(".url").textContent = identifier;
tableRow.querySelector(".url").setAttribute("href", url);
tableRow.querySelector(".human-server-timestamp").textContent = humanDate(
serverTimestamp,
);
tableRow.querySelector(".human-server-timestamp").textContent =
humanDate(serverTimestamp);
tableRow.querySelector(".server-timestamp").textContent = serverTimestamp;
tableRow.querySelector(".human-local-timestamp").className +=
localTimestamp == serverTimestamp ? " up-to-date" : " unsync";
tableRow.querySelector(".human-local-timestamp").textContent = humanDate(
localTimestamp,
);
tableRow.querySelector(".human-local-timestamp").textContent =
humanDate(localTimestamp);
tableRow.querySelector(".local-timestamp").textContent = localTimestamp;
tableRow.querySelector(".last-check").textContent = humanDate(
lastCheck * 1000,
Expand All @@ -161,27 +155,27 @@ async function main() {
// Load the UI in the background.
remotesettings
.getState()
.then(data => {
.then((data) => {
showLoading(false);
refreshUI(data);
})
.catch(showGlobalError);

remotesettings.onStateChanged.addListener(data => {
remotesettings.onStateChanged.addListener((data) => {
showLoading(false);
try {
refreshUI(JSON.parse(data));
} catch (e) {
showGlobalError(e);
}
});
remotesettings.onGlobalError.addListener(error => showGlobalError(error));
remotesettings.onSyncError.addListener(data => {
remotesettings.onGlobalError.addListener((error) => showGlobalError(error));
remotesettings.onSyncError.addListener((data) => {
const { bucket, collection, error } = JSON.parse(data);
showSyncError(bucket, collection, error);
});

document.getElementById("environment").onchange = async event => {
document.getElementById("environment").onchange = async (event) => {
showGlobalError(null);
showLoading(true);
await remotesettings.switchEnvironment(event.target.value);
Expand Down
19 changes: 13 additions & 6 deletions extension/experiments/remotesettings/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,18 @@ async function getState() {
// Detect whether user tried to switch server, and whether it had effect or not.
let serverSettingIgnored = false;
if (Services.prefs.prefHasUserValue("services.settings.server")) {
const manuallySet = Services.prefs.getStringPref("services.settings.server");
const manuallySet = Services.prefs.getStringPref(
"services.settings.server",
);
if (manuallySet != serverURL) {
serverSettingIgnored = true;
}
}
// Same for preview mode.
if (Services.prefs.prefHasUserValue("services.settings.preview_enabled")) {
const manuallyEnabled = Services.prefs.getBoolPref("services.settings.preview_enabled");
const manuallyEnabled = Services.prefs.getBoolPref(
"services.settings.preview_enabled",
);
if (manuallyEnabled && !previewMode) {
serverSettingIgnored = true;
}
Expand Down Expand Up @@ -142,7 +146,10 @@ var remotesettings = class extends ExtensionAPI {
const previewMode = env.includes("-preview");
RemoteSettings.enablePreviewMode(previewMode);
// Set pref to persist change across restarts.
Services.prefs.setBoolPref("services.settings.preview_enabled", previewMode);
Services.prefs.setBoolPref(
"services.settings.preview_enabled",
previewMode,
);

refreshUI();
},
Expand Down Expand Up @@ -200,7 +207,7 @@ var remotesettings = class extends ExtensionAPI {
onStateChanged: new EventManager({
context,
name: "remotesettings.onStateChanged",
register: fire => {
register: (fire) => {
const observer = async () => {
const state = await getState();
fire.async(JSON.stringify(state));
Expand Down Expand Up @@ -229,7 +236,7 @@ var remotesettings = class extends ExtensionAPI {
onGlobalError: new EventManager({
context,
name: "remotesettings.onGlobalError",
register: fire => {
register: (fire) => {
const observer = (subject, topic, data) => {
fire.async(data);
};
Expand All @@ -245,7 +252,7 @@ var remotesettings = class extends ExtensionAPI {
onSyncError: new EventManager({
context,
name: "remotesettings.onSyncError",
register: fire => {
register: (fire) => {
const observer = (subject, topic, data) => {
fire.async(data);
};
Expand Down
Loading
Loading