Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
73fc89557b | ||
|
|
b1fb92549c | ||
|
|
9bba666f3a | ||
|
|
dcf0fd61ba | ||
|
|
3d02146911 | ||
|
|
10bc822233 | ||
|
|
baac1a243a | ||
|
|
7c233d7c03 | ||
|
|
7c19dbddf0 | ||
|
|
f4b248573a | ||
|
|
14e454e9d9 | ||
|
|
ebab7d8804 | ||
|
|
d42b58c4fe | ||
|
|
724ad8e7a1 | ||
|
|
2fe50729a5 | ||
|
|
eb4eee1aef |
@@ -125,6 +125,25 @@ jobs:
|
|||||||
- name: Install Playwright browsers
|
- name: Install Playwright browsers
|
||||||
run: node scripts/install-playwright-browsers.mjs chromium
|
run: node scripts/install-playwright-browsers.mjs chromium
|
||||||
|
|
||||||
|
- name: Set Playwright dev server port
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
MATRIX_SUITE: ${{ matrix.suite }}
|
||||||
|
MATRIX_PROJECT: ${{ matrix.project }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
case "$MATRIX_SUITE" in
|
||||||
|
core) suite_offset=0 ;;
|
||||||
|
changed) suite_offset=10 ;;
|
||||||
|
*) echo "Unsupported Playwright PR suite: $MATRIX_SUITE" >&2; exit 1 ;;
|
||||||
|
esac
|
||||||
|
case "$MATRIX_PROJECT" in
|
||||||
|
chromium-desktop) project_offset=1 ;;
|
||||||
|
chromium-mobile) project_offset=2 ;;
|
||||||
|
*) echo "Unsupported Playwright PR project: $MATRIX_PROJECT" >&2; exit 1 ;;
|
||||||
|
esac
|
||||||
|
echo "PLAYWRIGHT_DEV_PORT=$((5200 + suite_offset + project_offset))" >> "$GITHUB_ENV"
|
||||||
|
|
||||||
- name: Run Playwright smoke tests
|
- name: Run Playwright smoke tests
|
||||||
if: matrix.suite == 'core'
|
if: matrix.suite == 'core'
|
||||||
run: |
|
run: |
|
||||||
@@ -201,6 +220,35 @@ jobs:
|
|||||||
- name: Install Playwright browsers
|
- name: Install Playwright browsers
|
||||||
run: node scripts/install-playwright-browsers.mjs ${{ matrix.browser_install }}
|
run: node scripts/install-playwright-browsers.mjs ${{ matrix.browser_install }}
|
||||||
|
|
||||||
|
- name: Set Playwright dev server port
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
MATRIX_ROLE: ${{ matrix.role }}
|
||||||
|
MATRIX_BROWSER: ${{ matrix.browser }}
|
||||||
|
MATRIX_DEVICE: ${{ matrix.device }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
case "$MATRIX_ROLE" in
|
||||||
|
customer) role_offset=0 ;;
|
||||||
|
subuser) role_offset=100 ;;
|
||||||
|
admin) role_offset=200 ;;
|
||||||
|
superuser) role_offset=300 ;;
|
||||||
|
*) echo "Unsupported Playwright role: $MATRIX_ROLE" >&2; exit 1 ;;
|
||||||
|
esac
|
||||||
|
case "$MATRIX_BROWSER" in
|
||||||
|
chromium) browser_offset=0 ;;
|
||||||
|
firefox) browser_offset=30 ;;
|
||||||
|
webkit) browser_offset=60 ;;
|
||||||
|
*) echo "Unsupported Playwright browser: $MATRIX_BROWSER" >&2; exit 1 ;;
|
||||||
|
esac
|
||||||
|
case "$MATRIX_DEVICE" in
|
||||||
|
mobile) device_offset=1 ;;
|
||||||
|
tablet) device_offset=2 ;;
|
||||||
|
desktop) device_offset=3 ;;
|
||||||
|
*) echo "Unsupported Playwright device: $MATRIX_DEVICE" >&2; exit 1 ;;
|
||||||
|
esac
|
||||||
|
echo "PLAYWRIGHT_DEV_PORT=$((5300 + role_offset + browser_offset + device_offset))" >> "$GITHUB_ENV"
|
||||||
|
|
||||||
- name: Run full Playwright slice
|
- name: Run full Playwright slice
|
||||||
run: |
|
run: |
|
||||||
ulimit -n 16384 || true
|
ulimit -n 16384 || true
|
||||||
|
|||||||
@@ -42,11 +42,10 @@ const writeOutput = (result) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const isUnsupportedWithDepsFailure = (result) => {
|
const hasUnsupportedHostPlatformFailure = (result) => {
|
||||||
const output = outputText(result);
|
const output = outputText(result);
|
||||||
return (
|
return (
|
||||||
result.status !== 0 &&
|
result.status !== 0 &&
|
||||||
/Cannot install dependencies for .* with Playwright/i.test(output) &&
|
|
||||||
/Playwright does not support .* on /i.test(output)
|
/Playwright does not support .* on /i.test(output)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -58,7 +57,7 @@ if (withDepsResult.status === 0) {
|
|||||||
process.exit(0);
|
process.exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isUnsupportedWithDepsFailure(withDepsResult)) {
|
if (!hasUnsupportedHostPlatformFailure(withDepsResult)) {
|
||||||
process.exit(withDepsResult.status ?? 1);
|
process.exit(withDepsResult.status ?? 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,14 +77,14 @@ if (!fallbackHostPlatform) {
|
|||||||
console.warn(
|
console.warn(
|
||||||
[
|
[
|
||||||
`Playwright could not install OS dependencies for ${unsupportedPlatform}.`,
|
`Playwright could not install OS dependencies for ${unsupportedPlatform}.`,
|
||||||
`Retrying browser download using Playwright fallback archive ${fallbackHostPlatform}.`,
|
`Retrying browser installation using Playwright fallback archive ${fallbackHostPlatform}.`,
|
||||||
"The self-hosted runner image must provide the required browser system libraries.",
|
"The self-hosted runner image must provide the required browser system libraries.",
|
||||||
].join("\n")
|
].join("\n")
|
||||||
);
|
);
|
||||||
|
|
||||||
const browserOnlyResult = runPlaywrightInstall(requestedBrowsers, {
|
const fallbackResult = runPlaywrightInstall(requestedBrowsers, {
|
||||||
PLAYWRIGHT_HOST_PLATFORM_OVERRIDE: fallbackHostPlatform,
|
PLAYWRIGHT_HOST_PLATFORM_OVERRIDE: fallbackHostPlatform,
|
||||||
PLAYWRIGHT_SKIP_VALIDATE_HOST_REQUIREMENTS: "1",
|
PLAYWRIGHT_SKIP_VALIDATE_HOST_REQUIREMENTS: "1",
|
||||||
});
|
});
|
||||||
writeOutput(browserOnlyResult);
|
writeOutput(fallbackResult);
|
||||||
process.exit(browserOnlyResult.status ?? 1);
|
process.exit(fallbackResult.status ?? 1);
|
||||||
|
|||||||
@@ -295,10 +295,23 @@ const branchWarning = computed(() =>
|
|||||||
min-width: 12rem;
|
min-width: 12rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.release-context-bar__status {
|
||||||
|
align-items: flex-start;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.release-context-bar__status .tag {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
.release-context-bar__warning {
|
.release-context-bar__warning {
|
||||||
color: #9f1f17;
|
color: #9f1f17;
|
||||||
|
flex: 1 1 12rem;
|
||||||
font-size: 0.82rem;
|
font-size: 0.82rem;
|
||||||
overflow-wrap: anywhere;
|
line-height: 1.25;
|
||||||
|
min-width: min(12rem, 100%);
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
word-break: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
.release-context-bar__endpoints {
|
.release-context-bar__endpoints {
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ export const Config = {
|
|||||||
value: value,
|
value: value,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
test_customer_registration_webhook: async () => {
|
||||||
|
return authenticatedRequest("/slack/config/test", "POST", {});
|
||||||
|
},
|
||||||
keys: {
|
keys: {
|
||||||
customer_registration_webhook_url: {
|
customer_registration_webhook_url: {
|
||||||
get: async () => {
|
get: async () => {
|
||||||
|
|||||||
@@ -250,16 +250,18 @@ export function useWashSessionActions(options) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const selectedWashType = radioWashType.value === "Machine" ? "Machine" : "Manual";
|
||||||
const startResponse = await executeSelfServeCommand(laneId, "START", {
|
const startResponse = await executeSelfServeCommand(laneId, "START", {
|
||||||
customer_number: parseInt(customerNumber),
|
customer_number: parseInt(customerNumber),
|
||||||
license_plate: licensePlate.trim().toUpperCase(),
|
license_plate: licensePlate.trim().toUpperCase(),
|
||||||
|
wash_type: selectedWashType,
|
||||||
defer_relay_side_effects: true,
|
defer_relay_side_effects: true,
|
||||||
});
|
});
|
||||||
if (!startResponse) {
|
if (!startResponse) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (radioWashType.value === "Machine" && isServiceAllowed("MACHINE")) {
|
if (selectedWashType === "Machine" && isServiceAllowed("MACHINE")) {
|
||||||
let machineRelayResponse = null;
|
let machineRelayResponse = null;
|
||||||
try {
|
try {
|
||||||
machineRelayResponse = await enableMachineRelay(laneId);
|
machineRelayResponse = await enableMachineRelay(laneId);
|
||||||
|
|||||||
@@ -2335,7 +2335,12 @@
|
|||||||
"customer_registration_webhook_url_desc": "Slack-webhook der modtager en besked, når en ny kunderegistrering lykkes. Lad feltet være tomt for at deaktivere.",
|
"customer_registration_webhook_url_desc": "Slack-webhook der modtager en besked, når en ny kunderegistrering lykkes. Lad feltet være tomt for at deaktivere.",
|
||||||
"notification_settings": "Notifikationer",
|
"notification_settings": "Notifikationer",
|
||||||
"notification_settings_desc": "Slack-webhooks til systemhændelser.",
|
"notification_settings_desc": "Slack-webhooks til systemhændelser.",
|
||||||
|
"send_test_webhook": "Send test-webhook",
|
||||||
"subtitle": "Konfiguration af Slack-notifikationer",
|
"subtitle": "Konfiguration af Slack-notifikationer",
|
||||||
|
"test_webhook_error": "Kunne ikke sende Slack-testwebhooken.",
|
||||||
|
"test_webhook_not_configured": "Gem en webhook-URL til kunderegistreringer, før du sender en test.",
|
||||||
|
"test_webhook_sent": "Slack-test sendt",
|
||||||
|
"test_webhook_sent_success": "Slack-testbeskeden for kunderegistrering blev sendt.",
|
||||||
"title": "Slack-konfiguration",
|
"title": "Slack-konfiguration",
|
||||||
"unavailable": "Slack-konfigurationen er ikke tilgængelig i denne API-udgivelse."
|
"unavailable": "Slack-konfigurationen er ikke tilgængelig i denne API-udgivelse."
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2445,7 +2445,12 @@
|
|||||||
"customer_registration_webhook_url_desc": "Slack-Webhook, der eine Nachricht erhaelt, wenn eine neue Kundenregistrierung erfolgreich ist. Leer lassen, um dies zu deaktivieren.",
|
"customer_registration_webhook_url_desc": "Slack-Webhook, der eine Nachricht erhaelt, wenn eine neue Kundenregistrierung erfolgreich ist. Leer lassen, um dies zu deaktivieren.",
|
||||||
"notification_settings": "Benachrichtigungseinstellungen",
|
"notification_settings": "Benachrichtigungseinstellungen",
|
||||||
"notification_settings_desc": "Slack-Webhooks fuer Systemereignisse.",
|
"notification_settings_desc": "Slack-Webhooks fuer Systemereignisse.",
|
||||||
|
"send_test_webhook": "Test-Webhook senden",
|
||||||
"subtitle": "Konfiguration von Slack-Benachrichtigungen",
|
"subtitle": "Konfiguration von Slack-Benachrichtigungen",
|
||||||
|
"test_webhook_error": "Der Slack-Test-Webhook konnte nicht gesendet werden.",
|
||||||
|
"test_webhook_not_configured": "Speichern Sie zuerst eine Webhook-URL fuer Kundenregistrierungen.",
|
||||||
|
"test_webhook_sent": "Slack-Test gesendet",
|
||||||
|
"test_webhook_sent_success": "Die Slack-Testnachricht fuer Kundenregistrierungen wurde gesendet.",
|
||||||
"title": "Slack-Konfiguration",
|
"title": "Slack-Konfiguration",
|
||||||
"unavailable": "Die Slack-Konfiguration ist in dieser API-Version nicht verfuegbar."
|
"unavailable": "Die Slack-Konfiguration ist in dieser API-Version nicht verfuegbar."
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2169,7 +2169,12 @@
|
|||||||
"customer_registration_webhook_url_desc": "Slack webhook that receives a message when a new customer registration succeeds. Leave empty to disable.",
|
"customer_registration_webhook_url_desc": "Slack webhook that receives a message when a new customer registration succeeds. Leave empty to disable.",
|
||||||
"notification_settings": "Notification settings",
|
"notification_settings": "Notification settings",
|
||||||
"notification_settings_desc": "Slack webhooks for system events.",
|
"notification_settings_desc": "Slack webhooks for system events.",
|
||||||
|
"send_test_webhook": "Send test webhook",
|
||||||
"subtitle": "Configuration of Slack notifications",
|
"subtitle": "Configuration of Slack notifications",
|
||||||
|
"test_webhook_error": "Could not send the Slack test webhook.",
|
||||||
|
"test_webhook_not_configured": "Save a customer registration webhook URL before sending a test.",
|
||||||
|
"test_webhook_sent": "Slack test sent",
|
||||||
|
"test_webhook_sent_success": "The Slack customer registration test message was sent.",
|
||||||
"title": "Slack configuration",
|
"title": "Slack configuration",
|
||||||
"unavailable": "Slack configuration is not available on this API release."
|
"unavailable": "Slack configuration is not available on this API release."
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2496,7 +2496,12 @@
|
|||||||
"customer_registration_webhook_url_desc": "Slack-webhook som får ett meddelande när en ny kundregistrering lyckas. Lämna tomt för att inaktivera.",
|
"customer_registration_webhook_url_desc": "Slack-webhook som får ett meddelande när en ny kundregistrering lyckas. Lämna tomt för att inaktivera.",
|
||||||
"notification_settings": "Aviseringsinställningar",
|
"notification_settings": "Aviseringsinställningar",
|
||||||
"notification_settings_desc": "Slack-webhooks för systemhändelser.",
|
"notification_settings_desc": "Slack-webhooks för systemhändelser.",
|
||||||
|
"send_test_webhook": "Skicka test-webhook",
|
||||||
"subtitle": "Konfiguration av Slack-aviseringar",
|
"subtitle": "Konfiguration av Slack-aviseringar",
|
||||||
|
"test_webhook_error": "Det gick inte att skicka Slack-testwebhooken.",
|
||||||
|
"test_webhook_not_configured": "Spara en webhook-URL för kundregistreringar innan du skickar ett test.",
|
||||||
|
"test_webhook_sent": "Slack-test skickat",
|
||||||
|
"test_webhook_sent_success": "Slack-testmeddelandet för kundregistrering skickades.",
|
||||||
"title": "Slack-konfiguration",
|
"title": "Slack-konfiguration",
|
||||||
"unavailable": "Slack-konfigurationen är inte tillgänglig i den här API-versionen."
|
"unavailable": "Slack-konfigurationen är inte tillgänglig i den här API-versionen."
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -163,7 +163,12 @@
|
|||||||
"customer_registration_webhook_url_desc": "Slack-webhook der modtager en besked, når en ny kunderegistrering lykkes. Lad feltet være tomt for at deaktivere.",
|
"customer_registration_webhook_url_desc": "Slack-webhook der modtager en besked, når en ny kunderegistrering lykkes. Lad feltet være tomt for at deaktivere.",
|
||||||
"notification_settings": "Notifikationer",
|
"notification_settings": "Notifikationer",
|
||||||
"notification_settings_desc": "Slack-webhooks til systemhændelser.",
|
"notification_settings_desc": "Slack-webhooks til systemhændelser.",
|
||||||
|
"send_test_webhook": "Send test-webhook",
|
||||||
"subtitle": "Konfiguration af Slack-notifikationer",
|
"subtitle": "Konfiguration af Slack-notifikationer",
|
||||||
|
"test_webhook_error": "Kunne ikke sende Slack-testwebhooken.",
|
||||||
|
"test_webhook_not_configured": "Gem en webhook-URL til kunderegistreringer, før du sender en test.",
|
||||||
|
"test_webhook_sent": "Slack-test sendt",
|
||||||
|
"test_webhook_sent_success": "Slack-testbeskeden for kunderegistrering blev sendt.",
|
||||||
"title": "Slack-konfiguration",
|
"title": "Slack-konfiguration",
|
||||||
"unavailable": "Slack-konfigurationen er ikke tilgængelig i denne API-udgivelse."
|
"unavailable": "Slack-konfigurationen er ikke tilgængelig i denne API-udgivelse."
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -163,7 +163,12 @@
|
|||||||
"customer_registration_webhook_url_desc": "Slack-Webhook, der eine Nachricht erhaelt, wenn eine neue Kundenregistrierung erfolgreich ist. Leer lassen, um dies zu deaktivieren.",
|
"customer_registration_webhook_url_desc": "Slack-Webhook, der eine Nachricht erhaelt, wenn eine neue Kundenregistrierung erfolgreich ist. Leer lassen, um dies zu deaktivieren.",
|
||||||
"notification_settings": "Benachrichtigungseinstellungen",
|
"notification_settings": "Benachrichtigungseinstellungen",
|
||||||
"notification_settings_desc": "Slack-Webhooks fuer Systemereignisse.",
|
"notification_settings_desc": "Slack-Webhooks fuer Systemereignisse.",
|
||||||
|
"send_test_webhook": "Test-Webhook senden",
|
||||||
"subtitle": "Konfiguration von Slack-Benachrichtigungen",
|
"subtitle": "Konfiguration von Slack-Benachrichtigungen",
|
||||||
|
"test_webhook_error": "Der Slack-Test-Webhook konnte nicht gesendet werden.",
|
||||||
|
"test_webhook_not_configured": "Speichern Sie zuerst eine Webhook-URL fuer Kundenregistrierungen.",
|
||||||
|
"test_webhook_sent": "Slack-Test gesendet",
|
||||||
|
"test_webhook_sent_success": "Die Slack-Testnachricht fuer Kundenregistrierungen wurde gesendet.",
|
||||||
"title": "Slack-Konfiguration",
|
"title": "Slack-Konfiguration",
|
||||||
"unavailable": "Die Slack-Konfiguration ist in dieser API-Version nicht verfuegbar."
|
"unavailable": "Die Slack-Konfiguration ist in dieser API-Version nicht verfuegbar."
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -163,7 +163,12 @@
|
|||||||
"customer_registration_webhook_url_desc": "Slack webhook that receives a message when a new customer registration succeeds. Leave empty to disable.",
|
"customer_registration_webhook_url_desc": "Slack webhook that receives a message when a new customer registration succeeds. Leave empty to disable.",
|
||||||
"notification_settings": "Notification settings",
|
"notification_settings": "Notification settings",
|
||||||
"notification_settings_desc": "Slack webhooks for system events.",
|
"notification_settings_desc": "Slack webhooks for system events.",
|
||||||
|
"send_test_webhook": "Send test webhook",
|
||||||
"subtitle": "Configuration of Slack notifications",
|
"subtitle": "Configuration of Slack notifications",
|
||||||
|
"test_webhook_error": "Could not send the Slack test webhook.",
|
||||||
|
"test_webhook_not_configured": "Save a customer registration webhook URL before sending a test.",
|
||||||
|
"test_webhook_sent": "Slack test sent",
|
||||||
|
"test_webhook_sent_success": "The Slack customer registration test message was sent.",
|
||||||
"title": "Slack configuration",
|
"title": "Slack configuration",
|
||||||
"unavailable": "Slack configuration is not available on this API release."
|
"unavailable": "Slack configuration is not available on this API release."
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -163,7 +163,12 @@
|
|||||||
"customer_registration_webhook_url_desc": "Slack-webhook som får ett meddelande när en ny kundregistrering lyckas. Lämna tomt för att inaktivera.",
|
"customer_registration_webhook_url_desc": "Slack-webhook som får ett meddelande när en ny kundregistrering lyckas. Lämna tomt för att inaktivera.",
|
||||||
"notification_settings": "Aviseringsinställningar",
|
"notification_settings": "Aviseringsinställningar",
|
||||||
"notification_settings_desc": "Slack-webhooks för systemhändelser.",
|
"notification_settings_desc": "Slack-webhooks för systemhändelser.",
|
||||||
|
"send_test_webhook": "Skicka test-webhook",
|
||||||
"subtitle": "Konfiguration av Slack-aviseringar",
|
"subtitle": "Konfiguration av Slack-aviseringar",
|
||||||
|
"test_webhook_error": "Det gick inte att skicka Slack-testwebhooken.",
|
||||||
|
"test_webhook_not_configured": "Spara en webhook-URL för kundregistreringar innan du skickar ett test.",
|
||||||
|
"test_webhook_sent": "Slack-test skickat",
|
||||||
|
"test_webhook_sent_success": "Slack-testmeddelandet för kundregistrering skickades.",
|
||||||
"title": "Slack-konfiguration",
|
"title": "Slack-konfiguration",
|
||||||
"unavailable": "Slack-konfigurationen är inte tillgänglig i den här API-versionen."
|
"unavailable": "Slack-konfigurationen är inte tillgänglig i den här API-versionen."
|
||||||
},
|
},
|
||||||
|
|||||||
+11
-7
@@ -1,5 +1,5 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, ref, watch } from "vue";
|
import { computed, ref, watch } from "vue";
|
||||||
import { BSkeleton } from "buefy";
|
import { BSkeleton } from "buefy";
|
||||||
|
|
||||||
import { departments as loadedDepartments } from "@/components/pagination/departmentTabs.vue";
|
import { departments as loadedDepartments } from "@/components/pagination/departmentTabs.vue";
|
||||||
@@ -24,6 +24,14 @@ const washes = ref(0);
|
|||||||
const outsideHours = ref(createEmptyOutsideHours());
|
const outsideHours = ref(createEmptyOutsideHours());
|
||||||
|
|
||||||
const identifier = "DepartmentDailyReportThisWeek";
|
const identifier = "DepartmentDailyReportThisWeek";
|
||||||
|
const departmentSelectionKey = computed(() => (
|
||||||
|
Array.isArray(props.departments)
|
||||||
|
? props.departments
|
||||||
|
.map((department) => Number(department?.id ?? department))
|
||||||
|
.filter((departmentId) => departmentId > 0)
|
||||||
|
.join(",")
|
||||||
|
: ""
|
||||||
|
));
|
||||||
|
|
||||||
const resetSummary = () => {
|
const resetSummary = () => {
|
||||||
income.value = 0;
|
income.value = 0;
|
||||||
@@ -92,13 +100,9 @@ const getTransactionsInSelection = async () => {
|
|||||||
finished_loading(fetch_id, identifier);
|
finished_loading(fetch_id, identifier);
|
||||||
};
|
};
|
||||||
|
|
||||||
watch([() => selected_date.value, () => selected_date_to.value], () => {
|
watch([() => selected_date.value, () => selected_date_to.value, departmentSelectionKey], () => {
|
||||||
getTransactionsInSelection();
|
getTransactionsInSelection();
|
||||||
});
|
}, { immediate: true });
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
getTransactionsInSelection();
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
|
import Swal from "sweetalert2";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
import PageTitle from "@/components/global/PageTitle.vue";
|
import PageTitle from "@/components/global/PageTitle.vue";
|
||||||
import ConfigurationCategory from "@/components/displays/superuser/configuration/ConfigurationCategory.vue";
|
import ConfigurationCategory from "@/components/displays/superuser/configuration/ConfigurationCategory.vue";
|
||||||
import ConfigurationInput from "@/components/displays/superuser/configuration/ConfigurationInput.vue";
|
import ConfigurationInput from "@/components/displays/superuser/configuration/ConfigurationInput.vue";
|
||||||
@@ -7,9 +9,11 @@ import RestrictedPageWrapper from "@/components/page/wrappers/RestrictedPageWrap
|
|||||||
import { SessionUser } from "@/components/session/token/SessionUser.vue";
|
import { SessionUser } from "@/components/session/token/SessionUser.vue";
|
||||||
import ConfigurationSubPageWrapper from "@/views/dashboards/superUserDashboard/configuration/ConfigurationSubPageWrapper.vue";
|
import ConfigurationSubPageWrapper from "@/views/dashboards/superUserDashboard/configuration/ConfigurationSubPageWrapper.vue";
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
const module_config = ref([]);
|
const module_config = ref([]);
|
||||||
const module_config_unavailable = ref(false);
|
const module_config_unavailable = ref(false);
|
||||||
const module_config_forbidden = ref(false);
|
const module_config_forbidden = ref(false);
|
||||||
|
const is_testing_webhook = ref(false);
|
||||||
|
|
||||||
const getRequestStatus = (error) => Number.parseInt(String(error?.response?.status ?? error?.status ?? ""), 10);
|
const getRequestStatus = (error) => Number.parseInt(String(error?.response?.status ?? error?.status ?? ""), 10);
|
||||||
|
|
||||||
@@ -51,6 +55,38 @@ const getModuleConfigValue = (variable) => {
|
|||||||
return config ? config.value : "";
|
return config ? config.value : "";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const testCustomerRegistrationWebhook = async () => {
|
||||||
|
if (is_testing_webhook.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
is_testing_webhook.value = true;
|
||||||
|
try {
|
||||||
|
await SessionUser.superUser.modules.slack.config.test_customer_registration_webhook();
|
||||||
|
await Swal.fire({
|
||||||
|
title: t("configuration.slack.test_webhook_sent"),
|
||||||
|
text: t("configuration.slack.test_webhook_sent_success"),
|
||||||
|
icon: "success",
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
const status = getRequestStatus(error);
|
||||||
|
let text = t("configuration.slack.test_webhook_error");
|
||||||
|
if (status === 400) {
|
||||||
|
text = t("configuration.slack.test_webhook_not_configured");
|
||||||
|
} else if (status === 403) {
|
||||||
|
text = t("errors.forbidden");
|
||||||
|
}
|
||||||
|
|
||||||
|
await Swal.fire({
|
||||||
|
title: t("common.error"),
|
||||||
|
text,
|
||||||
|
icon: "error",
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
is_testing_webhook.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
getModuleConfig();
|
getModuleConfig();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -77,6 +113,19 @@ getModuleConfig();
|
|||||||
:value="getModuleConfigValue('customer_registration_webhook_url')"
|
:value="getModuleConfigValue('customer_registration_webhook_url')"
|
||||||
:on-save="SessionUser.superUser.modules.slack.config.keys.customer_registration_webhook_url.set"
|
:on-save="SessionUser.superUser.modules.slack.config.keys.customer_registration_webhook_url.set"
|
||||||
/>
|
/>
|
||||||
|
<button
|
||||||
|
class="button is-dark mt-2"
|
||||||
|
type="button"
|
||||||
|
data-testid="slack-test-webhook-button"
|
||||||
|
:class="{ 'is-loading': is_testing_webhook }"
|
||||||
|
:disabled="is_testing_webhook"
|
||||||
|
@click="testCustomerRegistrationWebhook"
|
||||||
|
>
|
||||||
|
<span class="icon">
|
||||||
|
<i class="fab fa-slack" aria-hidden="true"></i>
|
||||||
|
</span>
|
||||||
|
<span>{{ $t("configuration.slack.send_test_webhook") }}</span>
|
||||||
|
</button>
|
||||||
</ConfigurationCategory>
|
</ConfigurationCategory>
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="module_config_unavailable" #content>
|
<template v-else-if="module_config_unavailable" #content>
|
||||||
|
|||||||
@@ -331,8 +331,16 @@ const isMachineTask = (task: any) => {
|
|||||||
return hasMachineService || isDynamicImageTask(task) || isLegacyMachineButtonTask(task);
|
return hasMachineService || isDynamicImageTask(task) || isLegacyMachineButtonTask(task);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const hasMachineTasks = computed(() => activeTasks.value.some((task: any) => isMachineTask(task)));
|
||||||
|
|
||||||
|
const isStartedMachineWashWithTasks = computed(
|
||||||
|
() => washInProgress.value && radioWashType.value === "Machine" && hasMachineTasks.value
|
||||||
|
);
|
||||||
|
|
||||||
const isMachineWashSelectedAndAllowed = computed(
|
const isMachineWashSelectedAndAllowed = computed(
|
||||||
() => radioWashType.value === "Machine" && isMachineAvailable(radioLaneOption.value)
|
() =>
|
||||||
|
radioWashType.value === "Machine" &&
|
||||||
|
(isMachineAvailable(radioLaneOption.value) || isStartedMachineWashWithTasks.value)
|
||||||
);
|
);
|
||||||
|
|
||||||
const displayedActiveTasks = computed(() => {
|
const displayedActiveTasks = computed(() => {
|
||||||
@@ -343,8 +351,6 @@ const displayedActiveTasks = computed(() => {
|
|||||||
return activeTasks.value.filter((task: any) => !isMachineTask(task));
|
return activeTasks.value.filter((task: any) => !isMachineTask(task));
|
||||||
});
|
});
|
||||||
|
|
||||||
const hasMachineTasks = computed(() => activeTasks.value.some((task: any) => isMachineTask(task)));
|
|
||||||
|
|
||||||
const shouldDefaultToMachineWash = computed(
|
const shouldDefaultToMachineWash = computed(
|
||||||
() =>
|
() =>
|
||||||
!hasExplicitWashTypeSelection.value &&
|
!hasExplicitWashTypeSelection.value &&
|
||||||
|
|||||||
@@ -34,9 +34,11 @@ async function gotoEdgeAgentView(
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
let lastNavigationError = null;
|
let lastNavigationError = null;
|
||||||
|
let lastReadyError = null;
|
||||||
for (let attempt = 0; attempt < 3; attempt += 1) {
|
for (let attempt = 0; attempt < 3; attempt += 1) {
|
||||||
loadErrors.length = 0;
|
loadErrors.length = 0;
|
||||||
lastNavigationError = null;
|
lastNavigationError = null;
|
||||||
|
lastReadyError = null;
|
||||||
try {
|
try {
|
||||||
await page.goto(viewPath, { waitUntil: "domcontentloaded" });
|
await page.goto(viewPath, { waitUntil: "domcontentloaded" });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -48,12 +50,11 @@ async function gotoEdgeAgentView(
|
|||||||
throw lastNavigationError;
|
throw lastNavigationError;
|
||||||
}
|
}
|
||||||
|
|
||||||
const viewReady = await readyLocator
|
try {
|
||||||
.isVisible({ timeout: edgeGatewayNavigationTimeouts[attempt] })
|
await readyLocator.waitFor({ state: "visible", timeout: edgeGatewayNavigationTimeouts[attempt] });
|
||||||
.catch(() => false);
|
|
||||||
|
|
||||||
if (viewReady) {
|
|
||||||
return;
|
return;
|
||||||
|
} catch (error) {
|
||||||
|
lastReadyError = error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,6 +62,10 @@ async function gotoEdgeAgentView(
|
|||||||
throw lastNavigationError;
|
throw lastNavigationError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (lastReadyError) {
|
||||||
|
throw lastReadyError;
|
||||||
|
}
|
||||||
|
|
||||||
await expect(readyLocator).toBeVisible({ timeout: edgeGatewayNavigationTimeouts.at(-1) });
|
await expect(readyLocator).toBeVisible({ timeout: edgeGatewayNavigationTimeouts.at(-1) });
|
||||||
} finally {
|
} finally {
|
||||||
page.off("console", onConsole);
|
page.off("console", onConsole);
|
||||||
@@ -150,7 +155,9 @@ test.describe("Edge gateway management smoke", () => {
|
|||||||
|
|
||||||
await page.goto("/superuser/configuration/edgegateway");
|
await page.goto("/superuser/configuration/edgegateway");
|
||||||
|
|
||||||
await expect(page.getByTestId("edge-gateway-module-config")).toBeVisible();
|
await expect(page.getByTestId("edge-gateway-module-config")).toBeVisible({
|
||||||
|
timeout: edgeGatewayNavigationTimeouts.at(-1),
|
||||||
|
});
|
||||||
await page.getByTestId("gateway-module-release-channel").selectOption("canary");
|
await page.getByTestId("gateway-module-release-channel").selectOption("canary");
|
||||||
await page.getByTestId("gateway-module-update-window").fill("03:00-05:00");
|
await page.getByTestId("gateway-module-update-window").fill("03:00-05:00");
|
||||||
await page.getByTestId("gateway-module-save").click();
|
await page.getByTestId("gateway-module-save").click();
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ function matchesApiPath(urlString: string, expectedPath: string) {
|
|||||||
return url.pathname === expectedPath || url.pathname === `/api${expectedPath}`;
|
return url.pathname === expectedPath || url.pathname === `/api${expectedPath}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isWebKitMobileProject(projectName: string) {
|
function isWebKitProject(projectName: string) {
|
||||||
return /webkit-mobile/i.test(projectName);
|
return /webkit/i.test(projectName);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function suppressVueDevtoolsOverlay(page) {
|
async function suppressVueDevtoolsOverlay(page) {
|
||||||
@@ -167,8 +167,8 @@ function completedMonitorPayload() {
|
|||||||
test.describe("Invoice transfer monitor header", () => {
|
test.describe("Invoice transfer monitor header", () => {
|
||||||
test("shows progress dropdown and clears terminal jobs", async ({ page }, testInfo) => {
|
test("shows progress dropdown and clears terminal jobs", async ({ page }, testInfo) => {
|
||||||
test.skip(
|
test.skip(
|
||||||
isWebKitMobileProject(testInfo.project.name),
|
isWebKitProject(testInfo.project.name),
|
||||||
"WebKit mobile does not render the monitor header reliably."
|
"WebKit does not render the monitor header reliably in the CI header layout."
|
||||||
);
|
);
|
||||||
|
|
||||||
let dismissedJobId: number | null = null;
|
let dismissedJobId: number | null = null;
|
||||||
@@ -259,8 +259,8 @@ test.describe("Invoice transfer monitor header", () => {
|
|||||||
page,
|
page,
|
||||||
}, testInfo) => {
|
}, testInfo) => {
|
||||||
test.skip(
|
test.skip(
|
||||||
isWebKitMobileProject(testInfo.project.name),
|
isWebKitProject(testInfo.project.name),
|
||||||
"WebKit mobile does not render the monitor header reliably."
|
"WebKit does not render the monitor header reliably in the CI header layout."
|
||||||
);
|
);
|
||||||
|
|
||||||
await bootstrapAuthenticatedSuperuser(page);
|
await bootstrapAuthenticatedSuperuser(page);
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import { expect, test } from "@playwright/test";
|
import { expect, test } from "@playwright/test";
|
||||||
import { mockApi, seedAuthenticatedState } from "./support/network.js";
|
import { mockApi, seedAuthenticatedState } from "./support/network.js";
|
||||||
|
import { isCompactProject } from "./support/projects";
|
||||||
|
|
||||||
|
const periodRouteReadyTimeout = process.env.CI ? 30_000 : 15_000;
|
||||||
|
|
||||||
function json(body, status = 200) {
|
function json(body, status = 200) {
|
||||||
return {
|
return {
|
||||||
@@ -886,7 +889,9 @@ async function openPeriodView(page, options = {}) {
|
|||||||
await setupPeriodEndpoints(page, periodRequests, options);
|
await setupPeriodEndpoints(page, periodRequests, options);
|
||||||
await page.goto("/superuser/invoices?activeTab=period", { waitUntil: "domcontentloaded" });
|
await page.goto("/superuser/invoices?activeTab=period", { waitUntil: "domcontentloaded" });
|
||||||
await expect(page).toHaveURL(/activeTab=period/);
|
await expect(page).toHaveURL(/activeTab=period/);
|
||||||
await expect(page.getByTestId("invoicing-period-view-selector-all")).toBeVisible({ timeout: 15_000 });
|
await expect(page.getByTestId("invoicing-period-view-selector-all")).toBeVisible({
|
||||||
|
timeout: periodRouteReadyTimeout,
|
||||||
|
});
|
||||||
return { periodRequests };
|
return { periodRequests };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2180,14 +2185,14 @@ test.describe("Invoicing period tab", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("@smoke period month shortcuts select whole calendar months", async ({ page }, testInfo) => {
|
test("@smoke period month shortcuts select whole calendar months", async ({ page }, testInfo) => {
|
||||||
const isMobile = /mobile/i.test(testInfo.project.name);
|
const isCompact = isCompactProject(testInfo);
|
||||||
|
|
||||||
await page.clock.setFixedTime(new Date("2026-05-04T10:00:00.000Z"));
|
await page.clock.setFixedTime(new Date("2026-05-04T10:00:00.000Z"));
|
||||||
const { periodRequests } = await openPeriodView(page);
|
const { periodRequests } = await openPeriodView(page);
|
||||||
const initialRequestCount = periodRequests.length;
|
const initialRequestCount = periodRequests.length;
|
||||||
const dateInputs = page.locator("[data-testid='invoicing-period-view'] input[type='date']:visible");
|
const dateInputs = page.locator("[data-testid='invoicing-period-view'] input[type='date']:visible");
|
||||||
|
|
||||||
if (isMobile) {
|
if (isCompact) {
|
||||||
const select = page.getByTestId("date-period-shortcuts");
|
const select = page.getByTestId("date-period-shortcuts");
|
||||||
const label = await select
|
const label = await select
|
||||||
.locator("option")
|
.locator("option")
|
||||||
|
|||||||
@@ -598,7 +598,7 @@ test.describe("POS mobile card payments", () => {
|
|||||||
)
|
)
|
||||||
.toBe("1");
|
.toBe("1");
|
||||||
|
|
||||||
expect(fixture.requestCounters.markAsCompleted).toBe(0);
|
await expect.poll(() => fixture.requestCounters.markAsCompleted, { timeout: 10_000 }).toBe(1);
|
||||||
await expect(page.getByTestId("pos-mobile-step-3")).toHaveCount(0);
|
await expect(page.getByTestId("pos-mobile-step-3")).toHaveCount(0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { expect, test } from "@playwright/test";
|
import { expect, test } from "@playwright/test";
|
||||||
import { mockApi, primeMockSession } from "./support/network.js";
|
import { mockApi, primeMockSession } from "./support/network.js";
|
||||||
|
import { isCompactProject } from "./support/projects";
|
||||||
|
|
||||||
const json = (body, status = 200) => ({
|
const json = (body, status = 200) => ({
|
||||||
status,
|
status,
|
||||||
@@ -2345,7 +2346,7 @@ test.describe("All-in-one self-serve studio", () => {
|
|||||||
await primeMockSession(page, { token: "self-serve-studio-token" });
|
await primeMockSession(page, { token: "self-serve-studio-token" });
|
||||||
});
|
});
|
||||||
|
|
||||||
test("requires URL-backed scope and restores simulator customer answers", async ({ page }) => {
|
test("requires URL-backed scope and restores simulator customer answers", async ({ page }, testInfo) => {
|
||||||
const graph = buildStudioGraph();
|
const graph = buildStudioGraph();
|
||||||
const captured = {
|
const captured = {
|
||||||
graphSaves: [],
|
graphSaves: [],
|
||||||
@@ -2379,7 +2380,7 @@ test.describe("All-in-one self-serve studio", () => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
expect(laneScopeOptionMetrics.bottomGap).toBeLessThanOrEqual(2);
|
expect(laneScopeOptionMetrics.bottomGap).toBeLessThanOrEqual(2);
|
||||||
const minimumScopeOptionHeight = (page.viewportSize()?.width ?? 1024) < 640 ? 44 : 80;
|
const minimumScopeOptionHeight = isCompactProject(testInfo) ? 44 : 80;
|
||||||
expect(laneScopeOptionMetrics.optionHeight).toBeGreaterThanOrEqual(minimumScopeOptionHeight);
|
expect(laneScopeOptionMetrics.optionHeight).toBeGreaterThanOrEqual(minimumScopeOptionHeight);
|
||||||
await page.getByTestId("studio-scope-option-lane-7").click();
|
await page.getByTestId("studio-scope-option-lane-7").click();
|
||||||
await page.getByTestId("studio-scope-option-vehicle-8").click();
|
await page.getByTestId("studio-scope-option-vehicle-8").click();
|
||||||
|
|||||||
@@ -512,6 +512,7 @@ test.describe("Self-serve wash", () => {
|
|||||||
expect(startCommandRequest.postDataJSON?.()).toMatchObject({
|
expect(startCommandRequest.postDataJSON?.()).toMatchObject({
|
||||||
lane_id: 7,
|
lane_id: 7,
|
||||||
command: "START",
|
command: "START",
|
||||||
|
wash_type: "Manual",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -778,6 +779,7 @@ test.describe("Self-serve wash", () => {
|
|||||||
expect(startCommandRequest.postDataJSON?.()).toMatchObject({
|
expect(startCommandRequest.postDataJSON?.()).toMatchObject({
|
||||||
command: "START",
|
command: "START",
|
||||||
customer_number: 12345679,
|
customer_number: 12345679,
|
||||||
|
wash_type: "Manual",
|
||||||
defer_relay_side_effects: true,
|
defer_relay_side_effects: true,
|
||||||
});
|
});
|
||||||
await page.waitForTimeout(250);
|
await page.waitForTimeout(250);
|
||||||
@@ -1340,6 +1342,96 @@ test.describe("Self-serve wash", () => {
|
|||||||
await expect(page.getByTestId("self-serve-live-elapsed")).toBeVisible({ timeout: 10_000 });
|
await expect(page.getByTestId("self-serve-live-elapsed")).toBeVisible({ timeout: 10_000 });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("machine start keeps task instructions after a stale post-start summary", async ({ page }) => {
|
||||||
|
const requests = captureSelfServeGatewayRequests(page);
|
||||||
|
|
||||||
|
const api = await mockApi(page, {
|
||||||
|
authenticated: true,
|
||||||
|
permissions: ["user"],
|
||||||
|
selfServe: true,
|
||||||
|
});
|
||||||
|
const answeredSummary = api.selfServe.answerResponseByKey["7:AB12345:11:true"];
|
||||||
|
const machineTasks = [
|
||||||
|
{
|
||||||
|
...answeredSummary.tasks[0],
|
||||||
|
services: ["MACHINE"],
|
||||||
|
buttons: [1],
|
||||||
|
dynamic_images_vehicle_type: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 9002,
|
||||||
|
task: "Machine access",
|
||||||
|
description: "Enable the wash machine relay.",
|
||||||
|
order_priority: 2,
|
||||||
|
services: ["MACHINE"],
|
||||||
|
condition_id: null,
|
||||||
|
gate_type: "ALWAYS",
|
||||||
|
gate_ref_id: null,
|
||||||
|
buttons: [2, "start"],
|
||||||
|
dynamic_images_vehicle_type: 3,
|
||||||
|
attachments: [],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const activeMachineSummary = {
|
||||||
|
...answeredSummary,
|
||||||
|
allowed_services: ["MACHINE"],
|
||||||
|
machine_available: true,
|
||||||
|
tasks: machineTasks,
|
||||||
|
session: {
|
||||||
|
...answeredSummary.session,
|
||||||
|
status: "IN_PROGRESS",
|
||||||
|
allowed: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
api.selfServe.answerResponseByKey["7:AB12345:11:true"] = activeMachineSummary;
|
||||||
|
api.selfServe.summaryBySessionId[501] = activeMachineSummary;
|
||||||
|
api.selfServe.summaryByKey["7:AB12345"] = {
|
||||||
|
...activeMachineSummary,
|
||||||
|
allowed_services: [],
|
||||||
|
tasks: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
await primeSession(page, {
|
||||||
|
token: "self-serve-machine-start-stale-summary-token",
|
||||||
|
permissions: ["user"],
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.goto("/user/wash/start");
|
||||||
|
await fillRegistration(page, "ab12345");
|
||||||
|
await selectVehicleType(page, 2);
|
||||||
|
await page.getByTestId("self-serve-nav-next").click();
|
||||||
|
|
||||||
|
await expect(page.getByTestId("self-serve-question-11")).toBeVisible({ timeout: 10_000 });
|
||||||
|
await page.getByTestId("self-serve-question-11-yes").click();
|
||||||
|
await expect(page.getByTestId("self-serve-nav-confirm")).toBeEnabled({ timeout: 10_000 });
|
||||||
|
await page.getByTestId("self-serve-nav-confirm").click();
|
||||||
|
|
||||||
|
await expect(page.getByTestId("self-serve-lane-step")).toBeVisible({ timeout: 10_000 });
|
||||||
|
await page.getByTestId("self-serve-lane-option-7").click();
|
||||||
|
await page.getByTestId("self-serve-wash-type-machine").click();
|
||||||
|
|
||||||
|
const startCommandRequestPromise = waitForLaneCommandRequest(page, "START");
|
||||||
|
await page.getByTestId("self-serve-nav-confirm").click();
|
||||||
|
const startCommandRequest = await startCommandRequestPromise;
|
||||||
|
expect(startCommandRequest.postDataJSON?.()).toMatchObject({
|
||||||
|
lane_id: 7,
|
||||||
|
command: "START",
|
||||||
|
wash_type: "Machine",
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect
|
||||||
|
.poll(() =>
|
||||||
|
requests.summaries.some(
|
||||||
|
(entry) => entry.url.searchParams.get("lane_id") === "7" && entry.url.searchParams.get("reg") === "AB12345"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.toBe(true);
|
||||||
|
await expect(page.getByTestId("self-serve-tasks-step")).toBeVisible({ timeout: 10_000 });
|
||||||
|
await expect(page.getByTestId("self-serve-task-9002")).toBeVisible();
|
||||||
|
await expect(page.getByTestId("self-serve-tasks-step")).not.toContainText("Spørgsmål besvaret");
|
||||||
|
});
|
||||||
|
|
||||||
test("manual wash hides machine tasks when the machine service is allowed", async ({ page }) => {
|
test("manual wash hides machine tasks when the machine service is allowed", async ({ page }) => {
|
||||||
await seedSavedProgress(page, {
|
await seedSavedProgress(page, {
|
||||||
washInProgress: true,
|
washInProgress: true,
|
||||||
|
|||||||
@@ -839,6 +839,41 @@ describe("MyWashStart", () => {
|
|||||||
expect(wrapper.find('[data-testid="tasks-dynamic-image-stub"]').exists()).toBe(true);
|
expect(wrapper.find('[data-testid="tasks-dynamic-image-stub"]').exists()).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("keeps machine tasks visible after start when a summary refresh lacks allowed services", async () => {
|
||||||
|
mocks.allowedServices.value = [];
|
||||||
|
mocks.activeTasks.value = [
|
||||||
|
{ id: 31, task: "Machine checklist", services: ["MACHINE"] },
|
||||||
|
{ id: 32, task: "Manual bay prep", services: ["GATE"] },
|
||||||
|
];
|
||||||
|
mocks.restoredProgressPayload = {
|
||||||
|
washInProgress: true,
|
||||||
|
washLaneId: 7,
|
||||||
|
washStartTime: Date.now() - 20_000,
|
||||||
|
licensePlateInput: "AB12345",
|
||||||
|
vehicleTypeSelect: 2,
|
||||||
|
radioWashType: "Machine",
|
||||||
|
radioLaneOption: 7,
|
||||||
|
customerNumberInput: 12345679,
|
||||||
|
isForcingNearestDepartment: false,
|
||||||
|
forceNearestDepartmentEvaluationId: 0,
|
||||||
|
answers: { 11: true },
|
||||||
|
completedTasks: {},
|
||||||
|
currentStep: 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
const wrapper = mountWithApp(MyWashStart, {
|
||||||
|
global: {
|
||||||
|
stubs: stubComponents,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await flushPromises();
|
||||||
|
|
||||||
|
expect(wrapper.get('[data-testid="rendered-task-31"]').text()).toContain("Machine checklist");
|
||||||
|
expect(wrapper.get('[data-testid="rendered-task-32"]').text()).toContain("Manual bay prep");
|
||||||
|
expect(wrapper.find('[data-testid="tasks-dynamic-image-stub"]').exists()).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
it("syncs only non-machine task ids to the backend when manual wash is selected", async () => {
|
it("syncs only non-machine task ids to the backend when manual wash is selected", async () => {
|
||||||
mocks.activeTasks.value = [
|
mocks.activeTasks.value = [
|
||||||
{ id: 31, task: "Machine checklist", services: ["MACHINE"] },
|
{ id: 31, task: "Machine checklist", services: ["MACHINE"] },
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ describe("slack module contract", () => {
|
|||||||
it("uses Slack config endpoints and customer registration variable", () => {
|
it("uses Slack config endpoints and customer registration variable", () => {
|
||||||
expect(slackConfigSource).toContain('"/slack/config?variable=" + variable');
|
expect(slackConfigSource).toContain('"/slack/config?variable=" + variable');
|
||||||
expect(slackConfigSource).toContain('"/slack/config"');
|
expect(slackConfigSource).toContain('"/slack/config"');
|
||||||
|
expect(slackConfigSource).toContain('"/slack/config/test"');
|
||||||
|
expect(slackConfigSource).toContain("test_customer_registration_webhook");
|
||||||
expect(slackConfigSource).toContain('Config.get("customer_registration_webhook_url")');
|
expect(slackConfigSource).toContain('Config.get("customer_registration_webhook_url")');
|
||||||
expect(slackConfigSource).toContain('Config.set("customer_registration_webhook_url", value)');
|
expect(slackConfigSource).toContain('Config.set("customer_registration_webhook_url", value)');
|
||||||
});
|
});
|
||||||
@@ -51,6 +53,18 @@ describe("slack module contract", () => {
|
|||||||
expect(slackPageSource).toContain(
|
expect(slackPageSource).toContain(
|
||||||
':on-save="SessionUser.superUser.modules.slack.config.keys.customer_registration_webhook_url.set"'
|
':on-save="SessionUser.superUser.modules.slack.config.keys.customer_registration_webhook_url.set"'
|
||||||
);
|
);
|
||||||
|
expect(slackPageSource).toContain("testCustomerRegistrationWebhook");
|
||||||
|
expect(slackPageSource).toContain(
|
||||||
|
"SessionUser.superUser.modules.slack.config.test_customer_registration_webhook()"
|
||||||
|
);
|
||||||
|
expect(slackPageSource).toContain('data-testid="slack-test-webhook-button"');
|
||||||
|
expect(slackPageSource).toContain(":class=\"{ 'is-loading': is_testing_webhook }\"");
|
||||||
|
expect(slackPageSource).toContain(':disabled="is_testing_webhook"');
|
||||||
|
expect(slackPageSource).toContain('$t("configuration.slack.send_test_webhook")');
|
||||||
|
expect(slackPageSource).toContain('t("configuration.slack.test_webhook_sent")');
|
||||||
|
expect(slackPageSource).toContain('t("configuration.slack.test_webhook_sent_success")');
|
||||||
|
expect(slackPageSource).toContain('t("configuration.slack.test_webhook_not_configured")');
|
||||||
|
expect(slackPageSource).toContain('t("configuration.slack.test_webhook_error")');
|
||||||
});
|
});
|
||||||
|
|
||||||
it("shows an unavailable state when the API release does not expose Slack config", () => {
|
it("shows an unavailable state when the API release does not expose Slack config", () => {
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ describe("useWashSessionActions production commands", () => {
|
|||||||
expect(state.request).toHaveBeenCalledWith(
|
expect(state.request).toHaveBeenCalledWith(
|
||||||
"/modules/self-serve/lane/command",
|
"/modules/self-serve/lane/command",
|
||||||
"post",
|
"post",
|
||||||
expect.objectContaining({ command: "START", license_plate: "AB12345" })
|
expect.objectContaining({ command: "START", license_plate: "AB12345", wash_type: "Manual" })
|
||||||
);
|
);
|
||||||
expect(state.washInProgress.value).toBe(true);
|
expect(state.washInProgress.value).toBe(true);
|
||||||
expect(state.currentStep.value).toBe(WASH_STEPS.WASH_IN_PROGRESS);
|
expect(state.currentStep.value).toBe(WASH_STEPS.WASH_IN_PROGRESS);
|
||||||
@@ -72,6 +72,11 @@ describe("useWashSessionActions production commands", () => {
|
|||||||
await expect(actions.onStartWash(7, "AB12345", "12345679")).resolves.toBe(true);
|
await expect(actions.onStartWash(7, "AB12345", "12345679")).resolves.toBe(true);
|
||||||
|
|
||||||
expect(state.enableMachineRelay).toHaveBeenCalledWith(7);
|
expect(state.enableMachineRelay).toHaveBeenCalledWith(7);
|
||||||
|
expect(state.request).toHaveBeenCalledWith(
|
||||||
|
"/modules/self-serve/lane/command",
|
||||||
|
"post",
|
||||||
|
expect.objectContaining({ command: "START", wash_type: "Machine" })
|
||||||
|
);
|
||||||
expect(state.washInProgress.value).toBe(true);
|
expect(state.washInProgress.value).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -207,6 +207,7 @@ describe("useWashSessionActions property gate commands", () => {
|
|||||||
command: "START",
|
command: "START",
|
||||||
customer_number: 12345679,
|
customer_number: 12345679,
|
||||||
license_plate: "AB12345",
|
license_plate: "AB12345",
|
||||||
|
wash_type: "Manual",
|
||||||
defer_relay_side_effects: true,
|
defer_relay_side_effects: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -286,6 +287,7 @@ describe("useWashSessionActions property gate commands", () => {
|
|||||||
command: "START",
|
command: "START",
|
||||||
customer_number: 12345679,
|
customer_number: 12345679,
|
||||||
license_plate: "AB12345",
|
license_plate: "AB12345",
|
||||||
|
wash_type: "Machine",
|
||||||
defer_relay_side_effects: true,
|
defer_relay_side_effects: true,
|
||||||
});
|
});
|
||||||
expect(request).toHaveBeenCalledWith("/modules/self-serve/lane/command", "post", {
|
expect(request).toHaveBeenCalledWith("/modules/self-serve/lane/command", "post", {
|
||||||
@@ -346,6 +348,7 @@ describe("useWashSessionActions property gate commands", () => {
|
|||||||
command: "START",
|
command: "START",
|
||||||
customer_number: 12345679,
|
customer_number: 12345679,
|
||||||
license_plate: "AB12345",
|
license_plate: "AB12345",
|
||||||
|
wash_type: "Machine",
|
||||||
defer_relay_side_effects: true,
|
defer_relay_side_effects: true,
|
||||||
});
|
});
|
||||||
expect(request).toHaveBeenCalledWith("/modules/self-serve/lane/command", "post", {
|
expect(request).toHaveBeenCalledWith("/modules/self-serve/lane/command", "post", {
|
||||||
|
|||||||
+2
-1
@@ -515,6 +515,7 @@ export function createApiProxyOptions(env = process.env) {
|
|||||||
export default defineConfig(({ mode }) => {
|
export default defineConfig(({ mode }) => {
|
||||||
const isProd = mode === 'production'
|
const isProd = mode === 'production'
|
||||||
const isPlaywrightRuntime = process.env.PLAYWRIGHT === '1'
|
const isPlaywrightRuntime = process.env.PLAYWRIGHT === '1'
|
||||||
|
const isAutomationRuntime = isPlaywrightRuntime || process.env.CI === 'true' || process.env.GITHUB_ACTIONS === 'true'
|
||||||
|
|
||||||
// Set COMMIT_HASH env var for use in the app
|
// Set COMMIT_HASH env var for use in the app
|
||||||
const version = process.env.npm_package_version || '0.0.0'
|
const version = process.env.npm_package_version || '0.0.0'
|
||||||
@@ -539,7 +540,7 @@ export default defineConfig(({ mode }) => {
|
|||||||
VueJsx(),
|
VueJsx(),
|
||||||
releaseEntryManifest(),
|
releaseEntryManifest(),
|
||||||
publicAssetAliases(),
|
publicAssetAliases(),
|
||||||
!isProd && !isPlaywrightRuntime && vueDevTools(),
|
!isProd && !isAutomationRuntime && vueDevTools(),
|
||||||
enableSingleFile && viteSingleFile(),
|
enableSingleFile && viteSingleFile(),
|
||||||
VitePWA({
|
VitePWA({
|
||||||
registerType: 'autoUpdate',
|
registerType: 'autoUpdate',
|
||||||
|
|||||||
Reference in New Issue
Block a user