Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 50 additions & 1 deletion task-launcher/cypress.config.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,47 @@
import { defineConfig } from 'cypress';

const LANGUAGE_OPTIONS_URL =
'https://storage.googleapis.com/levante-assets-dev/translations/dashboard-consolidated-flat/languageoptions.json';

async function buildLanguageLocaleTaskMatrix() {
const res = await fetch(LANGUAGE_OPTIONS_URL);
if (!res.ok) {
throw new Error(`Failed to fetch language options: ${res.status} ${res.statusText}`);
}
const languageOptions = await res.json();
const seen = new Set();

const matrix = Object.entries(languageOptions).flatMap(([locale, cfg]) => {
if (locale === 'en-US') {
return [];
}
if (!cfg || !Array.isArray(cfg.taskOptions)) {
return [];
}
return cfg.taskOptions
.filter((task) => {
const key = `${locale}\0${task}`;
if (seen.has(key)) {
return false;
}
seen.add(key);
return true;
})
.map((task) => ({ locale, task }));
});

if (matrix.length === 0) {
throw new Error(
'languageoptions.json produced an empty test matrix (no locales with taskOptions).',
);
}

return matrix;
}

export default defineConfig({
e2e: {
setupNodeEvents(on, config) {
async setupNodeEvents(on, config) {
// implement node event listeners here
on('task', {
progress(message) {
Expand All @@ -12,6 +51,16 @@ export default defineConfig({
return null;
},
});

const matrix = await buildLanguageLocaleTaskMatrix();

return {
...config,
env: {
...config.env,
languageLocaleTaskMatrix: matrix,
},
};
},
// Video recording settings
video: true,
Expand Down
46 changes: 27 additions & 19 deletions task-launcher/cypress/e2e/task_locales.cy.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,37 @@
const LOCALES = ['de-DE', 'es-CO', 'es-AR'];

const TASKS = [
'intro',
'egma-math',
'matrix-reasoning',
'mental-rotation',
'hearts-and-flowers',
'memory-game',
'same-different-selection',
'trog',
'vocab',
'theory-of-mind',
'hostile-attribution',
'child-survey',
];
/* global cy, describe, expect, it, Cypress */

function visitTaskWithLocaleAndEnterFullscreen(task, lng) {
cy.visit(`http://localhost:8080/?task=${task}&lng=${lng}`);
cy.get('button.primary').should('be.visible').first().realClick();
}

describe('tasks load in non-English locales (fullscreen only)', () => {
TASKS.forEach((task) => {
function groupLocalesByTask(matrix) {
const byTask = {};
matrix.forEach(({ locale, task }) => {
if (!byTask[task]) {
byTask[task] = [];
}
byTask[task].push(locale);
});
return byTask;
}

describe('tasks load per languageoptions.json (fullscreen only)', () => {
const matrix = Cypress.env('languageLocaleTaskMatrix');

if (!Array.isArray(matrix) || matrix.length === 0) {
it('fails when languageLocaleTaskMatrix is not preloaded (see cypress.config.js)', () => {
expect(matrix).to.be.an('array');
expect(matrix).to.have.length.greaterThan(0);
});
return;
}

const byTask = groupLocalesByTask(matrix);

Object.entries(byTask).forEach(([task, locales]) => {
describe(task, () => {
LOCALES.forEach((lng) => {
locales.forEach((lng) => {
it(`lng=${lng}`, () => {
visitTaskWithLocaleAndEnterFullscreen(task, lng);
});
Expand Down
18 changes: 0 additions & 18 deletions task-launcher/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 0 additions & 22 deletions task-launcher/serve/firebaseConfig.js

This file was deleted.

90 changes: 32 additions & 58 deletions task-launcher/serve/serve.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import { RoarAppkit, initializeFirebaseProject } from '@levante-framework/firekit';
import { onAuthStateChanged, signInAnonymously } from 'firebase/auth';
import * as Sentry from '@sentry/browser';
import i18next from 'i18next';
import { TaskLauncher } from '../src';
import { firebaseConfig } from './firebaseConfig';
import { stringToBoolean } from '../src/tasks/shared/helpers/stringToBoolean';
import firebaseJSON from '../firebase.json';

Expand Down Expand Up @@ -68,63 +65,40 @@ const emulatorConfig = EMULATORS ? firebaseJSON.emulators : undefined;
const demoMode = DEMO;

async function startWebApp() {
const appKit = await initializeFirebaseProject(firebaseConfig, 'admin', emulatorConfig, 'none');

onAuthStateChanged(appKit.auth, (user) => {
if (user) {
const userInfo = {
assessmentUid: user.uid,
userMetadata: {},
};
const firekit = null;
const gameParams = {
taskName,
skipInstructions,
sequentialPractice,
sequentialStimulus,
corpus,
buttonLayout,
numOfPracticeTrials,
numberOfTrials,
maxIncorrect,
stimulusBlocks,
keyHelpers,
language: language ?? i18next.language,
age,
maxTime,
storeItemId,
cat,
inferenceNumStories,
numberOfStories,
semThreshold,
startingTheta,
heavyInstructions,
demoMode,
version,
debug,
};
const userParams = {
pid,
};
const task = new TaskLauncher(firekit, gameParams, userParams);
task.run();

const userParams = {
pid,
};

const gameParams = {
taskName,
skipInstructions,
sequentialPractice,
sequentialStimulus,
corpus,
buttonLayout,
numOfPracticeTrials,
numberOfTrials,
maxIncorrect,
stimulusBlocks,
keyHelpers,
language: language ?? i18next.language,
age,
maxTime,
storeItemId,
cat,
inferenceNumStories,
numberOfStories,
semThreshold,
startingTheta,
heavyInstructions,
demoMode,
version,
debug,
};

const taskInfo = {
taskId: taskName,
variantParams: gameParams,
};

const firekit = new RoarAppkit({
firebaseProject: appKit,
taskInfo,
userInfo,
});

const task = new TaskLauncher(firekit, gameParams, userParams);
task.run();
}
});

await signInAnonymously(appKit.auth);
}

await startWebApp();
10 changes: 5 additions & 5 deletions task-launcher/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,17 @@ let sharedVisualAssets: MediaAssetsType;
export class TaskLauncher {
gameParams: GameParamsType;
userParams: UserParamsType;
firekit: RoarAppkit;
firekit: RoarAppkit | null;
logger?: LevanteLogger;
constructor(firekit: RoarAppkit, gameParams: GameParamsType, userParams: UserParamsType, logger?: LevanteLogger) {
constructor(firekit: RoarAppkit | null, gameParams: GameParamsType, userParams: UserParamsType, logger?: LevanteLogger) {
this.gameParams = gameParams;
this.userParams = userParams;
this.firekit = firekit;
Logger.setInstance(logger, gameParams, userParams);
}

async init() {
if (!this.gameParams.demoMode) {
if (!this.gameParams.demoMode && this.firekit) {
await this.firekit.startRun();
}

Expand All @@ -55,7 +55,7 @@ export class TaskLauncher {
const { setConfig, getCorpus, buildTaskTimeline, getTranslations } =
taskConfig[dashToCamelCase(taskName) as keyof typeof taskConfig];

const isDev = this.firekit.firebaseProject?.firebaseApp?.options?.projectId === 'hs-levante-admin-dev';
const isDev = this.firekit ? this.firekit.firebaseProject?.firebaseApp?.options?.projectId === 'hs-levante-admin-dev' : !!this.gameParams.demoMode;
const taskVisualBucket = getBucketName(taskName, isDev, 'visual', language);
const sharedVisualBucket = getBucketName('shared', isDev, 'visual', language);
const languageAudioBucket = getBucketName('shared', isDev, 'audio', language);
Expand Down Expand Up @@ -117,7 +117,7 @@ export class TaskLauncher {
const translations = taskStore().translations;
const pageSetup = new InitPageSetup(4000, translations);
pageSetup.init();
const checkTaskFinished = this.gameParams.demoMode
const checkTaskFinished = (this.gameParams.demoMode || this.firekit === null)
? () => taskStore().taskComplete
: () => this.firekit?.run?.completed === true && taskStore().taskComplete;

Expand Down
10 changes: 6 additions & 4 deletions task-launcher/src/tasks/shared/helpers/baseTimeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ export const initTimeline = (
const beginningTimeline = {
timeline: initialTimeline,
on_timeline_finish: async () => {
await config.firekit.updateUser({
assessmentPid: config.pid || makePid(),
...config.userMetadata,
});
if (config.firekit) {
await config.firekit.updateUser({
assessmentPid: config.pid || makePid(),
...config.userMetadata,
});
}

startAppTimer(config.maxTime, finishExperiment);
},
Expand Down
6 changes: 1 addition & 5 deletions task-launcher/src/tasks/shared/helpers/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ const defaultCorpus: Record<string, string> = {
};

export const setSharedConfig = async (
firekit: RoarAppkit,
firekit: RoarAppkit | null,
gameParams: GameParamsType,
userParams: UserParamsType,
): Promise<TaskStoreDataType> => {
Expand Down Expand Up @@ -138,9 +138,5 @@ export const setSharedConfig = async (
config.corpus = defaultCorpus[camelize(taskName)];
}

const updatedGameParams = Object.fromEntries(
Object.entries(gameParams).map(([key, value]) => [key, config[key as keyof typeof config] ?? value]),
);

return config;
};
5 changes: 4 additions & 1 deletion task-launcher/src/tasks/shared/helpers/isRoarApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ const roarFirebaseProjects = [
'gse-roar-admin-dev',
];

export function isRoarApp(_firekit: RoarAppkit) {
export function isRoarApp(_firekit: RoarAppkit | null) {
if (!_firekit) {
return false;
}
const projectId = _firekit?.firebaseProject?.firebaseApp?.options?.projectId ?? '';
return roarFirebaseProjects.includes(projectId);
}
2 changes: 1 addition & 1 deletion task-launcher/src/tasks/shared/helpers/recordCompletion.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { taskStore } from '../../../taskStore';

export function recordCompletion(config: Record<string, any>) {
if (!config?.firekit?.run?.completed && !taskStore().demoMode) {
if (!taskStore().demoMode && config.firekit && !config.firekit?.run?.completed) {
config.firekit.finishRun();
}
}
Loading
Loading