Merge remote-tracking branch 'origin/master'

# Conflicts:
#	src/features/edgeGateways/EdgeGatewayManager.vue
This commit is contained in:
Jeppe Bundgaard
2026-04-23 09:01:11 +02:00
10 changed files with 1380 additions and 1655 deletions
+38 -13
View File
@@ -59,24 +59,49 @@ const normalizePositiveInteger = (value) => {
return Number.isInteger(parsedValue) && parsedValue > 0 ? parsedValue : null;
};
const getStoredSessionSnapshot = () => {
if (typeof window === "undefined") {
return null;
}
try {
const token = window.localStorage.getItem("token");
if (token === null) {
return null;
}
return {
token,
isSubuser: window.localStorage.getItem("is_subuser") === "true",
selectedCustomerNumber: normalizePositiveInteger(window.localStorage.getItem("selected_customer_number")),
};
} catch {
return null;
}
};
const hydrateSessionFromStorage = () => {
const snapshot = getStoredSessionSnapshot();
if (!snapshot) {
return false;
}
SessionUser.token.value = snapshot.token;
SessionUser.authenticated.value = true;
SessionUser.isSubuser.value = snapshot.isSubuser;
SessionUser.subuser.selectedGrantCustomerNumber.value = snapshot.selectedCustomerNumber;
return true;
};
/**
* Initiate the user session on app start
* @returns {Promise<void>}
*/
export const initiateOnAppStart = async () => {
// Check if the user has a token stored in local storage
if (localStorage.getItem("token") !== null) {
// Set the token and authenticated status
SessionUser.token.value = localStorage.getItem("token");
SessionUser.authenticated.value = true;
// Check if this is a subuser session
if (localStorage.getItem("is_subuser") === "true") {
SessionUser.isSubuser.value = true;
// Fetch the subuser's session data
if (hydrateSessionFromStorage()) {
if (SessionUser.isSubuser.value) {
await getSubuserSessionData();
} else {
// Fetch the user's session data
await getSessionData();
}
}
@@ -460,7 +485,7 @@ export const SessionUser = {
return true; // All users can access guest features
},
hasToken: () => {
return SessionUser.token.value !== null;
return SessionUser.token.value !== null || hydrateSessionFromStorage();
},
/** Shortcuts for the user's group */
superUser: SuperUserObject,
@@ -690,7 +715,7 @@ export const SessionUser = {
},
/** Check if the user has a token */
hasToken: () => {
return SessionUser.token.value !== null;
return SessionUser.token.value !== null || hydrateSessionFromStorage();
},
ucFirst: (str) => {
return str.charAt(0).toUpperCase() + str.slice(1);
File diff suppressed because it is too large Load Diff
@@ -232,7 +232,22 @@ const getStripeHostedInvoiceStatusKey = () => {
};
const getStripeHostedInvoiceStatusLabel = () => {
return t(`admin.pos.stripe.email.states.${getStripeHostedInvoiceStatusKey()}`);
switch (getStripeHostedInvoiceStatusKey()) {
case 'open':
return t('admin.pos.stripe.email.states.open');
case 'paid':
return t('admin.pos.stripe.email.states.paid');
case 'void':
return t('admin.pos.stripe.email.states.void');
case 'uncollectible':
return t('admin.pos.stripe.email.states.uncollectible');
case 'deleted':
return t('admin.pos.stripe.email.states.deleted');
case 'draft':
return t('admin.pos.stripe.email.states.draft');
default:
return t('admin.pos.stripe.email.states.unknown');
}
};
const getStripeHostedInvoiceStatusToneClass = () => {
@@ -486,6 +486,10 @@ test.describe("Economic queue async export workflow", () => {
});
await openHarness(page);
await page.evaluate(async () => {
const draftCustomerModule = await import("/src/composables/useDraftTransactionCustomer.js");
draftCustomerModule.setDraftTransactionCustomerNumber(6001);
});
await expect(page.getByTestId("collected-economic-draft-customer-blocked")).toBeVisible();
await expect(page.getByTestId("collected-economic-flow")).toHaveCount(0);
+18 -1
View File
@@ -19,6 +19,7 @@ const CONNECTIVITY_ISSUE_PATTERN = /Forbindelsesproblem|Connection issue/i;
const MOCK_USER_TOKEN = "e2e-user-session-token";
const MOCK_SUBUSER_TOKEN = "e2e-subuser-session-token";
const MOCK_OPERATOR_TOKEN = "e2e-operator-session-token";
const LOCAL_PLAYWRIGHT_HOSTS = new Set(["127.0.0.1", "localhost", "0.0.0.0", "::1"]);
const isBenignNavigationError = (error: unknown) => {
const message = error instanceof Error ? error.message : String(error);
@@ -49,6 +50,19 @@ async function hasConnectivityIssue(page: Page) {
.catch(() => false);
}
function shouldInstallMockAuthRoutes() {
const configuredBaseUrl = process.env.PLAYWRIGHT_BASE_URL;
if (!configuredBaseUrl) {
return true;
}
try {
return LOCAL_PLAYWRIGHT_HOSTS.has(new URL(configuredBaseUrl).hostname);
} catch {
return true;
}
}
function createUserSessionData(overrides: Record<string, unknown> = {}) {
return {
id: 1,
@@ -317,12 +331,15 @@ async function ensureAuthPageReady(
selector: string,
installMockRoutes: () => Promise<void>
) {
if (shouldInstallMockAuthRoutes()) {
await installMockRoutes();
}
await navigateTo(page, targetPath);
if (await waitForStableAuthField(page, selector)) {
return;
}
await installMockRoutes();
await navigateTo(page, targetPath);
await ensureAuthFieldVisible(page, targetPath, selector);
}
+2 -2
View File
@@ -3,8 +3,8 @@ import { userCredentials, loginAsSubuserByPhone } from "./fixtures";
// Navigate to profile page helper
async function goToUserProfile(page) {
await page.click('a[href="/user/profile"]');
await expect(page).toHaveURL("/user/profile");
await page.goto("/user/profile");
await expect(page).toHaveURL(/\/user\/profile(?:[?#].*)?$/);
}
// Subuser contact information display tests
+2 -2
View File
@@ -3,8 +3,8 @@ import { userCredentials, loginAsSubuserByUsername } from "./fixtures";
// Navigate to profile page helper
async function goToUserProfile(page) {
await page.click('a[href="/user/profile"]');
await expect(page).toHaveURL("/user/profile");
await page.goto("/user/profile");
await expect(page).toHaveURL(/\/user\/profile(?:[?#].*)?$/);
}
// Subuser grant selection display tests
+2 -2
View File
@@ -3,8 +3,8 @@ import { userCredentials, loginAsSubuserByUsername } from "./fixtures";
// Navigate to profile page helper
async function goToUserProfile(page) {
await page.click('a[href="/user/profile"]');
await expect(page).toHaveURL("/user/profile");
await page.goto("/user/profile");
await expect(page).toHaveURL(/\/user\/profile(?:[?#].*)?$/);
}
/** User Profile Tests - Subuser via Username Login */
+2 -2
View File
@@ -3,8 +3,8 @@ import { userCredentials, loginAsUser } from "./fixtures";
// Navigate to profile page helper
async function goToUserProfile(page) {
await page.click('a[href="/user/profile"]');
await expect(page).toHaveURL("/user/profile");
await page.goto("/user/profile");
await expect(page).toHaveURL(/\/user\/profile(?:[?#].*)?$/);
}
// Helper to expand a category section by clicking its header
+2 -2
View File
@@ -3,8 +3,8 @@ import { userCredentials, loginAsUser, loginAsSubuserByPhone } from "./fixtures"
// Navigate to profile page helper
async function goToUserProfile(page) {
await page.click('a[href="/user/profile"]');
await expect(page).toHaveURL("/user/profile");
await page.goto("/user/profile");
await expect(page).toHaveURL(/\/user\/profile(?:[?#].*)?$/);
}
/** User Profile Tests - Section Visibility */