Add Connectivity Issue Page and Improve Table Semantics
- Introduced `ConnectivityIssue.vue` for handling and displaying connectivity problems. - Added new route for the connectivity issue error page. - Enhanced accessibility by wrapping table headers in `OrderBookingsTable.vue` with `<thead>` and `<tbody>` tags. - Included connectivity-related internationalization keys across various locale files. - Updated components to integrate `ConnectivityIssue` as a fallback display for connection errors.
This commit is contained in:
@@ -8386,6 +8386,18 @@ components:
|
||||
For backward-compatibility the API also accepts camelCase `progressAlertTimeOfDay` on input.
|
||||
pattern: '^([01]\d|2[0-3]):[0-5]\d(?:Z|[+-](?:[01]\d|2[0-3]):?[0-5]\d)$'
|
||||
example: "14:30+02:00"
|
||||
department_daily_targets:
|
||||
type: object
|
||||
description: |
|
||||
Optional per-department custom daily targets. Keys are department IDs and values are non-negative integers representing the target per operating day for that department.
|
||||
If omitted, the daily target is split evenly across selected departments. The canonical field name is snake_case `department_daily_targets`.
|
||||
For backward-compatibility the API also accepts camelCase `departmentDailyTargets` on input.
|
||||
additionalProperties:
|
||||
type: integer
|
||||
minimum: 0
|
||||
example:
|
||||
"12": 3
|
||||
"15": 5
|
||||
|
||||
DepartmentGoal:
|
||||
type: object
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import {defineProps, defineSlots, onMounted, ref, watch, defineEmits} from 'vue';
|
||||
import { onMounted, ref, watch } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
/**
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import logo from "@/assets/img/truckwash.png";
|
||||
import LanguageSwitcher from "@/components/LanguageSwitcher.vue";
|
||||
import {onBeforeUnmount, onMounted} from "vue";
|
||||
import ConnectivityIssue from "@/views/errors/ConnectivityIssue.vue";
|
||||
onMounted(() => {
|
||||
const headerWrapper = document.querySelector('.header-wrapper');
|
||||
const toggle = document.querySelector('.menu-toggle__icon');
|
||||
|
||||
@@ -10,6 +10,7 @@ import ViewportFooter from "@/components/viewport/page/footers/ViewportFooter.vu
|
||||
import {useRouter} from "vue-router";
|
||||
import { showFooterInContent } from "@/components/viewport/conditions/ViewPortFooterOptions.vue";
|
||||
import { isHidden } from "@/components/viewport/page/headers/ViewportHeaderSettings.vue";
|
||||
import ConnectivityIssue from "@/views/errors/ConnectivityIssue.vue";
|
||||
const router = useRouter();
|
||||
|
||||
console.log("Viewport component loaded");
|
||||
@@ -77,7 +78,9 @@ const isFooterInContent = computed(() => {
|
||||
}">
|
||||
<div class="independent-scroll slick-scroll-layout" :style="{ maxHeight: independentScrollMaxHeight }">
|
||||
<ViewportContent>
|
||||
<slot />
|
||||
<ConnectivityIssue>
|
||||
<slot />
|
||||
</ConnectivityIssue>
|
||||
<div class="has-text-centered" v-show="isFooterInContent">
|
||||
<a class="is-align-content-center" href="/">
|
||||
<!-- All rights reserved footer logo -->
|
||||
|
||||
+26
-18
@@ -843,22 +843,22 @@
|
||||
"updated": "Opdateret",
|
||||
"updated_at": "Opdateret",
|
||||
"upload": "Upload",
|
||||
"user": "Bruger",
|
||||
"users": "Brugere",
|
||||
"vehicle": "Køretøj",
|
||||
"vehicles": "Køretøjer",
|
||||
"via": "Via",
|
||||
"view": "Vis",
|
||||
"warning": "Advarsel",
|
||||
"was": "var",
|
||||
"week": "Uge",
|
||||
"were": "var",
|
||||
"will": "vil",
|
||||
"would": "ville",
|
||||
"year": "År",
|
||||
"yes": "Ja",
|
||||
"yesterday": "I går",
|
||||
"zip": "Postnummer"
|
||||
"user": "Bruger",
|
||||
"users": "Brugere",
|
||||
"vehicle": "Køretøj",
|
||||
"vehicles": "Køretøjer",
|
||||
"via": "Via",
|
||||
"view": "Vis",
|
||||
"warning": "Advarsel",
|
||||
"was": "var",
|
||||
"week": "Uge",
|
||||
"were": "var",
|
||||
"will": "vil",
|
||||
"would": "ville",
|
||||
"year": "År",
|
||||
"yes": "Ja",
|
||||
"yesterday": "I går",
|
||||
"zip": "Postnummer"
|
||||
},
|
||||
"configuration": {
|
||||
"backups": {
|
||||
@@ -3881,7 +3881,15 @@
|
||||
"enter_code": "Indtast din bekræftelseskode",
|
||||
"back_to_login": "Tilbage til login"
|
||||
},
|
||||
"common": {
|
||||
"cancel": "Annuller"
|
||||
"connectivity": {
|
||||
"title": "Forbindelsesproblem",
|
||||
"subtitle": "Det ser ud til, at der er et problem med din internetforbindelse eller vores server.",
|
||||
"no_internet": "Ingen internetforbindelse.",
|
||||
"server_down": "Ingen forbindelse til serveren.",
|
||||
"description": "Dette løser sig som regel selv. Vent venligst, mens vi tjekker serveren.",
|
||||
"retry_in": "Prøver igen om {seconds} sekunder...",
|
||||
"retry_now": "Prøver at genoprette forbindelsen...",
|
||||
"retry": "Prøv igen",
|
||||
"footer": "Hvis problemet fortsætter, kontakt venligst support."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3807,5 +3807,16 @@
|
||||
"subtitle": "Her kan du håndtere XLVask integrationen",
|
||||
"title": "XLVask administration"
|
||||
}
|
||||
},
|
||||
"connectivity": {
|
||||
"title": "Verbindungsproblem",
|
||||
"subtitle": "Wird alle paar Sekunden automatisch versucht, die Verbindung wiederherzustellen.",
|
||||
"no_internet": "Keine Internetverbindung.",
|
||||
"server_down": "Keine Verbindung zum Server.",
|
||||
"description": "Dies klärt sich in der Regel von selbst. Bitte warten Sie, während wir den Server überprüfen.",
|
||||
"retry_in": "Wird erneut versucht in {seconds} Sekunden...",
|
||||
"retry_now": "Versuche Verbindung wiederherzustellen...",
|
||||
"retry": "Wiederholen",
|
||||
"footer": "Wenn das Problem weiterhin besteht, kontaktieren Sie bitte den Support."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3876,7 +3876,15 @@
|
||||
"enter_code": "Enter your verification code",
|
||||
"back_to_login": "Back to Login"
|
||||
},
|
||||
"common": {
|
||||
"cancel": "Cancel"
|
||||
"connectivity": {
|
||||
"title": "Connectivity Issue",
|
||||
"subtitle": "Retrying every 5 seconds...",
|
||||
"no_internet": "No internet connection.",
|
||||
"server_down": "No connection to the server.",
|
||||
"description": "This usually resolves itself. Please wait while we check the server.",
|
||||
"retry_in": "Retrying in {seconds} seconds...",
|
||||
"retry_now": "Trying to reconnect...",
|
||||
"retry": "Retry",
|
||||
"footer": "If the issue persists, please contact support."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3837,5 +3837,16 @@
|
||||
"not_supported_browser": "Nettleseren din støtter ikke passnøkler. Vennligst prøv en moderne nettleser som Chrome, Safari, Firefox eller Edge.",
|
||||
"login_with_passkey": "Logg inn med passnøkkel",
|
||||
"login_failed": "Kunne ikke logge inn med passnøkkel. Vennligst prøv igjen."
|
||||
},
|
||||
"connectivity": {
|
||||
"title": "Forbindelsesproblem",
|
||||
"subtitle": "Det ser ut til at det er et problem med din internettforbindelse eller vår server.",
|
||||
"no_internet": "Ingen internettforbindelse.",
|
||||
"server_down": "Ingen forbindelse til serveren.",
|
||||
"description": "Dette løser seg vanligvis av seg selv. Vennligst vent mens vi sjekker serveren.",
|
||||
"retry_in": "Prøver på nytt om {seconds} sekunder...",
|
||||
"retry_now": "Prøver å gjenopprette forbindelsen...",
|
||||
"retry": "Prøv på nytt",
|
||||
"footer": "Hvis problemet vedvarer, kontakt support."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3844,5 +3844,16 @@
|
||||
"not_supported_browser": "Din webbläsare stöder inte lösennycklar. Prova en modern webbläsare som Chrome, Safari, Firefox eller Edge.",
|
||||
"login_with_passkey": "Logga in med lösennyckel",
|
||||
"login_failed": "Kunde inte logga in med lösennyckel. Försök igen."
|
||||
},
|
||||
"connectivity": {
|
||||
"title": "Anslutningsproblem",
|
||||
"subtitle": "Försöker igen var 5:e sekund...",
|
||||
"no_internet": "Ingen internetanslutning.",
|
||||
"server_down": "Ingen anslutning till servern.",
|
||||
"description": "Det här brukar lösa sig själv. Vänta medan vi kontrollerar servern.",
|
||||
"retry_in": "Försöker igen om {seconds} sekunder...",
|
||||
"retry_now": "Försöker att återställa anslutningen...",
|
||||
"retry": "Försök igen",
|
||||
"footer": "Om problemet kvarstår, kontakta supporten."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ import MyMaterials from "@/views/dashboards/userDashboard/materials/MyMaterials.
|
||||
|
||||
/** Errors */
|
||||
import NotFound from './views/errors/NotFound.vue';
|
||||
import ConnectivityIssue from './views/errors/ConnectivityIssue.vue';
|
||||
import MyOrders from "@/views/dashboards/userDashboard/orders/MyOrders.vue";
|
||||
import NewVehicle from "@/views/dashboards/userDashboard/vehicles/NewVehicle.vue";
|
||||
import MyBookings from "@/views/dashboards/userDashboard/bookings/MyBookings.vue";
|
||||
@@ -886,6 +887,11 @@ export const router = createRouter({
|
||||
path: '/guest/book/wash',
|
||||
component: GuestBookExteriorWash,
|
||||
},
|
||||
{
|
||||
name: 'connectivity-issue',
|
||||
path: '/connectivity-issue',
|
||||
component: ConnectivityIssue
|
||||
},
|
||||
{
|
||||
name: 'notfound',
|
||||
path: '/:pathMatch(.*)*',
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* This is the user store.
|
||||
*/
|
||||
import { createStore } from 'vuex';
|
||||
import { API_URL } from '../config.js';
|
||||
|
||||
export default createStore({
|
||||
state: {
|
||||
|
||||
@@ -6,6 +6,7 @@ import AuthPageWrapper from "@/components/page/wrappers/AuthPageWrapper.vue";
|
||||
import GuestDashboardPageWrapper from "@/views/guest/GuestDashboardPageWrapper.vue";
|
||||
import Viewport from "@/components/viewport/Viewport.vue";
|
||||
import logo from "@/assets/img/truckwash-banner.png";
|
||||
import ConnectivityIssue from "@/views/errors/ConnectivityIssue.vue";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -18,7 +19,9 @@ import logo from "@/assets/img/truckwash-banner.png";
|
||||
<router-link :to="{ name: 'landing' }" class="logo">
|
||||
<img :src="logo" alt="Truck Wash">
|
||||
</router-link>
|
||||
<LoginForm />
|
||||
<ConnectivityIssue>
|
||||
<LoginForm />
|
||||
</ConnectivityIssue>
|
||||
<p>{{ $t('auth.terms_acceptance') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script setup>
|
||||
import SubuserLoginForm from "@/components/forms/auth/SubuserLoginForm.vue";
|
||||
import logo from "@/assets/img/truckwash-banner.png";
|
||||
import ConnectivityIssue from "@/views/errors/ConnectivityIssue.vue";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -13,7 +14,9 @@ import logo from "@/assets/img/truckwash-banner.png";
|
||||
<router-link :to="{ name: 'landing' }" class="logo">
|
||||
<img :src="logo" alt="Truck Wash">
|
||||
</router-link>
|
||||
<SubuserLoginForm />
|
||||
<ConnectivityIssue>
|
||||
<SubuserLoginForm />
|
||||
</ConnectivityIssue>
|
||||
<p>{{ $t('auth.terms_acceptance') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import {defineProps, onMounted, watch} from 'vue'
|
||||
import { onMounted, watch} from 'vue'
|
||||
import { ref } from 'vue'
|
||||
import {authenticatedRequest} from "@/components/session/authenticatedRequest.vue";
|
||||
const props = defineProps({
|
||||
|
||||
+16
-12
@@ -200,17 +200,20 @@ const getColspan = () => {
|
||||
<div>
|
||||
<table class="table is-fullwidth is-hoverable is-striped"
|
||||
style="table-layout: fixed;">
|
||||
<tr>
|
||||
<th>{{ t('tables.bookings.date') }}</th>
|
||||
<th v-show="fieldVisibility.customer_number">{{ t('tables.common.customer') }}</th>
|
||||
<th>{{ t('tables.bookings.vehicles') }}</th>
|
||||
<th>{{ t('tables.bookings.department') }}</th>
|
||||
<th>{{ t('tables.bookings.po') }}</th>
|
||||
<th>{{ t('tables.bookings.reference') }}</th>
|
||||
<th>{{ t('tables.bookings.pickup') }}</th>
|
||||
<th>{{ t('tables.bookings.services') }}</th>
|
||||
<th><!-- Actions --></th>
|
||||
</tr>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ t('tables.bookings.date') }}</th>
|
||||
<th v-show="fieldVisibility.customer_number">{{ t('tables.common.customer') }}</th>
|
||||
<th>{{ t('tables.bookings.vehicles') }}</th>
|
||||
<th>{{ t('tables.bookings.department') }}</th>
|
||||
<th>{{ t('tables.bookings.po') }}</th>
|
||||
<th>{{ t('tables.bookings.reference') }}</th>
|
||||
<th>{{ t('tables.bookings.pickup') }}</th>
|
||||
<th>{{ t('tables.bookings.services') }}</th>
|
||||
<th><!-- Actions --></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<template v-if="(objects || []).length === 0">
|
||||
<tr>
|
||||
<td :colspan="getColspan()" class="has-text-centered">{{ t('tables.bookings.no_bookings_found') }}</td>
|
||||
@@ -394,7 +397,8 @@ const getColspan = () => {
|
||||
</tr>
|
||||
</template>
|
||||
</template>
|
||||
</table>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</template>
|
||||
<template #mobile>
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue';
|
||||
import {useI18n} from "vue-i18n";
|
||||
import {BButton, BIcon} from "buefy";
|
||||
const { t } = useI18n();
|
||||
import { SessionUser } from "@/components/session/token/SessionUser.vue";
|
||||
// font awesome icon name
|
||||
const iconName = 'wifi';
|
||||
const connectionStatus = ref<'loading' | 'ok' | 'error'>('loading');
|
||||
const check_connection = () => {
|
||||
reset_timer();
|
||||
try {
|
||||
SessionUser.request('/ping')
|
||||
.then((response: any) => {
|
||||
connectionStatus.value = 'ok';
|
||||
console.warn('Connection OK', response);
|
||||
})
|
||||
.catch((error: any) => {
|
||||
// Keep the user on the error page if the connection check fails
|
||||
connectionStatus.value = 'error';
|
||||
console.error('Connection failed, staying on the error page', error);
|
||||
});
|
||||
} catch (error) {
|
||||
connectionStatus.value = 'error';
|
||||
console.error('Unexpected error during connection check', error);
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
check_connection();
|
||||
// Check every 5 seconds
|
||||
setInterval(() => {
|
||||
check_connection();
|
||||
}, 5000);
|
||||
})
|
||||
|
||||
const timer = ref<NodeJS.Timeout | null>(null);
|
||||
const clear_timer_interval = () => {
|
||||
if (timer.value) {
|
||||
clearInterval(timer.value);
|
||||
timer.value = null;
|
||||
}
|
||||
}
|
||||
|
||||
const reset_timer = () => {
|
||||
clear_timer_interval();
|
||||
start_timer_interval();
|
||||
}
|
||||
|
||||
const start_timer_interval = () => {
|
||||
time_until_retry.value = 5;
|
||||
timer.value = setInterval(() => {
|
||||
time_until_retry.value--;
|
||||
if (time_until_retry.value === 0) {
|
||||
clearInterval(timer.value);
|
||||
}
|
||||
}, 1000)
|
||||
}
|
||||
const time_until_retry = ref(5);
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<template>
|
||||
<template v-if="connectionStatus === 'error'">
|
||||
<section class="hero is-light" v-if="connectionStatus === 'error'">
|
||||
<div class="hero-body">
|
||||
<div class="container has-text-centered">
|
||||
<div class="icon is-large mb-5">
|
||||
<b-icon :icon="iconName" type="is-danger" size="is-large" style="animation: pulse 2s infinite;" pack="fas"></b-icon>
|
||||
</div>
|
||||
<h1 class="title mb-4">{{ t('connectivity.title') }}</h1>
|
||||
<h2 class="subtitle mb-5">{{ t('connectivity.subtitle') }}</h2>
|
||||
<p class="content mt-6">{{ t('connectivity.description') }}</p>
|
||||
<!-- Retry timer -->
|
||||
<p class="content mt-2">
|
||||
<span v-if="time_until_retry > 0">{{ t('connectivity.retry_in', { seconds: time_until_retry }) }}</span>
|
||||
<span v-else>{{ t('connectivity.retry_now') }}</span>
|
||||
</p>
|
||||
|
||||
<b-button
|
||||
class="mt-6"
|
||||
type="is-primary"
|
||||
size="is-large"
|
||||
icon-left="refresh"
|
||||
iconPack="fas"
|
||||
@click="check_connection()"
|
||||
>
|
||||
{{ t('connectivity.retry') }}
|
||||
</b-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hero-foot">
|
||||
<div class="container has-text-centered">
|
||||
<p class="is-size-7 has-text-grey">{{ t('connectivity.footer') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
<template v-if="connectionStatus === 'ok' || connectionStatus === 'loading'">
|
||||
<slot></slot>
|
||||
</template>
|
||||
</template>
|
||||
@@ -0,0 +1,201 @@
|
||||
/**
|
||||
* Test to verify i18n translations for MyBookingsBook component
|
||||
* are properly defined in both English and Danish locale files.
|
||||
*/
|
||||
|
||||
describe('i18n Bookings Translations', () => {
|
||||
const bookingKeys = [
|
||||
'guest_mode_warning',
|
||||
'guest_mode_login_prompt',
|
||||
'login',
|
||||
'select_wash_hall',
|
||||
'select_vehicle',
|
||||
'is_this_your_vehicle',
|
||||
'next_step_addons',
|
||||
'product_selection_unavailable',
|
||||
'login_for_products',
|
||||
'interior_wash',
|
||||
'exterior_wash',
|
||||
'no_items_in_basket',
|
||||
'total',
|
||||
'required',
|
||||
'expected_date_time',
|
||||
'reference',
|
||||
'po_number',
|
||||
'note',
|
||||
'customer_number',
|
||||
'enter_customer_number',
|
||||
'customer_not_found',
|
||||
'pickup',
|
||||
'incl_pickup',
|
||||
'basket',
|
||||
'step_x_of_y',
|
||||
'select_a_wash_hall',
|
||||
'enter_vehicle',
|
||||
'select_products',
|
||||
'select_date_time_customer',
|
||||
'back',
|
||||
'next',
|
||||
'go_to_confirmation',
|
||||
'missing_wash_hall',
|
||||
'missing_customer_number',
|
||||
'missing_valid_customer_number',
|
||||
'missing_vehicle',
|
||||
'missing_date',
|
||||
'missing_products',
|
||||
'error_copied',
|
||||
'guest_info_html',
|
||||
'confirm_booking'
|
||||
];
|
||||
|
||||
const commonKeys = [
|
||||
'select',
|
||||
'during_the_day',
|
||||
'approx',
|
||||
'thank_you',
|
||||
'booking_created',
|
||||
'unknown_error',
|
||||
'error_creating_booking',
|
||||
'try_again',
|
||||
'copy_error',
|
||||
'cancel',
|
||||
'close',
|
||||
'confirm'
|
||||
];
|
||||
|
||||
it('should have all booking translations defined in English', async () => {
|
||||
await browser.url('/');
|
||||
|
||||
const result = await browser.executeAsync(async (keys, done) => {
|
||||
try {
|
||||
const response = await fetch('/src/i18n/locales/en.json');
|
||||
const data = await response.json();
|
||||
|
||||
if (!data.user_dashboard || !data.user_dashboard.bookings || !data.user_dashboard.bookings.book) {
|
||||
done({ success: false, error: 'user_dashboard.bookings.book namespace not found' });
|
||||
return;
|
||||
}
|
||||
|
||||
const missingKeys = [];
|
||||
for (const key of keys) {
|
||||
if (!data.user_dashboard.bookings.book[key]) {
|
||||
missingKeys.push(key);
|
||||
}
|
||||
}
|
||||
|
||||
if (missingKeys.length > 0) {
|
||||
done({ success: false, error: 'Missing keys: ' + missingKeys.join(', ') });
|
||||
} else {
|
||||
done({ success: true, keyCount: Object.keys(data.user_dashboard.bookings.book).length });
|
||||
}
|
||||
} catch (e) {
|
||||
done({ success: false, error: e.message });
|
||||
}
|
||||
}, bookingKeys);
|
||||
|
||||
console.log('English booking result:', JSON.stringify(result));
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
|
||||
it('should have all booking translations defined in Danish', async () => {
|
||||
await browser.url('/');
|
||||
|
||||
const result = await browser.executeAsync(async (keys, done) => {
|
||||
try {
|
||||
const response = await fetch('/src/i18n/locales/da.json');
|
||||
const data = await response.json();
|
||||
|
||||
if (!data.user_dashboard || !data.user_dashboard.bookings || !data.user_dashboard.bookings.book) {
|
||||
done({ success: false, error: 'user_dashboard.bookings.book namespace not found' });
|
||||
return;
|
||||
}
|
||||
|
||||
const missingKeys = [];
|
||||
for (const key of keys) {
|
||||
if (!data.user_dashboard.bookings.book[key]) {
|
||||
missingKeys.push(key);
|
||||
}
|
||||
}
|
||||
|
||||
if (missingKeys.length > 0) {
|
||||
done({ success: false, error: 'Missing keys: ' + missingKeys.join(', ') });
|
||||
} else {
|
||||
done({ success: true, keyCount: Object.keys(data.user_dashboard.bookings.book).length });
|
||||
}
|
||||
} catch (e) {
|
||||
done({ success: false, error: e.message });
|
||||
}
|
||||
}, bookingKeys);
|
||||
|
||||
console.log('Danish booking result:', JSON.stringify(result));
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
|
||||
it('should have all common translations defined in English', async () => {
|
||||
await browser.url('/');
|
||||
|
||||
const result = await browser.executeAsync(async (keys, done) => {
|
||||
try {
|
||||
const response = await fetch('/src/i18n/locales/en.json');
|
||||
const data = await response.json();
|
||||
|
||||
if (!data.common) {
|
||||
done({ success: false, error: 'common namespace not found' });
|
||||
return;
|
||||
}
|
||||
|
||||
const missingKeys = [];
|
||||
for (const key of keys) {
|
||||
if (!data.common[key]) {
|
||||
missingKeys.push(key);
|
||||
}
|
||||
}
|
||||
|
||||
if (missingKeys.length > 0) {
|
||||
done({ success: false, error: 'Missing keys: ' + missingKeys.join(', ') });
|
||||
} else {
|
||||
done({ success: true, keyCount: Object.keys(data.common).length });
|
||||
}
|
||||
} catch (e) {
|
||||
done({ success: false, error: e.message });
|
||||
}
|
||||
}, commonKeys);
|
||||
|
||||
console.log('English common result:', JSON.stringify(result));
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
|
||||
it('should have all common translations defined in Danish', async () => {
|
||||
await browser.url('/');
|
||||
|
||||
const result = await browser.executeAsync(async (keys, done) => {
|
||||
try {
|
||||
const response = await fetch('/src/i18n/locales/da.json');
|
||||
const data = await response.json();
|
||||
|
||||
if (!data.common) {
|
||||
done({ success: false, error: 'common namespace not found' });
|
||||
return;
|
||||
}
|
||||
|
||||
const missingKeys = [];
|
||||
for (const key of keys) {
|
||||
if (!data.common[key]) {
|
||||
missingKeys.push(key);
|
||||
}
|
||||
}
|
||||
|
||||
if (missingKeys.length > 0) {
|
||||
done({ success: false, error: 'Missing keys: ' + missingKeys.join(', ') });
|
||||
} else {
|
||||
done({ success: true, keyCount: Object.keys(data.common).length });
|
||||
}
|
||||
} catch (e) {
|
||||
done({ success: false, error: e.message });
|
||||
}
|
||||
}, commonKeys);
|
||||
|
||||
console.log('Danish common result:', JSON.stringify(result));
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user