Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f05d950227 | ||
|
|
301332a538 |
@@ -1,66 +0,0 @@
|
||||
name: Qodana
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
branches: [master, beta, canary, internal]
|
||||
types: [opened, synchronize, reopened, ready_for_review]
|
||||
push:
|
||||
branches: [master, beta, canary, internal]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
checks: write
|
||||
pull-requests: write
|
||||
|
||||
concurrency:
|
||||
group: qodana-${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
qodana:
|
||||
name: Qodana
|
||||
if: >-
|
||||
github.event_name != 'pull_request' ||
|
||||
(
|
||||
github.event.pull_request.draft == false &&
|
||||
github.event.pull_request.head.repo.full_name == github.repository &&
|
||||
github.event.pull_request.user.login != 'dependabot[bot]'
|
||||
)
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 60
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
# v5.0.1
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: Require Qodana project token
|
||||
shell: bash
|
||||
env:
|
||||
QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [[ -z "${QODANA_TOKEN:-}" ]]; then
|
||||
echo "::error::QODANA_TOKEN is not configured for this repository."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Qodana
|
||||
# v2026.1.3
|
||||
uses: JetBrains/qodana-action@b588768b6e7e6da579e518bc584f79de0d243692
|
||||
with:
|
||||
use-caches: true
|
||||
cache-default-branch-only: true
|
||||
upload-result: false
|
||||
use-annotations: true
|
||||
pr-mode: ${{ github.event_name == 'pull_request' }}
|
||||
post-pr-comment: true
|
||||
github-token: ${{ github.token }}
|
||||
push-fixes: none
|
||||
env:
|
||||
QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
|
||||
@@ -2143,6 +2143,7 @@
|
||||
"customer_login_title": "@:{'templates.generated.compat.global.login'}",
|
||||
"driver_entry_intro": "@.capitalize:{'words.generated.er'} @:{'words.generated.du'} @:{'words.generated.chauffør'}? @.capitalize:{'words.generated.log'} @:{'words.generated.ind'} @:{'words.generated.her'} @:{'words.generated.for'} @:{'words.generated.at'} @:{'words.generated.fa'} @:{'words.generated.adgang'} @:{'words.generated.til'} @:{'words.generated.dine'} @:{'words.generated.vaske'}.",
|
||||
"driver_login_button": "@.capitalize:{'words.generated.log'} @:{'words.generated.ind'} @.lower:{'words.generated.som'} @:{'words.generated.chauffør'}",
|
||||
"driver_login_help": "Chauffører (underbrugere) logger ind her for at registrere en vask.",
|
||||
"driver_login_title": "@.capitalize:{'words.generated.chauffør'} @.capitalize:{'words.generated.login'}",
|
||||
"forgot_password": "Glemt @:{'words.generated.adgangskode'}",
|
||||
"operator_tagline": "@.capitalize:{'words.generated.truckwash'}.@:{'words.generated.io'} – @:{'words.generated.digitale'} @:{'words.generated.løsninger'} @:{'words.generated.til'} @:{'words.generated.lastbilvask'}",
|
||||
|
||||
@@ -2260,6 +2260,7 @@
|
||||
"username": "@:{'words.generated.benutzername'}",
|
||||
"your_username": "@:{'words.generated.ihr'} @:{'words.generated.benutzername'}",
|
||||
"driver_login_button": "Fahrer-Login",
|
||||
"driver_login_help": "Fahrer (Unterbenutzer) melden sich hier an, um eine Wäsche zu registrieren.",
|
||||
"driver_entry_intro": "Sind Sie Fahrer? Melden Sie sich hier an, um eine Wäsche zu registrieren."
|
||||
},
|
||||
"book_wash": {
|
||||
|
||||
@@ -1974,6 +1974,7 @@
|
||||
"customer_login_title": "@.capitalize:{'words.generated.customer'} @.capitalize:{'words.generated.login'}",
|
||||
"driver_entry_intro": "@.capitalize:{'words.generated.are'} @:{'words.generated.you'} @:{'words.generated.a'} @:{'words.generated.driver'}? @.capitalize:{'words.generated.log'} @:{'words.generated.in'} @:{'words.generated.here'} @:{'words.generated.to'} @:{'words.generated.register'} @:{'words.generated.a'} @:{'words.generated.wash'}.",
|
||||
"driver_login_button": "@.capitalize:{'words.generated.driver'} @.capitalize:{'words.generated.login'}",
|
||||
"driver_login_help": "Drivers (sub-users) log in here to register a wash.",
|
||||
"driver_login_title": "@.capitalize:{'words.generated.driver'} @.capitalize:{'words.generated.login'}",
|
||||
"forgot_password": "Forgot @.capitalize:{'words.generated.password'}",
|
||||
"operator_tagline": "@.capitalize:{'words.generated.truckwash'}.@:{'words.generated.io'} - @:{'words.generated.digital'} @:{'words.generated.solutions'} @:{'words.generated.for'} @:{'words.generated.truck'} @:{'words.generated.washing'}",
|
||||
|
||||
@@ -786,6 +786,7 @@
|
||||
"customer_number": "@:{'templates.generated.compat.objects.columns.customer_number'}",
|
||||
"driver_entry_intro": "@:{'templates.generated.compat.auth.driver_entry_intro'}",
|
||||
"driver_login_button": "@:{'templates.generated.compat.auth.driver_login_button'}",
|
||||
"driver_login_help": "@:{'templates.generated.compat.auth.driver_login_help'}",
|
||||
"driver_login_title": "@:{'templates.generated.compat.auth.driver_login_title'}",
|
||||
"forgot_password": "@:{'templates.generated.compat.auth.forgot_password'}",
|
||||
"login_as_customer": "@:{'templates.generated.compat.global.login_as_customer'}",
|
||||
|
||||
@@ -2263,6 +2263,7 @@
|
||||
"username": "@.capitalize:{'words.generated.brukernavn'}",
|
||||
"your_username": "@.capitalize:{'words.generated.ditt'} @:{'words.generated.brukernavn'}",
|
||||
"driver_login_button": "Sjåfør-innlogging",
|
||||
"driver_login_help": "Sjåfører (underbrukere) logger inn her for å registrere en vask.",
|
||||
"driver_entry_intro": "Er du sjåfør? Logg inn her for å registrere en vask."
|
||||
},
|
||||
"book_wash": {
|
||||
|
||||
@@ -2313,6 +2313,7 @@
|
||||
"username": "@.capitalize:{'words.generated.anvandarnamn'}",
|
||||
"your_username": "@.capitalize:{'words.generated.ditt'} @:{'words.generated.anvandarnamn'}",
|
||||
"driver_login_button": "Förarinloggning",
|
||||
"driver_login_help": "Förare (underanvändare) loggar in här för att registrera en tvätt.",
|
||||
"driver_entry_intro": "Är du förare? Logga in här för att registrera en tvätt."
|
||||
},
|
||||
"book_wash": {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"customer_login_title": "@:{'phrases.compat.global.login'}",
|
||||
"driver_entry_intro": "@.capitalize:{'terms.glossary.er'} @:{'terms.glossary.du'} @:{'terms.glossary.chauffør'}? @.capitalize:{'terms.glossary.log'} @:{'terms.glossary.ind'} @:{'terms.glossary.her'} @:{'terms.glossary.for'} @:{'terms.glossary.at'} @:{'terms.glossary.fa'} @:{'terms.glossary.adgang'} @:{'terms.glossary.til'} @:{'terms.glossary.dine'} @:{'terms.glossary.vaske'}.",
|
||||
"driver_login_button": "@.capitalize:{'terms.glossary.log'} @:{'terms.glossary.ind'} @.lower:{'terms.glossary.som'} @:{'terms.glossary.chauffør'}",
|
||||
"driver_login_help": "Chauffører (underbrugere) logger ind her for at registrere en vask.",
|
||||
"driver_login_title": "@.capitalize:{'terms.glossary.chauffør'} @.capitalize:{'terms.glossary.login'}",
|
||||
"forgot_password": "Glemt @:{'terms.glossary.adgangskode'}",
|
||||
"operator_tagline": "@.capitalize:{'terms.glossary.truckwash'}.@:{'terms.glossary.io'} – @:{'terms.glossary.digitale'} @:{'terms.glossary.løsninger'} @:{'terms.glossary.til'} @:{'terms.glossary.lastbilvask'}",
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
"username": "@:{'terms.glossary.benutzername'}",
|
||||
"your_username": "@:{'terms.glossary.ihr'} @:{'terms.glossary.benutzername'}",
|
||||
"driver_login_button": "Fahrer-Login",
|
||||
"driver_login_help": "Fahrer (Unterbenutzer) melden sich hier an, um eine Wäsche zu registrieren.",
|
||||
"driver_entry_intro": "Sind Sie Fahrer? Melden Sie sich hier an, um eine Wäsche zu registrieren."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"customer_login_title": "@.capitalize:{'terms.glossary.customer'} @.capitalize:{'terms.glossary.login'}",
|
||||
"driver_entry_intro": "@.capitalize:{'terms.glossary.are'} @:{'terms.glossary.you'} @:{'terms.glossary.a'} @:{'terms.glossary.driver'}? @.capitalize:{'terms.glossary.log'} @:{'terms.glossary.in'} @:{'terms.glossary.here'} @:{'terms.glossary.to'} @:{'terms.glossary.register'} @:{'terms.glossary.a'} @:{'terms.glossary.wash'}.",
|
||||
"driver_login_button": "@.capitalize:{'terms.glossary.driver'} @.capitalize:{'terms.glossary.login'}",
|
||||
"driver_login_help": "Drivers (sub-users) log in here to register a wash.",
|
||||
"driver_login_title": "@.capitalize:{'terms.glossary.driver'} @.capitalize:{'terms.glossary.login'}",
|
||||
"forgot_password": "Forgot @.capitalize:{'terms.glossary.password'}",
|
||||
"operator_tagline": "@.capitalize:{'terms.glossary.truckwash'}.@:{'terms.glossary.io'} - @:{'terms.glossary.digital'} @:{'terms.glossary.solutions'} @:{'terms.glossary.for'} @:{'terms.glossary.truck'} @:{'terms.glossary.washing'}",
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"customer_number": "@:{'phrases.compat.objects.columns.customer_number'}",
|
||||
"driver_entry_intro": "@:{'phrases.compat.auth.driver_entry_intro'}",
|
||||
"driver_login_button": "@:{'phrases.compat.auth.driver_login_button'}",
|
||||
"driver_login_help": "@:{'phrases.compat.auth.driver_login_help'}",
|
||||
"driver_login_title": "@:{'phrases.compat.auth.driver_login_title'}",
|
||||
"forgot_password": "@:{'phrases.compat.auth.forgot_password'}",
|
||||
"login_as_customer": "@:{'phrases.compat.global.login_as_customer'}",
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
"username": "@.capitalize:{'terms.glossary.brukernavn'}",
|
||||
"your_username": "@.capitalize:{'terms.glossary.ditt'} @:{'terms.glossary.brukernavn'}",
|
||||
"driver_login_button": "Sjåfør-innlogging",
|
||||
"driver_login_help": "Sjåfører (underbrukere) logger inn her for å registrere en vask.",
|
||||
"driver_entry_intro": "Er du sjåfør? Logg inn her for å registrere en vask."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
"username": "@.capitalize:{'terms.glossary.anvandarnamn'}",
|
||||
"your_username": "@.capitalize:{'terms.glossary.ditt'} @:{'terms.glossary.anvandarnamn'}",
|
||||
"driver_login_button": "Förarinloggning",
|
||||
"driver_login_help": "Förare (underanvändare) loggar in här för att registrera en tvätt.",
|
||||
"driver_entry_intro": "Är du förare? Logga in här för att registrera en tvätt."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,23 @@ const { hasPendingStoredSession } = useStoredSessionGuestRedirect();
|
||||
<LoginForm />
|
||||
</ConnectivityIssue>
|
||||
<p>{{ $t('auth.terms_acceptance') }}</p>
|
||||
|
||||
<!-- Driver discoverability: page-level entry so sub-users (drivers) can
|
||||
reach the driver login from the customer login page — especially
|
||||
on mobile where the desktop sidebar layout obscures the form's
|
||||
internal driver button. -->
|
||||
<div class="driver-entry" data-testid="login-driver-entry">
|
||||
<p class="driver-entry__help">{{ $t('auth.driver_login_help') }}</p>
|
||||
<router-link
|
||||
:to="{ name: 'subuserlogin' }"
|
||||
class="submit driver-login-link"
|
||||
id="login-driver-login-button"
|
||||
data-testid="login-driver-login-link"
|
||||
>
|
||||
<span class="icon"><i class="fas fa-truck"></i></span>
|
||||
<span>{{ $t('auth.driver_login_button') }}</span>
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -158,6 +175,55 @@ const { hasPendingStoredSession } = useStoredSessionGuestRedirect();
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* Driver entry block — page-level, always visible on every viewport.
|
||||
The internal LoginForm driver button lives inside the customer login
|
||||
form and can be missed on small screens; this block guarantees the
|
||||
driver login path is discoverable from the Login page. */
|
||||
.login-section .wrapper .main .driver-entry {
|
||||
max-width: 400px;
|
||||
width: 100%;
|
||||
margin-top: 24px;
|
||||
padding: 16px 20px;
|
||||
background-color: #F2F8FC;
|
||||
border: 1px solid #BFE0EF;
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.login-section .wrapper .main .driver-entry__help {
|
||||
max-width: none;
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
color: #13324C;
|
||||
text-align: center;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.login-section .wrapper .main .driver-entry .driver-login-link {
|
||||
background-color: #1584BC;
|
||||
color: #fff;
|
||||
border: 0;
|
||||
border-radius: 30px;
|
||||
min-height: 43px;
|
||||
font-size: 16px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
text-decoration: none;
|
||||
margin: 0;
|
||||
width: auto;
|
||||
padding: 0 24px;
|
||||
}
|
||||
|
||||
.login-section .wrapper .main .driver-entry .driver-login-link:hover {
|
||||
background-color: #1a99d6;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
@media (max-width: 1250px) {
|
||||
.login-section .wrapper .sidebar h1{
|
||||
font-size: 2.3em;
|
||||
@@ -187,5 +253,20 @@ const { hasPendingStoredSession } = useStoredSessionGuestRedirect();
|
||||
padding: 70px 10px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Mobile (<768px): ensure the driver entry block is always visible and
|
||||
takes full width so it's impossible to miss on phones. */
|
||||
@media (max-width: 767px) {
|
||||
.login-section .wrapper .main .driver-entry {
|
||||
margin-top: 20px;
|
||||
padding: 14px 16px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.login-section .wrapper .main .driver-entry .driver-login-link {
|
||||
width: 100%;
|
||||
padding: 0 16px;
|
||||
}
|
||||
}
|
||||
/*end login-section*/
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
// @vitest-environment jsdom
|
||||
|
||||
import { defineComponent, h, nextTick } from "vue";
|
||||
import { mount } from "@vue/test-utils";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
hasStoredSessionToken: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("@/services/sessionStorage.js", () => ({
|
||||
hasStoredSessionToken: mocks.hasStoredSessionToken,
|
||||
}));
|
||||
|
||||
vi.mock("@/composables/useStoredSessionGuestRedirect.js", () => ({
|
||||
useStoredSessionGuestRedirect: () => ({
|
||||
hasPendingStoredSession: mocks.hasStoredSessionToken(),
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("@/components/forms/auth/LoginForm.vue", () => ({
|
||||
default: defineComponent({
|
||||
name: "MockLoginForm",
|
||||
setup() {
|
||||
return () => h("form", { "data-testid": "login-form" });
|
||||
},
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("@/views/errors/ConnectivityIssue.vue", () => ({
|
||||
default: defineComponent({
|
||||
name: "MockConnectivityIssue",
|
||||
setup(_, { slots }) {
|
||||
return () => h("div", { "data-testid": "connectivity-wrapper" }, slots.default?.());
|
||||
},
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("@/components/global/BrandedLoadingScreen.vue", () => ({
|
||||
default: defineComponent({
|
||||
name: "MockBrandedLoadingScreen",
|
||||
props: {
|
||||
testId: { type: String, default: "branded-loading-screen" },
|
||||
},
|
||||
setup(props) {
|
||||
return () => h("div", { "data-testid": props.testId });
|
||||
},
|
||||
}),
|
||||
}));
|
||||
|
||||
import Login from "@/views/auth/Login.vue";
|
||||
|
||||
const routerLinkStub = defineComponent({
|
||||
name: "RouterLinkStub",
|
||||
props: {
|
||||
to: {
|
||||
type: [String, Object],
|
||||
default: "",
|
||||
},
|
||||
},
|
||||
setup(_, { slots, attrs }) {
|
||||
return () => h("a", attrs, slots.default?.());
|
||||
},
|
||||
});
|
||||
|
||||
const mountLogin = () =>
|
||||
mount(Login, {
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => key,
|
||||
},
|
||||
stubs: {
|
||||
RouterLink: routerLinkStub,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
describe("Login.vue — driver entry block (TRU-61)", () => {
|
||||
beforeEach(() => {
|
||||
mocks.hasStoredSessionToken.mockReset();
|
||||
mocks.hasStoredSessionToken.mockReturnValue(false);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
document.body.innerHTML = "";
|
||||
});
|
||||
|
||||
it("renders the page-level driver entry block on the customer login page", async () => {
|
||||
const wrapper = mountLogin();
|
||||
await nextTick();
|
||||
|
||||
const entry = wrapper.find('[data-testid="login-driver-entry"]');
|
||||
expect(entry.exists()).toBe(true);
|
||||
});
|
||||
|
||||
it("renders a router-link to the subuser login route inside the driver entry block", async () => {
|
||||
const wrapper = mountLogin();
|
||||
await nextTick();
|
||||
|
||||
const link = wrapper.find('[data-testid="login-driver-login-link"]');
|
||||
expect(link.exists()).toBe(true);
|
||||
});
|
||||
|
||||
it("renders the driver login button with the localized driver_login_button label", async () => {
|
||||
const wrapper = mountLogin();
|
||||
await nextTick();
|
||||
|
||||
const link = wrapper.find('[data-testid="login-driver-login-link"]');
|
||||
expect(link.text()).toContain("auth.driver_login_button");
|
||||
});
|
||||
|
||||
it("renders the driver login help text inside the entry block", async () => {
|
||||
const wrapper = mountLogin();
|
||||
await nextTick();
|
||||
|
||||
const entry = wrapper.find('[data-testid="login-driver-entry"]');
|
||||
expect(entry.text()).toContain("auth.driver_login_help");
|
||||
});
|
||||
|
||||
it("places the driver entry block in the main content area (not the sidebar)", async () => {
|
||||
const wrapper = mountLogin();
|
||||
await nextTick();
|
||||
|
||||
// The sidebar should NOT contain the driver entry — it must be in main
|
||||
const html = wrapper.html();
|
||||
const entryHtml = wrapper.find('[data-testid="login-driver-entry"]').html();
|
||||
// Find the sidebar's outer container and ensure the entry is not inside it
|
||||
const sidebarMatch = html.match(/<div class="sidebar">[\s\S]*?<\/div>\s*<div class="main">/);
|
||||
if (sidebarMatch) {
|
||||
expect(sidebarMatch[0]).not.toContain("login-driver-entry");
|
||||
}
|
||||
expect(entryHtml).toBeTruthy();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user