diff --git a/packages/ui/src/components/calendar/internal/DateUtils.ts b/packages/ui/src/components/calendar/internal/DateUtils.ts index 915c544abf..f89b7228c7 100644 --- a/packages/ui/src/components/calendar/internal/DateUtils.ts +++ b/packages/ui/src/components/calendar/internal/DateUtils.ts @@ -160,11 +160,20 @@ export function getFormattedDate (value: number | null, options?: Intl.DateTimeF } export function formatDate (date: Date, showYear: boolean): string { - const locale = new Intl.NumberFormat().resolvedOptions().locale + // Intl.DateTimeFormat.format throws RangeError for an invalid date, which breaks rendering + // of the whole component tree, so never let it propagate. + if (!(date instanceof Date) || Number.isNaN(date.getTime())) { + return '' + } const options: Intl.DateTimeFormatOptions = showYear ? { year: 'numeric', month: 'short', day: 'numeric' } : { month: 'short', day: 'numeric' } - return new Intl.DateTimeFormat(locale, options).format(date) + try { + const locale = new Intl.NumberFormat().resolvedOptions().locale + return new Intl.DateTimeFormat(locale, options).format(date) + } catch (err: any) { + return date.toLocaleDateString('default', options) + } } export const getTimeZoneName = ( diff --git a/plugins/login-resources/src/components/Form.svelte b/plugins/login-resources/src/components/Form.svelte index 492e86c5a3..9ea6a076a5 100644 --- a/plugins/login-resources/src/components/Form.svelte +++ b/plugins/login-resources/src/components/Form.svelte @@ -96,7 +96,12 @@ void validate($themeStore.language) } - $: if ($themeStore.language != null && $themeStore.language !== '') { + // Re-validate only when the language actually changes (to re-translate validation messages). + // Theme updates with the same language (e.g. after plugin strings are loaded) must not + // reset the status, otherwise an action error (like wrong credentials) could be wiped out. + let validatedLanguage: string | undefined + $: if ($themeStore.language != null && $themeStore.language !== '' && $themeStore.language !== validatedLanguage) { + validatedLanguage = $themeStore.language void validate($themeStore.language) } diff --git a/tests/sanity/tests/model/recruiting/common-recruiting-page.ts b/tests/sanity/tests/model/recruiting/common-recruiting-page.ts index 01201a85e0..d2b47f9097 100644 --- a/tests/sanity/tests/model/recruiting/common-recruiting-page.ts +++ b/tests/sanity/tests/model/recruiting/common-recruiting-page.ts @@ -67,11 +67,11 @@ export class CommonRecruitingPage extends CalendarPage { readonly chenRosamundPopupButton = (): Locator => this.page.locator('.popup button:has-text("Chen Rosamund")') async clickOnTitle (): Promise { - await this.title().click() + await this.newReviewTitle().click() } async fillTitle (title: string): Promise { - await this.title().fill(title) + await this.newReviewTitle().fill(title) } async clickAppleseedJohn (): Promise { @@ -90,13 +90,16 @@ export class CommonRecruitingPage extends CalendarPage { await this.talentButton().click() } - async createApplication (): Promise { + async createReview (): Promise { await this.createButton().click() await this.page.waitForSelector('form.antiCard', { state: 'detached' }) + await this.page.reload({ waitUntil: 'domcontentloaded' }) } async selectReviewItem (reviewId: string): Promise { - await this.reviewItemLink(reviewId).first().click() + const link = this.reviewItemLink(reviewId).first() + await expect(link).toBeVisible() + await Promise.all([this.page.waitForURL(/\/recruit\/RVE-/), link.click()]) } async clickTwoMembers (): Promise { @@ -136,6 +139,7 @@ export class CommonRecruitingPage extends CalendarPage { await this.newReviewTitle().fill(title) await this.newReviewDescription().fill(description) await this.submitNewReview().click() + await this.submitNewReview().waitFor({ state: 'hidden' }) } async deleteEntity (): Promise { diff --git a/tests/sanity/tests/model/recruiting/recruiting-page.ts b/tests/sanity/tests/model/recruiting/recruiting-page.ts index 26fb405104..6b042e9817 100644 --- a/tests/sanity/tests/model/recruiting/recruiting-page.ts +++ b/tests/sanity/tests/model/recruiting/recruiting-page.ts @@ -11,6 +11,8 @@ export class RecruitingPage { talentsNavElement = (): Locator => this.page.locator('.antiPanel-navigator').locator('text=Talents') reviews = (): Locator => this.page.locator('.antiPanel-navigator').locator('text=Reviews') reviewButton = (): Locator => this.page.getByRole('button', { name: 'Review', exact: true }) + reviewForm = (): Locator => this.page.locator('form[id="recruit:string:CreateReviewParams"]') + reviewsTotal = (): Locator => this.page.getByText(/^Total: \d+$/) frontendEngineerOption = (): Locator => this.page.locator('td:has-text("Frontend Engineer")') searchOrRunCommandInput = (): Locator => this.page.locator('[placeholder="Search\\ or\\ run\\ a\\ command\\.\\.\\."]') @@ -44,10 +46,12 @@ export class RecruitingPage { async clickOnReviews (): Promise { await this.reviews().click() + await expect(this.reviewsTotal()).toBeVisible() } async clickOnReviewButton (): Promise { await this.reviewButton().click() + await expect(this.reviewForm()).toBeVisible() } async clickTalentsNavElement (): Promise { diff --git a/tests/sanity/tests/model/recruiting/talent-details-page.ts b/tests/sanity/tests/model/recruiting/talent-details-page.ts index fdfd5bef85..37d13563b7 100644 --- a/tests/sanity/tests/model/recruiting/talent-details-page.ts +++ b/tests/sanity/tests/model/recruiting/talent-details-page.ts @@ -33,8 +33,18 @@ export class TalentDetailsPage extends CommonRecruitingPage { readonly titleAndSourceTalent = (title: string): Locator => this.page.locator('button > span', { hasText: title }) async addSkill (skillTag: string, skillDescription: string): Promise { - await this.buttonAddSkill().click() - await this.pressCreateButtonSelectPopup(this.page) + const tagTitle = this.tagsStringAddTagForm('title') + await expect(async () => { + if (!(await tagTitle.isVisible())) { + if (!(await this.selectPopupAddButton().isVisible())) { + await this.buttonAddSkill().click({ timeout: 3000 }) + } + await this.selectPopupAddButton().click({ timeout: 3000 }) + } + await expect(tagTitle).toBeVisible({ timeout: 3000 }) + await this.page.waitForTimeout(300) + await expect(tagTitle).toBeVisible({ timeout: 1000 }) + }).toPass({ timeout: 20000 }) await this.addNewTagPopup(this.page, skillTag, skillDescription) await this.pressShowAllButtonSelectPopup(this.page) diff --git a/tests/sanity/tests/recruiting/reviews.spec.ts b/tests/sanity/tests/recruiting/reviews.spec.ts index 3e5e70ce63..a3962d66d8 100644 --- a/tests/sanity/tests/recruiting/reviews.spec.ts +++ b/tests/sanity/tests/recruiting/reviews.spec.ts @@ -30,7 +30,7 @@ test.describe('review tests', () => { await commonRecruitingPage.clickTalent() // Click button:has-text("Chen Rosamund") await commonRecruitingPage.clickChenRosamund() - await commonRecruitingPage.createApplication() + await commonRecruitingPage.createReview() await commonRecruitingPage.selectReviewItem(reviewId) await commonRecruitingPage.clickTwoMembers() await commonRecruitingPage.clickAppleseedJohn()