Compare commits

...

50 Commits

Author SHA1 Message Date
cesnimda 4ff80391f4 test(documents): record canonical profile capture interleavings 2026-11-11 18:32:10 +01:00
cesnimda b9c0b61868 fix(documents): reject profile changes during submitted CV rendering 2026-11-11 18:31:58 +01:00
cesnimda 444d5c89a2 test(documents): preserve revision race and legacy capture evidence 2026-11-11 18:20:38 +01:00
cesnimda e161ea3346 fix(documents): bind submitted CV captures to their saved revision 2026-11-11 18:17:08 +01:00
cesnimda 9f341750ec docs(ui): record Settings preservation and discrepancy coverage 2026-11-11 15:22:06 +01:00
cesnimda a8bf49e084 test(settings): qualify Rules density and recovery across devices 2026-11-11 15:20:57 +01:00
cesnimda 99b144f482 style(settings): use full rules form width without spacer rows 2026-11-11 15:17:36 +01:00
cesnimda 511b68a19d test(i18n): retain import and conversion count evidence 2026-11-11 15:11:21 +01:00
cesnimda 44112c7cbb fix(i18n): distinguish singular import and conversion feedback 2026-11-11 15:10:22 +01:00
cesnimda 50e16ff2e1 test(settings): qualify native export bytes and session lifetime 2026-11-11 15:02:59 +01:00
cesnimda 2a02e1023e fix(settings): bind job exports and CSV conversion to their session 2026-11-11 15:02:06 +01:00
cesnimda 214eb25fa5 docs(ui): reconcile correspondence route and discrepancy coverage 2026-11-11 14:19:18 +01:00
cesnimda e3ea2ef471 test(ui): qualify open correspondence surfaces across widths 2026-11-11 14:17:51 +01:00
cesnimda c637587648 style(correspondence): flatten history and linked thread surfaces 2026-11-11 14:14:58 +01:00
cesnimda 72be614496 test(correspondence): qualify pasted email recovery states 2026-11-11 14:10:33 +01:00
cesnimda 5f61d8f72a fix(correspondence): protect pasted email recording and recovery 2026-11-11 14:08:57 +01:00
cesnimda 1f3bdbcd31 test(correspondence): retain recovery and accessibility evidence 2026-11-11 14:02:11 +01:00
cesnimda c751b4272e fix(correspondence): preserve current reads and note recovery 2026-11-11 14:00:44 +01:00
cesnimda b9bb3c44de docs(ui): track widget and unapplied date discrepancies 2026-11-11 13:30:56 +01:00
cesnimda 291b1e521a docs(applications): distinguish UI date defect from backend safeguards 2026-11-11 13:30:04 +01:00
cesnimda e6240ad309 test(applications): qualify unapplied date preservation 2026-11-11 13:27:33 +01:00
cesnimda 138f1ee69c fix(applications): retain missing applied dates during editing 2026-11-11 13:25:44 +01:00
cesnimda 5a92ae3ba6 test(i18n): preserve shared widget locale verification 2026-11-11 13:22:33 +01:00
cesnimda cb9d039786 fix(i18n): localize shared MUI widget controls 2026-11-11 13:21:57 +01:00
cesnimda c172427fb2 test(applications): retain flat edit dialog visual evidence 2026-11-11 13:15:12 +01:00
cesnimda 552525dfcd test(applications): cover flat edit dialog at tablet width 2026-11-11 13:14:09 +01:00
cesnimda 7bfebe88bf style(applications): flatten edit dialog sections 2026-11-11 13:05:05 +01:00
cesnimda 89c553cb96 test(i18n): bind committed date evidence across line endings 2026-11-11 12:59:16 +01:00
cesnimda d2dbe851c0 test(i18n): preserve real date widget locale and timezone evidence 2026-11-11 12:57:40 +01:00
cesnimda 285d3c88c7 test(i18n): inspect localized mouse and touch calendars 2026-11-11 12:54:22 +01:00
cesnimda 3e55b3c5aa docs(perf): preserve failed settings save qualification 2026-11-11 12:51:43 +01:00
cesnimda 361e79ddf3 test(rules): verify failed save preserves committed owner settings 2026-11-11 12:51:43 +01:00
cesnimda beb5564223 fix(i18n): apply UI locale to real date picker controls 2026-11-11 12:48:23 +01:00
cesnimda d027754d15 test(a3): seal confirmation date and native discovery follow-up 2026-11-11 08:04:44 +01:00
cesnimda df8f4dbbd3 test(discovery): reconcile native journeys with filter drawer 2026-11-11 07:59:13 +01:00
cesnimda 5a880a6ac7 fix(i18n): initialize new applications on local today 2026-11-11 07:53:22 +01:00
cesnimda 9143927609 docs(i18n): distinguish raw receipt whitespace warning 2026-11-11 07:45:31 +01:00
cesnimda 265794e010 fix(i18n): preserve local calendar day in job date pickers 2026-11-11 07:45:31 +01:00
cesnimda df93bfb64e fix(dialog): recover initial focus from nested modal fallback 2026-11-11 07:44:38 +01:00
cesnimda ec57a24d77 test(a3): preserve saved search drawer regression evidence 2026-11-11 07:27:15 +01:00
cesnimda 07df0c688d test(discovery): retain saved search and tracker verifier flows with drawer 2026-11-11 07:26:24 +01:00
cesnimda 1d73367576 test(a3): seal integrated discovery drawer evidence 2026-11-11 07:14:44 +01:00
cesnimda 4f3541e5b8 test(discovery): verify drawer focus and retained search journeys 2026-11-11 07:12:49 +01:00
cesnimda 7e23a27463 test(applications): preserve server-backed density and action evidence 2026-11-11 07:07:15 +01:00
cesnimda a6ec225598 feat(discovery): move mobile filters into accessible drawer 2026-11-11 07:05:42 +01:00
cesnimda ce7e48665c fix(i18n): sort discovery titles using selected UI locale 2026-11-10 23:22:41 +01:00
cesnimda 6c75e97bdb docs(perf): preserve native rules freshness receipts 2026-11-10 23:22:41 +01:00
cesnimda e286be48ba fix(rules): read saved thresholds without stale cache 2026-11-10 23:22:40 +01:00
cesnimda 64603f5602 fix(applications): preserve mobile title space and selected link contrast 2026-11-10 23:22:40 +01:00
cesnimda aac0f3b3a9 docs(a3): reconcile application route coverage 2026-11-10 23:11:58 +01:00
896 changed files with 29371 additions and 186 deletions
@@ -0,0 +1,246 @@
using JobTrackerApi.Controllers;
using JobTrackerApi.Data;
using JobTrackerApi.Models;
using JobTrackerApi.Services;
using JobTrackerApi.Tests.TestSupport;
using System.Security.Claims;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Moq;
using Xunit;
namespace JobTrackerApi.Tests;
public sealed class RuleSettingsFreshnessTests
{
[Fact]
public async Task Failed_owner_save_preserves_committed_rules_and_a_later_success_refreshes_existing_readers()
{
await using var fixture = await Fixture.CreateAsync();
await using var readA = fixture.Context("owner-a");
await using var readB = fixture.Context("owner-b");
var ownerA = fixture.Controller(readA);
var ownerB = fixture.Controller(readB);
Assert.False(await FollowUp(ownerA, fixture.JobA, "list"));
Assert.False(await FollowUp(ownerB, fixture.JobB, "list"));
var failure = new SaveFailureInterceptor();
await using var write = fixture.Context("owner-a", failure);
await Assert.ThrowsAsync<DbUpdateException>(() => new RulesController(write).Update(Settings(1), default));
Assert.Equal(1, write.UserRuleSettings.Local.Single().AppliedFollowUpDays);
Assert.Equal(30, (await RulesEngine.GetSettings(write, default)).AppliedFollowUpDays);
Assert.False(await FollowUp(ownerA, fixture.JobA, "list"));
Assert.False(await FollowUp(ownerB, fixture.JobB, "list"));
failure.RejectWrites = false;
Assert.IsType<NoContentResult>(await new RulesController(write).Update(Settings(1), default));
Assert.True(await FollowUp(ownerA, fixture.JobA, "list"));
Assert.False(await FollowUp(ownerB, fixture.JobB, "list"));
}
private sealed class SaveFailureInterceptor : SaveChangesInterceptor
{
public bool RejectWrites { get; set; } = true;
public override ValueTask<InterceptionResult<int>> SavingChangesAsync(
DbContextEventData eventData, InterceptionResult<int> result, CancellationToken cancellationToken = default)
{
if (RejectWrites) throw new DbUpdateException("Synthetic settings storage failure");
return base.SavingChangesAsync(eventData, result, cancellationToken);
}
}
[Theory]
[InlineData("list")]
[InlineData("board")]
[InlineData("detail")]
[InlineData("reminders")]
[InlineData("readiness")]
public async Task Saved_owner_thresholds_are_visible_on_the_next_read(string surface)
{
await using var fixture = await Fixture.CreateAsync();
await using var read = fixture.Context("owner-a");
var controller = fixture.Controller(read);
Assert.False(await FollowUp(controller, fixture.JobA, surface));
await using (var write = fixture.Context("owner-a"))
Assert.IsType<NoContentResult>(await new RulesController(write).Update(Settings(1), default));
// Reuse the reader after the committed write: tracked entities must not mask persistence.
Assert.True(await FollowUp(controller, fixture.JobA, surface));
}
[Fact]
public async Task Saving_owner_a_preserves_owner_b_and_existing_readers_see_subsequent_writes()
{
await using var fixture = await Fixture.CreateAsync();
await using var a = fixture.Context("owner-a");
await using var b = fixture.Context("owner-b");
var ca = fixture.Controller(a);
var cb = fixture.Controller(b);
Assert.False(await FollowUp(ca, fixture.JobA, "list"));
Assert.False(await FollowUp(cb, fixture.JobB, "list"));
await using (var write = fixture.Context("owner-a"))
await new RulesController(write).Update(Settings(1), default);
Assert.True(await FollowUp(ca, fixture.JobA, "list"));
Assert.False(await FollowUp(cb, fixture.JobB, "list"));
// A later write in the opposite direction must also be visible through both old/new controllers.
await using (var write = fixture.Context("owner-a"))
await new RulesController(write).Update(Settings(30), default);
await using var fresh = fixture.Context("owner-a");
Assert.False(await FollowUp(fixture.Controller(fresh), fixture.JobA, "list"));
Assert.False(await FollowUp(ca, fixture.JobA, "list"));
}
[Fact]
public async Task Completing_an_older_request_cannot_restore_old_rules_after_a_save()
{
await using var fixture = await Fixture.CreateAsync();
await using var oldRead = fixture.Context("owner-a");
var reached = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
var release = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
var users = TestHostFactory.CreateUserManager();
users.Setup(x => x.FindByIdAsync("owner-a")).Returns(async () =>
{
// GetById has already loaded/evaluated the old rules; no model is called.
reached.TrySetResult(true);
await release.Task;
return (ApplicationUser?)null;
});
var older = new JobApplicationsController(oldRead, Mock.Of<ISummarizerService>(), users.Object)
{
ControllerContext = new ControllerContext { HttpContext = new DefaultHttpContext
{
User = new ClaimsPrincipal(new ClaimsIdentity(new[] { new Claim(ClaimTypes.NameIdentifier, "owner-a") }, "test")),
} },
};
var pending = FollowUp(older, fixture.JobA, "detail");
try
{
await reached.Task.WaitAsync(TimeSpan.FromSeconds(10));
await using (var write = fixture.Context("owner-a"))
await new RulesController(write).Update(Settings(1), default);
await using var freshRead = fixture.Context("owner-a");
var fresh = fixture.Controller(freshRead);
Assert.True(await FollowUp(fresh, fixture.JobA, "list"));
release.TrySetResult(true);
Assert.False(await pending); // A request begun before the save may return its earlier snapshot.
Assert.True(await FollowUp(fresh, fixture.JobA, "list"));
}
finally
{
release.TrySetResult(true);
await pending;
}
}
[Fact]
public async Task Global_save_refreshes_fallback_but_preserves_explicit_owner_override()
{
await using var fixture = await Fixture.CreateAsync();
await using (var setup = fixture.Context("owner-a"))
{
setup.UserRuleSettings.Remove(await setup.UserRuleSettings.SingleAsync());
await setup.SaveChangesAsync();
}
await using var fallback = fixture.Context("owner-a");
await using var overridden = fixture.Context("owner-b");
var ca = fixture.Controller(fallback);
var cb = fixture.Controller(overridden);
Assert.False(await FollowUp(ca, fixture.JobA, "list"));
Assert.False(await FollowUp(cb, fixture.JobB, "list"));
await using (var global = fixture.Context(null))
Assert.IsType<NoContentResult>(await new RulesController(global).Update(Settings(1), default));
Assert.True(await FollowUp(ca, fixture.JobA, "list"));
Assert.False(await FollowUp(cb, fixture.JobB, "list"));
}
private static RuleSettings Settings(int followUp) => new()
{
AppliedFollowUpDays = followUp, AppliedGhostDays = 365,
OfferFollowUpDays = 30, OfferGhostDays = 365,
FeedbackFollowUpDays = 30, FeedbackGhostDays = 365,
};
private static async Task<bool> FollowUp(JobApplicationsController controller, int id, string surface)
{
if (surface == "readiness")
{
var result = await controller.GetReadiness(id, default);
return Assert.IsType<ReadinessDto>(Assert.IsType<OkObjectResult>(result.Result).Value).WorkflowSignal.NeedsFollowUpAction;
}
if (surface == "detail")
{
var result = await controller.GetById(id, default);
return Assert.IsType<JobApplicationDto>(Assert.IsType<OkObjectResult>(result.Result).Value).NeedsFollowUp;
}
if (surface == "list")
{
var result = await controller.GetAll();
return Assert.Single(Assert.IsType<PagedResult<JobApplicationDto>>(Assert.IsType<OkObjectResult>(result.Result).Value).Items).NeedsFollowUp;
}
if (surface == "board")
{
var result = await controller.GetBoard(needsFollowUp: true);
return Assert.IsType<List<JobApplication>>(Assert.IsType<OkObjectResult>(result.Result).Value).Any(job => job.Id == id);
}
var response = await controller.GetReminders();
var jobs = Assert.IsType<List<JobApplicationDto>>(Assert.IsType<OkObjectResult>(response.Result).Value);
return jobs.SingleOrDefault(job => job.Id == id)?.NeedsFollowUp ?? false;
}
private sealed class Fixture : IAsyncDisposable
{
private readonly SqliteConnection _connection = new("Data Source=:memory:");
public int JobA { get; private set; }
public int JobB { get; private set; }
public JobTrackerContext Context(string? owner, IInterceptor? interceptor = null)
{
var user = new Mock<ICurrentUserService>();
user.SetupGet(x => x.UserId).Returns(owner);
var options = new DbContextOptionsBuilder<JobTrackerContext>().UseSqlite(_connection);
if (interceptor is not null) options.AddInterceptors(interceptor);
return new JobTrackerContext(options.Options, user.Object);
}
public JobApplicationsController Controller(JobTrackerContext db) =>
new(db, Mock.Of<ISummarizerService>(), TestHostFactory.CreateUserManager().Object);
public static async Task<Fixture> CreateAsync()
{
var fixture = new Fixture();
await fixture._connection.OpenAsync();
await using var db = fixture.Context("owner-a");
await db.Database.EnsureCreatedAsync();
db.Users.AddRange(new ApplicationUser { Id = "owner-a", UserName = "owner-a" }, new ApplicationUser { Id = "owner-b", UserName = "owner-b" });
var global = await db.RuleSettings.SingleAsync();
global.AppliedFollowUpDays = 30;
global.AppliedGhostDays = 365;
foreach (var owner in new[] { "owner-a", "owner-b" })
{
await using var owned = fixture.Context(owner);
owned.UserRuleSettings.Add(new UserRuleSettings { OwnerUserId = owner, AppliedFollowUpDays = 30, AppliedGhostDays = 365 });
// Complete package avoids unrelated package-gap reminders masking the rule decision.
var job = new JobApplication
{
OwnerUserId = owner, Company = new Company { Name = owner, OwnerUserId = owner },
JobTitle = "Synthetic engineer", Status = "Applied", DateApplied = DateTime.Now.AddDays(-10), FollowUpAt = DateTime.Now.AddDays(100),
TailoredCvText = "Synthetic CV", Notes = "<<<APPLICATION_ANSWER_DRAFT>>>\nSynthetic answer\n<<<END_APPLICATION_ANSWER_DRAFT>>>",
};
owned.JobApplications.Add(job);
// Users must exist before owner-scoped rows are inserted.
await db.SaveChangesAsync();
await owned.SaveChangesAsync();
if (owner == "owner-a") fixture.JobA = job.Id; else fixture.JobB = job.Id;
}
return fixture;
}
public async ValueTask DisposeAsync()
{
await _connection.DisposeAsync();
}
}
}
@@ -4,6 +4,7 @@ using JobTrackerApi.Services;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using System.Text.Json;
using Moq;
using Xunit;
@@ -90,16 +91,119 @@ public sealed class SubmittedApplicationPackageTests
Assert.Empty(db.SubmittedApplicationPackages);
}
private static (JobTrackerContext Db, SubmittedApplicationPackageService Service, Mock<IAttachmentStorage> Storage) New(string userId)
[Theory]
[InlineData(false, 4)]
[InlineData(true, 4)]
[InlineData(false, 0)]
[InlineData(true, 0)]
public async Task Cv_changed_between_metadata_and_render_aborts_without_a_partial_package(bool deleted, int version)
{
var renderer = new Mock<ICvVariantService>();
var (db, service, storage) = New("owner-1", renderer);
await using var dispose = db;
var job = await SeedAsync(db, "owner-1", null);
(await db.CvVariants.SingleAsync()).Version = version;
await db.SaveChangesAsync();
var real = new CvVariantService(db, new CareerProfileService(db), new ThemedCvRenderer());
async Task<ThemedCvRenderResult?> RenderAfterChange(string owner, int id, CvRenderPerson person, CancellationToken ct, int? expected)
{
var current = await db.CvVariants.SingleAsync(x => x.Id == id, ct);
if (deleted) db.CvVariants.Remove(current);
else
{
current.Version += 1;
current.SettingsJson = CvVariantSettingsJson.Serialize(new CvVariantSettings { ThemeId = "nordic", Headline = "Newer CV content" });
}
await db.SaveChangesAsync(ct);
return await real.RenderAsync(owner, id, person, ct, expected);
}
renderer.Setup(x => x.RenderAsync("owner-1", It.IsAny<int>(), It.IsAny<CvRenderPerson>(), It.IsAny<CancellationToken>()))
.Returns((string owner, int id, CvRenderPerson person, CancellationToken ct) => RenderAfterChange(owner, id, person, ct, null));
renderer.Setup(x => x.RenderAsync("owner-1", It.IsAny<int>(), It.IsAny<CvRenderPerson>(), It.IsAny<CancellationToken>(), It.IsAny<int?>()))
.Returns((string owner, int id, CvRenderPerson person, CancellationToken ct, int? expected) => RenderAfterChange(owner, id, person, ct, expected));
await Assert.ThrowsAsync<DbUpdateConcurrencyException>(() => service.CaptureAsync("owner-1", job.Id, new CvRenderPerson("Ada", null), default));
Assert.Empty(db.SubmittedApplicationPackages);
Assert.Empty(db.SubmittedPackageAttachments);
renderer.Verify(x => x.RenderAsync("owner-1", It.IsAny<int>(), It.IsAny<CvRenderPerson>(), It.IsAny<CancellationToken>(), version > 0 ? version : null), Times.Once);
storage.Verify(x => x.SnapshotAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<CancellationToken>()), Times.Never);
}
[Fact]
public async Task Profile_changed_during_render_cannot_publish_old_profile_metadata_with_new_html()
{
var renderer = new Mock<ICvVariantService>();
var (db, service, _) = New("owner-1", renderer);
await using var dispose = db;
var job = await SeedAsync(db, "owner-1", null);
var career = new CareerProfileService(db);
var real = new CvVariantService(db, career, new ThemedCvRenderer());
renderer.Setup(x => x.RenderAsync("owner-1", It.IsAny<int>(), It.IsAny<CvRenderPerson>(), It.IsAny<CancellationToken>(), It.IsAny<int?>()))
.Returns(async (string owner, int id, CvRenderPerson person, CancellationToken ct, int? expected) =>
{
var changed = JsonSerializer.Deserialize<StructuredCvProfile>("{\"version\":\"1\",\"contact\":{\"fullName\":\"New during capture\",\"headline\":\"Updated canonical profile\"}}", new JsonSerializerOptions { PropertyNameCaseInsensitive = true })!;
await career.SaveVersionAsync(owner, changed, "manual", 0, ct);
var rendered = await real.RenderAsync(owner, id, person, ct, expected);
Assert.Contains("New during capture", rendered!.Html);
return rendered;
});
await Assert.ThrowsAsync<DbUpdateConcurrencyException>(() => service.CaptureAsync("owner-1", job.Id, new CvRenderPerson("Ada", null), default));
Assert.Empty(db.SubmittedApplicationPackages);
Assert.Equal(1, (await db.CareerProfiles.SingleAsync()).Version);
}
[Theory]
[InlineData(true)]
[InlineData(false)]
public async Task Profile_created_or_removed_during_render_aborts_capture(bool created)
{
var renderer = new Mock<ICvVariantService>();
var (db, service, _) = New("owner-1", renderer);
await using var dispose = db;
var job = await SeedAsync(db, "owner-1", null);
if (created) { db.CareerProfiles.Remove(await db.CareerProfiles.SingleAsync()); await db.SaveChangesAsync(); }
var real = new CvVariantService(db, new CareerProfileService(db), new ThemedCvRenderer());
renderer.Setup(x => x.RenderAsync("owner-1", It.IsAny<int>(), It.IsAny<CvRenderPerson>(), It.IsAny<CancellationToken>(), It.IsAny<int?>()))
.Returns(async (string owner, int id, CvRenderPerson person, CancellationToken ct, int? expected) =>
{
if (created) db.CareerProfiles.Add(new CareerProfile { OwnerUserId = owner, Version = 1, ProfileJson = "{}" });
else db.CareerProfiles.Remove(await db.CareerProfiles.SingleAsync(ct));
await db.SaveChangesAsync(ct);
return await real.RenderAsync(owner, id, person, ct, expected);
});
await Assert.ThrowsAsync<DbUpdateConcurrencyException>(() => service.CaptureAsync("owner-1", job.Id, new CvRenderPerson("Ada", null), default));
Assert.Empty(db.SubmittedApplicationPackages);
}
[Fact]
public async Task Unchanged_absent_legacy_profile_remains_capturable()
{
var (db, service, _) = New("owner-1");
await using var dispose = db;
var job = await SeedAsync(db, "owner-1", null);
db.CareerProfiles.Remove(await db.CareerProfiles.SingleAsync()); await db.SaveChangesAsync();
Assert.NotNull(await service.CaptureAsync("owner-1", job.Id, new CvRenderPerson("Ada", null), default));
Assert.Null((await db.SubmittedApplicationPackages.SingleAsync()).CareerProfileJson);
}
private static (JobTrackerContext Db, SubmittedApplicationPackageService Service, Mock<IAttachmentStorage> Storage) New(string userId, Mock<ICvVariantService>? renderOverride = null)
{
var currentUser = new Mock<ICurrentUserService>();
currentUser.SetupGet(x => x.UserId).Returns(userId);
var options = new DbContextOptionsBuilder<JobTrackerContext>().UseInMemoryDatabase(Guid.NewGuid().ToString()).Options;
var db = new JobTrackerContext(options, currentUser.Object);
var variants = new Mock<ICvVariantService>();
variants.Setup(x => x.RenderAsync(userId, It.IsAny<int>(), It.IsAny<CvRenderPerson>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new ThemedCvRenderResult("nordic", "cv.pdf", "<html>Frozen CV</html>"));
var variants = renderOverride ?? new Mock<ICvVariantService>();
if (renderOverride is null)
{
variants.Setup(x => x.RenderAsync(userId, It.IsAny<int>(), It.IsAny<CvRenderPerson>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(new ThemedCvRenderResult("nordic", "cv.pdf", "<html>Frozen CV</html>"));
variants.Setup(x => x.RenderAsync(userId, It.IsAny<int>(), It.IsAny<CvRenderPerson>(), It.IsAny<CancellationToken>(), It.IsAny<int?>()))
.ReturnsAsync(new ThemedCvRenderResult("nordic", "cv.pdf", "<html>Frozen CV</html>"));
}
var storage = new Mock<IAttachmentStorage>();
storage.Setup(x => x.IsManagedPath(It.IsAny<string>())).Returns(true);
@@ -0,0 +1,44 @@
using System.Security.Claims;
using System.Text.Json;
using JobTrackerApi.Controllers;
using JobTrackerApi.Models;
using JobTrackerApi.Services;
using JobTrackerApi.Tests.TestSupport;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Moq;
using Xunit;
namespace JobTrackerApi.Tests;
public sealed class SubmittedPackageConflictTests
{
[Theory]
[InlineData(true)]
[InlineData(false)]
public async Task Capture_reports_revision_conflict_only_for_an_authenticated_caller(bool authenticated)
{
var user = new ApplicationUser { Id = "owner" };
var users = TestHostFactory.CreateUserManager(user);
users.Setup(x => x.GetUserAsync(It.IsAny<ClaimsPrincipal>())).ReturnsAsync(authenticated ? user : null);
var packages = new Mock<ISubmittedApplicationPackageService>();
packages.Setup(x => x.CaptureAsync("owner", 7, It.IsAny<CvRenderPerson>(), It.IsAny<CancellationToken>()))
.ThrowsAsync(new DbUpdateConcurrencyException("CV changed"));
var controller = new ApplicationAssetsController(users.Object, Mock.Of<IApplicationAssetsService>(), packages.Object)
{ ControllerContext = new() { HttpContext = new DefaultHttpContext() } };
var result = await controller.CaptureSubmittedPackage(7, default);
if (authenticated)
{
var conflict = Assert.IsType<ConflictObjectResult>(result.Result);
Assert.Equal("revision_conflict", JsonSerializer.SerializeToElement(conflict.Value).GetProperty("code").GetString());
}
else
{
Assert.IsType<UnauthorizedResult>(result.Result);
packages.Verify(x => x.CaptureAsync(It.IsAny<string>(), It.IsAny<int>(), It.IsAny<CvRenderPerson>(), It.IsAny<CancellationToken>()), Times.Never);
}
}
}
@@ -148,8 +148,15 @@ public sealed class ApplicationAssetsController : ControllerBase
{
var user = await _users.GetUserAsync(User);
if (user is null) return Unauthorized();
var result = await _packages.CaptureAsync(user.Id, jobId, Person(user), ct);
return result is null ? NotFound() : Ok(result);
try
{
var result = await _packages.CaptureAsync(user.Id, jobId, Person(user), ct);
return result is null ? NotFound() : Ok(result);
}
catch (DbUpdateConcurrencyException)
{
return Conflict(new { code = "revision_conflict", message = "The application documents changed. Reload and review them before capturing a submitted package." });
}
}
[HttpGet("submitted-packages/{packageId:int}")]
@@ -1,7 +1,6 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory;
using JobTrackerApi.Data;
using JobTrackerApi.Models;
using JobTrackerApi.Services;
@@ -31,10 +30,9 @@ namespace JobTrackerApi.Controllers
private readonly ICvTemplateRenderer _cvTemplateRenderer;
private readonly ICvPdfExporter _cvPdfExporter;
private readonly IJobCvMatchService _matchService;
private readonly IMemoryCache _cache;
private readonly IApplicationChecklistService _checklist;
public JobApplicationsController(JobTrackerContext db, ISummarizerService summarizer, UserManager<ApplicationUser> users, ICvTemplateRenderer? cvTemplateRenderer = null, ICvPdfExporter? cvPdfExporter = null, IJobCvMatchService? matchService = null, IMemoryCache? cache = null, IApplicationChecklistService? checklist = null)
public JobApplicationsController(JobTrackerContext db, ISummarizerService summarizer, UserManager<ApplicationUser> users, ICvTemplateRenderer? cvTemplateRenderer = null, ICvPdfExporter? cvPdfExporter = null, IJobCvMatchService? matchService = null, IApplicationChecklistService? checklist = null)
{
_checklist = checklist ?? new ApplicationChecklistService(db);
_db = db;
@@ -43,21 +41,6 @@ namespace JobTrackerApi.Controllers
_cvTemplateRenderer = cvTemplateRenderer ?? new CvTemplateRenderer();
_cvPdfExporter = cvPdfExporter ?? new ThrowingCvPdfExporter();
_matchService = matchService ?? new JobCvMatchService();
_cache = cache ?? new MemoryCache(new MemoryCacheOptions());
}
// ponytail: RulesEngine.GetSettings is per-user (falls back to the global RuleSettings
// singleton only when the user has no override), so the cache key must include the
// current user id -- a single global key would leak one user's follow-up rules to another.
private async Task<RuleSettings> GetCachedRuleSettingsAsync(CancellationToken cancellationToken)
{
var cacheKey = $"rulesettings:{_db.CurrentUserId ?? "anon"}";
var cached = await _cache.GetOrCreateAsync(cacheKey, async entry =>
{
entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(30);
return await RulesEngine.GetSettings(_db, cancellationToken);
});
return cached!;
}
private sealed class ThrowingCvPdfExporter : ICvPdfExporter
@@ -539,7 +522,7 @@ Canonical profile:
query = query.Where(j => j.Location != null && EF.Functions.Like(j.Location, like));
}
var settings = await GetCachedRuleSettingsAsync(cancellationToken);
var settings = await RulesEngine.GetSettings(_db, cancellationToken);
var now = DateTime.Now;
Task<Dictionary<int, DateTime?>> LoadLatestMessagesAsync(IQueryable<Correspondence> messages) => messages
@@ -650,7 +633,7 @@ Canonical profile:
if (job is null) return NotFound();
var settings = await GetCachedRuleSettingsAsync(cancellationToken);
var settings = await RulesEngine.GetSettings(_db, cancellationToken);
var now = DateTime.Now;
var lm = await _db.Correspondences
.AsNoTracking()
@@ -693,7 +676,7 @@ Canonical profile:
if (upcomingDays < 1) upcomingDays = 1;
if (upcomingDays > 90) upcomingDays = 90;
var settings = await GetCachedRuleSettingsAsync(cancellationToken);
var settings = await RulesEngine.GetSettings(_db, cancellationToken);
var now = DateTime.Now;
var upcomingTo = now.AddDays(upcomingDays);
@@ -839,7 +822,7 @@ Canonical profile:
.Include(j => j.Company)
.FirstAsync(j => j.Id == job.Id, cancellationToken);
var settings = await GetCachedRuleSettingsAsync(cancellationToken);
var settings = await RulesEngine.GetSettings(_db, cancellationToken);
var followUp = RulesEngine.Evaluate(settings, created, DateTime.Now, lastMessageAt: null);
return CreatedAtAction(nameof(GetById), new { id = created.Id }, BuildJobApplicationDto(created, followUp));
}
@@ -1029,7 +1012,7 @@ Canonical profile:
await _db.SaveChangesAsync(cancellationToken);
var settings = await GetCachedRuleSettingsAsync(cancellationToken);
var settings = await RulesEngine.GetSettings(_db, cancellationToken);
var lastMsg = await _db.Correspondences
.AsNoTracking()
.Where(c => c.JobApplicationId == id)
@@ -1383,7 +1366,7 @@ Candidate CV/profile:
.FirstOrDefaultAsync(j => j.Id == id, cancellationToken);
if (job is null) return NotFound();
var settings = await GetCachedRuleSettingsAsync(cancellationToken);
var settings = await RulesEngine.GetSettings(_db, cancellationToken);
var now = DateTime.Now;
var lastMessageAt = await _db.Correspondences
.AsNoTracking()
@@ -72,7 +72,20 @@ public sealed class SubmittedApplicationPackageService : ISubmittedApplicationPa
? (await variantQuery.ToListAsync(ct)).MaxBy(v => v.UpdatedAtUtc)
: await variantQuery.OrderByDescending(v => v.UpdatedAtUtc).FirstOrDefaultAsync(ct);
var profile = await _db.CareerProfiles.AsNoTracking().FirstOrDefaultAsync(p => p.OwnerUserId == ownerUserId, ct);
var render = variant is null ? null : await _variants.RenderAsync(ownerUserId, variant.Id, person, ct);
var render = variant is null ? null : await _variants.RenderAsync(ownerUserId, variant.Id, person, ct, variant.Version > 0 ? variant.Version : null);
if (variant is not null)
{
// Legacy version-zero variants remain capturable; recheck the stored snapshot after rendering.
var current = await _db.CvVariants.AsNoTracking()
.Where(v => v.Id == variant.Id && v.OwnerUserId == ownerUserId)
.Select(v => new { v.Version, v.SettingsJson }).SingleOrDefaultAsync(ct);
if (render is null || current is null || current.Version != variant.Version || current.SettingsJson != variant.SettingsJson)
throw new DbUpdateConcurrencyException("The selected CV changed before the submitted package could be captured.");
var currentProfile = await _db.CareerProfiles.AsNoTracking().Where(p => p.OwnerUserId == ownerUserId)
.Select(p => new { p.Version, p.ProfileJson }).FirstOrDefaultAsync(ct);
if (currentProfile?.Version != profile?.Version || currentProfile?.ProfileJson != profile?.ProfileJson)
throw new DbUpdateConcurrencyException("The Career Profile changed before the submitted package could be captured.");
}
var version = (await _db.SubmittedApplicationPackages.AsNoTracking()
.Where(x => x.OwnerUserId == ownerUserId && x.JobApplicationId == jobApplicationId)
.Select(x => (int?)x.Version)
@@ -0,0 +1,11 @@
# Canonical profile binding during submitted CV capture
Follow-up to PACKAGE_REVISION_CAPTURE.md. The CV variant guard alone did not bind the canonical profile metadata to the profile used by rendering. A deterministic test used the real CareerProfileService save and CvVariantService render to change the profile during capture; the resulting HTML contained the new name while capture could still publish the previously selected projection.
Capture now rechecks the owner's canonical profile revision/projection after rendering and rejects a changed, created or removed row through the existing revision_conflict path. A genuinely absent legacy profile remains capturable. Canonical save/render ownership, null-as-inherit selections, renderer code/styles and historical packages are unchanged. No new profile store, schema, API field or migration.
RED:one focused test failed with no conflict. GREEN:26 targeted cases passed/no skips across package/controller/export-revision/native SQLite suites;22 overlap the preceding run. Four added cases cover real profile save during render, creation, removal and unchanged absence. Logs and raw/gzip TRX/source receipts are in evidence/package-profile. Build/temp/results used E; no live DB/models/providers, physical PDF or production services were used.
This guards the canonical row/projection used by ordinary writers. It is not proof of a transactionally atomic snapshot across every source, arbitrary direct database mutations, legacy user-only fallback writes, or filesystem races. AttachmentStorage destination ownership remains a separate source-backed/unreproduced concern. Native/MariaDB/browser, full integrated security/recovery and final milestone gates remain open.
Rollback reverts only this follow-up source change and reruns affected package tests; no data repair required. Existing accepted PDF metrics remain historical and source-bound, not newly executed.
@@ -0,0 +1,15 @@
# Submitted CV snapshot revision integrity
Baseline9f341750; sourcee161ea33 on codex/frozen-document-integrity. This is a bounded integrity fix, not Phase10/A3/security/release acceptance.
The package service selected CV metadata and then rendered through the unversioned overload. A deterministic interleaving with the real CvVariantService reproduced publishing newer render data with older selected metadata, or continuing after deletion. Capture now passes the selected positive revision and rechecks the stored revision/settings after render. A missing selected CV aborts. The controller returns the existing409 revision_conflict code for this condition, preserving unauthenticated rejection and owner filtering.
An initial positive-only guard broke the native SQLite legacy version-zero fixture. The final path preserves zero-version capture through the existing unversioned compatibility path plus the post-render snapshot check. The public renderer's positive-revision validation was not relaxed. No fixture was weakened or renderer/physical PDF style changed.
Verification:RED8 cases (three failed/five passed); intermediate broader20 cases (one legacy compatibility failure/19 passed); final22 cases across package, controller conflict/auth, CV export revision and native SQLite compatibility suites passed with no skips. Four changed/deleted interleavings cover revisions4 and0; controller cases cover authenticated conflict/unauthenticated refusal. Existing frozen bytes/hash, tenant rejection, missing-file cleanup, later source mutation, account-export and native SQLite workflows remain covered. Native TRX gzip/raw receipts and source hashes are in evidence/package-revision.
The first build could not execute tests because C: was full. Only this run's fresh ignored bin/obj directories were removed after absolute-path/Git checks; generated output/temp/results were redirected to E using command flags, with offline cached restore. No source/evidence/dependency/user work was deleted. Delegated workers hit usage limits; root continued sequentially. C: capacity remains constrained.
Remaining investigation:concurrent Career Profile changes during render may require their own snapshot binding. AttachmentStorage.SnapshotAsync has a separate source-backed destination-ownership cleanup concern; no deterministic race reproduction or fix is claimed here. MariaDB, actual HTTP/browser capture, real models/providers, native device/AT, physical PDF acceptance, integrated security and recovery were not rerun. Existing accepted PDF metrics/visuals remain source-bound.
Rollback:revert the source commit, rebuild on the isolated development branch and rerun the affected suite. No migration/data repair is involved. UI callers retain the existing revision-conflict/review flow; an aborted capture publishes no package. This does not establish atomic consistency across every package source entity or filesystem race.
@@ -0,0 +1,27 @@
{
"sourceCommit": "b9c0b6186871d31a494c05cabc5e6361dccb851f",
"sourcesLfSha256": {
"JobTrackerApi/Services/SubmittedApplicationPackageService.cs": "75a4aabb02d21cc860cfa05006defaddeb8ef5918eb973e624690b0d8da42e70",
"JobTrackerApi.Tests/SubmittedApplicationPackageTests.cs": "9f737f14852a1bb93e36913407aaed9d2939207a4f0806d1086d8cc456350af3",
"JobTrackerApi/Services/CareerProfileService.cs": "dc91aca78069feff24eecf796c1fac459af9f5564e61d7918822f45b81807fef",
"JobTrackerApi/Services/CvVariantService.cs": "da85153ff2199a4410df91807c6073aa19736676332266a227149d6d089ea841"
},
"receipts": [
{
"name": "profile-red",
"total": 1,
"passed": 0,
"failed": 1,
"rawSha256": "b63f144ec7c99a3eb40841ac7e5cc59ae84d71017e388afccb4cf441e3a9fcba",
"gzipSha256": "482b6a8b235ce988134cbaebe52de232b3c469dcd02f535156a05a3bf5a2fa82"
},
{
"name": "profile-green",
"total": 26,
"passed": 26,
"failed": 0,
"rawSha256": "7eb74ef3e0aca92ae83dd673026cdb1bfc45da3b976732ad6f043ae83f9b6ea9",
"gzipSha256": "eb5251eda46e66feb96d69711757c19aea2bef1da70c9e404321b8827910301a"
}
]
}
@@ -0,0 +1,11 @@
JobTrackerApi -> E:\Storage\Projects\Active\temp docs\jobjakt-local-build\frozen-document-integrity\artifacts\bin\JobTrackerApi\debug\JobTrackerApi.dll
PdfBrowserStub -> E:\Storage\Projects\Active\temp docs\jobjakt-local-build\frozen-document-integrity\artifacts\bin\PdfBrowserStub\debug\PdfBrowserStub.dll
JobTrackerApi.Tests -> E:\Storage\Projects\Active\temp docs\jobjakt-local-build\frozen-document-integrity\artifacts\bin\JobTrackerApi.Tests\debug\JobTrackerApi.Tests.dll
Test run for E:\Storage\Projects\Active\temp docs\jobjakt-local-build\frozen-document-integrity\artifacts\bin\JobTrackerApi.Tests\debug\JobTrackerApi.Tests.dll (.NETCoreApp,Version=v9.0)
VSTest version 17.14.1 (x64)
Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
Results File: E:\Storage\Projects\Active\temp docs\jobjakt-local-build\frozen-document-integrity\profile-green\profile-green.trx
Passed! - Failed: 0, Passed: 26, Skipped: 0, Total: 26, Duration: 4 s - JobTrackerApi.Tests.dll (net9.0)
@@ -0,0 +1,20 @@
JobTrackerApi -> E:\Storage\Projects\Active\temp docs\jobjakt-local-build\frozen-document-integrity\artifacts\bin\JobTrackerApi\debug\JobTrackerApi.dll
PdfBrowserStub -> E:\Storage\Projects\Active\temp docs\jobjakt-local-build\frozen-document-integrity\artifacts\bin\PdfBrowserStub\debug\PdfBrowserStub.dll
JobTrackerApi.Tests -> E:\Storage\Projects\Active\temp docs\jobjakt-local-build\frozen-document-integrity\artifacts\bin\JobTrackerApi.Tests\debug\JobTrackerApi.Tests.dll
Test run for E:\Storage\Projects\Active\temp docs\jobjakt-local-build\frozen-document-integrity\artifacts\bin\JobTrackerApi.Tests\debug\JobTrackerApi.Tests.dll (.NETCoreApp,Version=v9.0)
VSTest version 17.14.1 (x64)
Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
[xUnit.net 00:00:03.18] JobTrackerApi.Tests.SubmittedApplicationPackageTests.Profile_changed_during_render_cannot_publish_old_profile_metadata_with_new_html [FAIL]
Failed JobTrackerApi.Tests.SubmittedApplicationPackageTests.Profile_changed_during_render_cannot_publish_old_profile_metadata_with_new_html [2 s]
Error Message:
Assert.Throws() Failure: No exception was thrown
Expected: typeof(Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException)
Stack Trace:
at JobTrackerApi.Tests.SubmittedApplicationPackageTests.Profile_changed_during_render_cannot_publish_old_profile_metadata_with_new_html() in C:\Users\Connor\.codex\worktrees\frozen-document-integrity\Job tracker\JobTrackerApi.Tests\SubmittedApplicationPackageTests.cs:line 152
at JobTrackerApi.Tests.SubmittedApplicationPackageTests.Profile_changed_during_render_cannot_publish_old_profile_metadata_with_new_html() in C:\Users\Connor\.codex\worktrees\frozen-document-integrity\Job tracker\JobTrackerApi.Tests\SubmittedApplicationPackageTests.cs:line 155
--- End of stack trace from previous location ---
Results File: E:\Storage\Projects\Active\temp docs\jobjakt-local-build\frozen-document-integrity\profile-red\profile-red.trx
Failed! - Failed: 1, Passed: 0, Skipped: 0, Total: 1, Duration: 2 s - JobTrackerApi.Tests.dll (net9.0)
@@ -0,0 +1,43 @@
{
"baseline": "9f341750ece2a3d2ddb11c14482df1ecc485a5c1",
"sourceCommit": "e161ea33461eaefbecf08c96d79984029131e990",
"sourcesLfSha256": {
"JobTrackerApi/Services/SubmittedApplicationPackageService.cs": "f6315d491ccd5ddffb098ef22f35b0d727485bfe866ea875fc19c33ab897867e",
"JobTrackerApi/Controllers/ApplicationAssetsController.cs": "bbed71f315f0be0381e4d6a09a536880f40040ba808b83a9d058b9fb1089bcc2",
"JobTrackerApi.Tests/SubmittedApplicationPackageTests.cs": "9073b2ddc1e92e84063e10c071042c408c9eb9ab7f8403b2801bf5bbd121463b",
"JobTrackerApi.Tests/SubmittedPackageConflictTests.cs": "e24038cae68c0d034bbcc07605aff09be1e23d4659ce391cf3de16c98f6a2ddf",
"JobTrackerApi.Tests/CvExportRevisionTests.cs": "ee19195cec7486dd71d30ce6daa064ad07d5995c1dd97e529761e43511d9235d",
"JobTrackerApi.Tests/SqliteDateTimeOffsetCompatibilityTests.cs": "5fb244d50d2378b8d18b14ac260beafc4bfd58190447306244b2916ea808d572",
"JobTrackerApi/Services/CvVariantService.cs": "da85153ff2199a4410df91807c6073aa19736676332266a227149d6d089ea841"
},
"nativeReceipts": [
{
"name": "red",
"artifact": "red.trx.gz",
"total": 8,
"passed": 5,
"failed": 3,
"rawSha256": "1c970594d7f3c7e86bc7d15ea7b79967cfcfb2202333d9933d9b70180d680bac",
"gzipSha256": "3b69eafa4bda144a132547ec54a8d20b54b14fe263eb5c23ea854afff7f69598"
},
{
"name": "intermediate",
"artifact": "intermediate.trx.gz",
"total": 20,
"passed": 19,
"failed": 1,
"rawSha256": "26968e1cf8b575ba37de5f57f2898e4542427cf03a5136e7df351b9c4096b99c",
"gzipSha256": "8a6021ee11a9dcd77b29f43cc3a146dd75d2f514e74f8650301a6e1cbd4fa0d2"
},
{
"name": "final",
"artifact": "final.trx.gz",
"total": 22,
"passed": 22,
"failed": 0,
"rawSha256": "faaa712ee6919280cda1e4384746f04ba02c7d807b3d4808b4468815df208062",
"gzipSha256": "36e214810c9c840b5335385d1c18786489f57dd6f4812249a7e7c7ecb652f76a"
}
],
"environment": "Pinned .NET9.0.317; existing offline NuGet cache; build/temp/results on E after C-drive exhaustion. No physical PDF/provider/MariaDB/live services."
}
@@ -0,0 +1,11 @@
JobTrackerApi -> E:\Storage\Projects\Active\temp docs\jobjakt-local-build\frozen-document-integrity\artifacts\bin\JobTrackerApi\debug\JobTrackerApi.dll
PdfBrowserStub -> E:\Storage\Projects\Active\temp docs\jobjakt-local-build\frozen-document-integrity\artifacts\bin\PdfBrowserStub\debug\PdfBrowserStub.dll
JobTrackerApi.Tests -> E:\Storage\Projects\Active\temp docs\jobjakt-local-build\frozen-document-integrity\artifacts\bin\JobTrackerApi.Tests\debug\JobTrackerApi.Tests.dll
Test run for E:\Storage\Projects\Active\temp docs\jobjakt-local-build\frozen-document-integrity\artifacts\bin\JobTrackerApi.Tests\debug\JobTrackerApi.Tests.dll (.NETCoreApp,Version=v9.0)
VSTest version 17.14.1 (x64)
Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
Results File: E:\Storage\Projects\Active\temp docs\jobjakt-local-build\frozen-document-integrity\green-final\green-final.trx
Passed! - Failed: 0, Passed: 22, Skipped: 0, Total: 22, Duration: 4 s - JobTrackerApi.Tests.dll (net9.0)
@@ -0,0 +1,21 @@
JobTrackerApi -> E:\Storage\Projects\Active\temp docs\jobjakt-local-build\frozen-document-integrity\artifacts\bin\JobTrackerApi\debug\JobTrackerApi.dll
PdfBrowserStub -> E:\Storage\Projects\Active\temp docs\jobjakt-local-build\frozen-document-integrity\artifacts\bin\PdfBrowserStub\debug\PdfBrowserStub.dll
JobTrackerApi.Tests -> E:\Storage\Projects\Active\temp docs\jobjakt-local-build\frozen-document-integrity\artifacts\bin\JobTrackerApi.Tests\debug\JobTrackerApi.Tests.dll
Test run for E:\Storage\Projects\Active\temp docs\jobjakt-local-build\frozen-document-integrity\artifacts\bin\JobTrackerApi.Tests\debug\JobTrackerApi.Tests.dll (.NETCoreApp,Version=v9.0)
VSTest version 17.14.1 (x64)
Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
[xUnit.net 00:00:03.64] JobTrackerApi.Tests.SqliteDateTimeOffsetCompatibilityTests.Tailoring_and_submission_capture_work_on_native_sqlite [FAIL]
Failed JobTrackerApi.Tests.SqliteDateTimeOffsetCompatibilityTests.Tailoring_and_submission_capture_work_on_native_sqlite [768 ms]
Error Message:
Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException : The CV changed after it was loaded.
Stack Trace:
at JobTrackerApi.Services.CvVariantService.RenderAsync(String ownerUserId, Int32 id, CvRenderPerson person, CancellationToken ct, Nullable`1 expectedVersion) in C:\Users\Connor\.codex\worktrees\frozen-document-integrity\Job tracker\JobTrackerApi\Services\CvVariantService.cs:line 188
at JobTrackerApi.Services.SubmittedApplicationPackageService.CaptureAsync(String ownerUserId, Int32 jobApplicationId, CvRenderPerson person, CancellationToken ct) in C:\Users\Connor\.codex\worktrees\frozen-document-integrity\Job tracker\JobTrackerApi\Services\SubmittedApplicationPackageService.cs:line 75
at JobTrackerApi.Tests.SqliteDateTimeOffsetCompatibilityTests.Tailoring_and_submission_capture_work_on_native_sqlite() in C:\Users\Connor\.codex\worktrees\frozen-document-integrity\Job tracker\JobTrackerApi.Tests\SqliteDateTimeOffsetCompatibilityTests.cs:line 50
at JobTrackerApi.Tests.SqliteDateTimeOffsetCompatibilityTests.Tailoring_and_submission_capture_work_on_native_sqlite() in C:\Users\Connor\.codex\worktrees\frozen-document-integrity\Job tracker\JobTrackerApi.Tests\SqliteDateTimeOffsetCompatibilityTests.cs:line 94
--- End of stack trace from previous location ---
Results File: E:\Storage\Projects\Active\temp docs\jobjakt-local-build\frozen-document-integrity\green\green.trx
Failed! - Failed: 1, Passed: 19, Skipped: 0, Total: 20, Duration: 3 s - JobTrackerApi.Tests.dll (net9.0)
@@ -0,0 +1,37 @@
JobTrackerApi -> E:\Storage\Projects\Active\temp docs\jobjakt-local-build\frozen-document-integrity\artifacts\bin\JobTrackerApi\debug\JobTrackerApi.dll
PdfBrowserStub -> E:\Storage\Projects\Active\temp docs\jobjakt-local-build\frozen-document-integrity\artifacts\bin\PdfBrowserStub\debug\PdfBrowserStub.dll
JobTrackerApi.Tests -> E:\Storage\Projects\Active\temp docs\jobjakt-local-build\frozen-document-integrity\artifacts\bin\JobTrackerApi.Tests\debug\JobTrackerApi.Tests.dll
Test run for E:\Storage\Projects\Active\temp docs\jobjakt-local-build\frozen-document-integrity\artifacts\bin\JobTrackerApi.Tests\debug\JobTrackerApi.Tests.dll (.NETCoreApp,Version=v9.0)
VSTest version 17.14.1 (x64)
Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
[xUnit.net 00:00:03.04] JobTrackerApi.Tests.SubmittedPackageConflictTests.Capture_reports_revision_conflict_only_for_an_authenticated_caller(authenticated: True) [FAIL]
Failed JobTrackerApi.Tests.SubmittedPackageConflictTests.Capture_reports_revision_conflict_only_for_an_authenticated_caller(authenticated: True) [723 ms]
Error Message:
Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException : CV changed
Stack Trace:
at JobTrackerApi.Controllers.ApplicationAssetsController.CaptureSubmittedPackage(Int32 jobId, CancellationToken ct) in C:\Users\Connor\.codex\worktrees\frozen-document-integrity\Job tracker\JobTrackerApi\Controllers\ApplicationAssetsController.cs:line 151
at JobTrackerApi.Tests.SubmittedPackageConflictTests.Capture_reports_revision_conflict_only_for_an_authenticated_caller(Boolean authenticated) in C:\Users\Connor\.codex\worktrees\frozen-document-integrity\Job tracker\JobTrackerApi.Tests\SubmittedPackageConflictTests.cs:line 31
--- End of stack trace from previous location ---
[xUnit.net 00:00:05.56] JobTrackerApi.Tests.SubmittedApplicationPackageTests.Cv_changed_between_metadata_and_render_aborts_without_a_partial_package(deleted: False) [FAIL]
[xUnit.net 00:00:05.58] JobTrackerApi.Tests.SubmittedApplicationPackageTests.Cv_changed_between_metadata_and_render_aborts_without_a_partial_package(deleted: True) [FAIL]
Failed JobTrackerApi.Tests.SubmittedApplicationPackageTests.Cv_changed_between_metadata_and_render_aborts_without_a_partial_package(deleted: False) [137 ms]
Error Message:
Assert.Throws() Failure: No exception was thrown
Expected: typeof(Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException)
Stack Trace:
at JobTrackerApi.Tests.SubmittedApplicationPackageTests.Cv_changed_between_metadata_and_render_aborts_without_a_partial_package(Boolean deleted) in C:\Users\Connor\.codex\worktrees\frozen-document-integrity\Job tracker\JobTrackerApi.Tests\SubmittedApplicationPackageTests.cs:line 120
at JobTrackerApi.Tests.SubmittedApplicationPackageTests.Cv_changed_between_metadata_and_render_aborts_without_a_partial_package(Boolean deleted) in C:\Users\Connor\.codex\worktrees\frozen-document-integrity\Job tracker\JobTrackerApi.Tests\SubmittedApplicationPackageTests.cs:line 124
--- End of stack trace from previous location ---
Failed JobTrackerApi.Tests.SubmittedApplicationPackageTests.Cv_changed_between_metadata_and_render_aborts_without_a_partial_package(deleted: True) [20 ms]
Error Message:
Assert.Throws() Failure: No exception was thrown
Expected: typeof(Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException)
Stack Trace:
at JobTrackerApi.Tests.SubmittedApplicationPackageTests.Cv_changed_between_metadata_and_render_aborts_without_a_partial_package(Boolean deleted) in C:\Users\Connor\.codex\worktrees\frozen-document-integrity\Job tracker\JobTrackerApi.Tests\SubmittedApplicationPackageTests.cs:line 120
at JobTrackerApi.Tests.SubmittedApplicationPackageTests.Cv_changed_between_metadata_and_render_aborts_without_a_partial_package(Boolean deleted) in C:\Users\Connor\.codex\worktrees\frozen-document-integrity\Job tracker\JobTrackerApi.Tests\SubmittedApplicationPackageTests.cs:line 124
--- End of stack trace from previous location ---
Results File: E:\Storage\Projects\Active\temp docs\jobjakt-local-build\frozen-document-integrity\red\red.trx
Failed! - Failed: 3, Passed: 5, Skipped: 0, Total: 8, Duration: 3 s - JobTrackerApi.Tests.dll (net9.0)
+43
View File
@@ -0,0 +1,43 @@
# Date-picker calendar-day preservation
11 November 2026. Baseline `ec57a24d77854bb416c9ea02b8b371be3e5554c0`, isolated branch `codex/i18n-deadline-picker-date`.
The Add and Edit job dialogs passed date-only strings through `new Date(value)`, which interprets a literal `YYYY-MM-DD` at UTC midnight. In Los Angeles, opening 23 November displayed the 22nd. Their picker change handlers then used `toISOString().slice(0, 10)`. Selecting local midnight on 23 November in Oslo or Kiritimati submitted the 22nd.
The existing wrapper pairs now use installed date-fns `parseISO` for literal dates and `format(value, "yyyy-MM-dd")` for the picker's local Date output. Non-literal parsing retains `new Date(value)`. Empty and invalid picker values retain their existing clearing behaviour. Edit's separate legacy `toDateInputValue` loading policy is preserved.
The affected callers are Add's applied date and deadline, and Edit's applied date, status-change date, reply date, follow-up date and deadline. All seven controls are date pickers backed by date-only state and request values. Revision and submit handlers are retained. There is no backend, timestamp serialization, dependency or migration change.
## Verification
The new component regression drives the actual Add/Edit state and conversion handlers with a picker test double implementing MUI's local-Date callback contract. Each run starts a new Jest process with the recorded timezone. It checks EN/NB values, a full create/save payload, source revision, five Edit/two Add controls, clearing, invalid picker values and an offset timestamp using the existing legacy loading policy.
| Process timezone | Before fix | After fix |
| --- | --- | --- |
| America/Los_Angeles | 5 failed | 5 passed |
| Europe/Oslo | 4 failed, 1 passed | 5 passed |
| Pacific/Kiritimati | 4 failed, 1 passed | 5 passed |
These are the same five cases executed in three environments. The nearby run passed 21 cases across date-picker, edit-revision, quick-capture and native file-input suites, including those same five cases. TypeScript without emit, ESLint on the three changed files and tracked source whitespace checks passed. The cached evidence check flagged the original TypeScript receipt's blank line at EOF. That raw receipt is retained unchanged in `evidence/date-picker-calendar-day/`.
Actual MUI browser interaction, screenshots, native devices and provider/API server verification were not run in this isolated increment. The root integration owner will qualify the rendered picker separately. Final I18N, A3, security and release acceptance remain open.
## Other reviewed date contracts
`CalendarAgenda` validates its date-only response, parses it at UTC noon and explicitly formats in UTC. The reviewed 23 November value stayed on the 23rd in all three zones. Its backend groups by the stored calendar date and explicitly retains the legacy timezone/precision limitation.
`CompaniesTable` formats next-contact and relationship dates using the viewer's local timezone. Its edit form takes the raw first ten characters. Literal `2026-11-23` therefore displays as the 22nd in Los Angeles, while `2026-11-23T23:30:00Z` displays as the 24th in Oslo and opens as the 23rd in the editor. The backend fields remain legacy `DateTime?` with no precision discriminator. The current locale test covers a noon UTC timestamp. These display/loading cases require a qualified date-versus-timestamp contract before a broader change.
The Jobs table similarly formats a literal deadline through local `Date`, and its current test expects that expression. Edit's retained `toDateInputValue` can also move an unspecified midnight timestamp back a day in positive zones. These are separate legacy precision/loading findings. This fix does not claim their closure or reconstruct missing timezone metadata.
Rollback reverts the two wrapper changes and their regression package. Existing persisted dates are not rewritten.
## Follow-up local-today default
From isolated checkpoint `f7c3617e`, Add's `getTodayIso` still selected the UTC date for initialization and a successful form reset. It now uses the already imported `format` with the browser's local Date. No persisted record is rewritten.
Two new component cases fix the UTC clock at `2026-11-23T00:30:00Z` and `2026-11-23T23:30:00Z`. Each timezone's red run had one failure and one pass, with the earlier five cases skipped. The final full suite passed seven cases in each of Los Angeles, Oslo and Kiritimati. That is seven distinct cases repeated in three environments, including the five earlier picker cases. TypeScript and two-file lint passed. The earlier 21-case nearby run belongs to the preceding checkpoint and was not rerun for this one-line follow-up.
The new cases cover initial creation, Create and add another resetting the default, and closing/reopening the resulting draft. Closing and reopening the same mounted dialog preserves its existing step and draft. It does not reset every open. The initial test draft assumed otherwise and was corrected to exercise the actual successful reset path. The only write is the explicit mocked creation request, whose selected date is asserted. API server, actual browser, export build and native checks remain unexecuted.
Follow-up receipts are in `evidence/date-picker-local-today/`. The legacy loading and Companies contract findings above remain open.
+21
View File
@@ -0,0 +1,21 @@
# Rendered date widgets and UI language
11 November 2026. Baseline `d027754d15fadd945e0d9d38e6ca85b50d8dc885`; application source `beb5564223f23e900477955dce244fda72f386c9`, expanded calendar verifier `285d3c88c771054b0ac0b3c95f041339c6cc6ed5` on `codex/a3-ui-10`.
The rendered Bokmål Edit dialog exposed English date sections and calendar controls through the default LocalizationProvider. DateLocalizationProvider now consumes the live inner application I18nProvider and selects installed date-fns English/Bokmål formatting and MUI picker translations. ClientApp retains its existing outer language/error boundary. Router and form identity survive language changes. Generated-content language stays with the document features.
The two new component cases failed before the locale binding and passed afterward. The final affected run passed 25 tests in six suites, including real MUI sections, live language changes, existing date/payload/revision checks, navigation, theme and locale-fetch stability. Full lint, final scoped lint and export/build with TypeScript passed. The first build exposed unsupported Testing Library matcher options in the new test; those options were removed before the successful build.
## Actual widget verification
`scripts/verify-job-date-widgets.mjs` exercised the exported React application with real MUI widgets. Six finite configurations pair English/light/1440 px mouse interaction and Bokmål/dark/390 px touch interaction in each of Los Angeles, Oslo and Kiritimati. They cover all seven Add/Edit controls, live EN↔NB changes with the unsaved title/date retained, stable detail-read count, local today at a fixed UTC boundary, exact version7 Edit payload and exact calendar-day create payload. The desktop calendar closes on selection; the touch calendar uses its explicit OK action.
All six journeys and 18 axe scans/captures passed, with zero page errors or unavailable reads. Captures include the open calendar as well as Add/Edit forms. Twelve intercepted fixture writes were verified. External Google Fonts requests were blocked. These are the defined six configurations, with widths/themes/input modes paired by language. Native devices and a full combinatorial matrix remain separate. The two-case pilot and earlier 12-capture run overlap the final six-case run. Its earlier failures were stale locale-dependent dialog selectors, corrected by binding each dialog to its stable title ID. Failed logs/JSON remain with final evidence. The dark touch calendar and light desktop popup were visually inspected.
Calendar selections and save/create requests used synthetic teacher/school data and intercepted API responses. This verifies the widget-to-payload boundary. Earlier native API/SQLite evidence retains its own source identity and scope. No provider or model was used.
## Remaining localisation work
Legacy timestamp-loading/display precision remains tracked in DATE_PICKER_CALENDAR_DAY.md. Other MUI controls still expose English internal labels, including the company Autocomplete's Clear/Open controls in the Bokmål probe. Those need their shared localisation owner and regression evidence. Broader European locale, native-language, device, integrated security and release acceptance remain open.
Rollback restores the prior provider placement and its default English picker configuration, with affected locale/widget verification required before publication.
+139
View File
@@ -0,0 +1,139 @@
{
"sourceContentCommit": "d2dbe851c03dfba711501af0a36a021e5c3b00f9",
"policy": "LF-normalised source and text evidence; raw PNG/gzip/zip/export. Content binding for offline UI verification; release candidate/image identity remains separate.",
"sources": {
"job-tracker-ui/src/App.tsx": "e0c50ec92954b90cf62b9d8b8e0d5b5802c2408e577e5e255a8d78c3dfdf7135",
"job-tracker-ui/src/ClientApp.tsx": "327cf5f9cf213c4d8146bf6fa2c0c0d20462b71e03ed0d73105823c5f26b0424",
"job-tracker-ui/src/DateLocalizationProvider.tsx": "cb03831c5f2e755585a82935614f8e1b778f563329535b0c9c9a38fb600fcf2c",
"job-tracker-ui/src/i18n/I18nProvider.tsx": "dfa8d95510a7dd872e1b9bbddf9bb4ab31da4d4b5e932b0099397d11e08ad108",
"job-tracker-ui/src/i18n/translations.ts": "a0b706a859264773418ec3f1275332e97add913de65c4d6efaef04b23ae8e1c5",
"job-tracker-ui/src/components/AddJobModal.tsx": "e3b846dc276e8d2a8084bf6e7e670a0639a46eea0788e90afa81722e6028f369",
"job-tracker-ui/src/components/EditJobDialog.tsx": "b6aacbb20f7b924b60c9d8162d7129608cf5af113c7bc2b0bfa68bc685d40430",
"job-tracker-ui/src/date-picker-localisation.test.tsx": "75616ddbe8741628c463d0532656310d6cf96ed71be027690cb54c028cf3108b",
"job-tracker-ui/scripts/verify-job-date-widgets.mjs": "578fbfb24df90406f113ee16352b34aa6a15abdcca7eaf473e8c7a0c49372815",
"job-tracker-ui/package.json": "3745394e406cefa40d30a87e840bccb359be147bac6ffba736e2ae90898a8626",
"job-tracker-ui/package-lock.json": "bf31e90d7af03e8af059db2aaf893925b9e8881a30a1688034ed88ef45d4fcab"
},
"evidence": {
"docs/i18n/evidence/date-widgets/add-America-Los_Angeles-en.png": "438d9c4a58e0d0cd4310deecc0185e29f8df2c81761282f5a5132354d35baa55",
"docs/i18n/evidence/date-widgets/add-America-Los_Angeles-nb.png": "deca21bc4a1bd9267a601e57176d9ae9cd56cf4adac4246d87fba3b8459f10f9",
"docs/i18n/evidence/date-widgets/add-Europe-Oslo-en.png": "1233effc1a3866f05723da0502bce17acbaed73f8bb0e231686c68c281866891",
"docs/i18n/evidence/date-widgets/add-Europe-Oslo-nb.png": "deca21bc4a1bd9267a601e57176d9ae9cd56cf4adac4246d87fba3b8459f10f9",
"docs/i18n/evidence/date-widgets/add-Pacific-Kiritimati-en.png": "523463c6eb3d5c26794d39e840f3fddf2a25bba4787d3bbbb0aef035c12110ef",
"docs/i18n/evidence/date-widgets/add-Pacific-Kiritimati-nb.png": "deca21bc4a1bd9267a601e57176d9ae9cd56cf4adac4246d87fba3b8459f10f9",
"docs/i18n/evidence/date-widgets/BEFORE-CALENDAR-CAPTURE.json": "6b0839f06250b243276d219862b77a0594475ffd2e3825f1b57ad6c3ead661f4",
"docs/i18n/evidence/date-widgets/BEFORE-CALENDAR-CAPTURE.txt": "03e8426093450fff861bc40d8a3105b4ae915412c7ffc2c546dfc5faf444e8d6",
"docs/i18n/evidence/date-widgets/BOKMAL-BEFORE.txt": "fdbd5a73d9fafbe4b2a03d4776d80cdbda85d6b7f73a04d70a24c85294fbc47e",
"docs/i18n/evidence/date-widgets/BROWSER.json": "7b349923cf25578e97ab41962a4d0142772377ccd1d4e3976caa9f50e815ac07",
"docs/i18n/evidence/date-widgets/BROWSER.txt": "f0cc7a53c3991d21d4a71aa757e7770694062d4011b1bcfb6996eaec51a7af07",
"docs/i18n/evidence/date-widgets/BUILD.txt": "c26f282b701f133bad7659595a123b22ba3584b6e7681f13bd74ec23b94dbad0",
"docs/i18n/evidence/date-widgets/calendar-America-Los_Angeles-en.png": "fd0b8100a6e1da2c2bbfd0458be457f5e53af02c3ee9b4269b642de27cc889a2",
"docs/i18n/evidence/date-widgets/calendar-America-Los_Angeles-nb.png": "0a8bc71fc1e6594eb63e2bfaf0e16cf7429064ccd60511324882d66448d14bcf",
"docs/i18n/evidence/date-widgets/calendar-Europe-Oslo-en.png": "0c54b91904908cd3529a12a4be06cab6f121d194f7deab4789666b669626e0e1",
"docs/i18n/evidence/date-widgets/calendar-Europe-Oslo-nb.png": "0e14c3fc57b96540ff895ca83c0c076db87abee3983ffc65cafb571ac883981e",
"docs/i18n/evidence/date-widgets/calendar-Pacific-Kiritimati-en.png": "9d9e1c206d383893d96e62f80c67e39445afe33fc4d684b930c926968a26ccf3",
"docs/i18n/evidence/date-widgets/calendar-Pacific-Kiritimati-nb.png": "0e14c3fc57b96540ff895ca83c0c076db87abee3983ffc65cafb571ac883981e",
"docs/i18n/evidence/date-widgets/edit-America-Los_Angeles-en.png": "ed8f083d2561e0d9e7c4e431847e8a606d461e4de130b1b33359b4e9ef6546f4",
"docs/i18n/evidence/date-widgets/edit-America-Los_Angeles-nb.png": "fd84d04daf9fe2788456e2a62f6bf019cbff0358caef7889ad2c42166cfa1d34",
"docs/i18n/evidence/date-widgets/edit-Europe-Oslo-en.png": "fb79eced4ef8aeb6d6599d9d992ae8e4f854b214fa1762e0567210da11c63fde",
"docs/i18n/evidence/date-widgets/edit-Europe-Oslo-nb.png": "fd84d04daf9fe2788456e2a62f6bf019cbff0358caef7889ad2c42166cfa1d34",
"docs/i18n/evidence/date-widgets/edit-Pacific-Kiritimati-en.png": "528881fc9458ba70557dc4f045589fa3ea804be784a94611dbde8752c467ac92",
"docs/i18n/evidence/date-widgets/edit-Pacific-Kiritimati-nb.png": "fd84d04daf9fe2788456e2a62f6bf019cbff0358caef7889ad2c42166cfa1d34",
"docs/i18n/evidence/date-widgets/FINAL-LINT.txt": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"docs/i18n/evidence/date-widgets/FIRST-BUILD.txt": "37d0824d5f695712a80c767d9e8aaf6926ac78754ed5904f9c1c4234e15fea5b",
"docs/i18n/evidence/date-widgets/FIRST-PILOT.json": "4fc9bae83fc3a97dffb19066b453cdc018a4739a376ef07dbb6bcaffc9603220",
"docs/i18n/evidence/date-widgets/FIRST-PILOT.txt": "92f5d2ef5e9028ce93f922852a810919fa0de10f1d99a75a13931d5d3435859d",
"docs/i18n/evidence/date-widgets/INITIAL-TESTS.txt": "fa82418c16bb28051617330efdb12e6854eaa6f501037fb95576e3d7b63e5162",
"docs/i18n/evidence/date-widgets/LINT.txt": "90b7cd72b55fedd90de76daaa13f3c58e1b051481ede133c1904168b2f6bf6b0",
"docs/i18n/evidence/date-widgets/PILOT.json": "12d2eef862eb6e947c3c9b263e5b72900391546bf8550f8a3833d40b4c0cc515",
"docs/i18n/evidence/date-widgets/PILOT.txt": "a3552d386e24b46902e5f0d405c468e25dbc61db32f476c49302ea916e5b3af6",
"docs/i18n/evidence/date-widgets/RED.txt": "a290b0ba61d73f3e4385cfab22f4cd47f02a4b2c6c3e5c76b7585a641d93d327",
"docs/i18n/evidence/date-widgets/SCRIPT-LINT.txt": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"docs/i18n/evidence/date-widgets/SECOND-PILOT.json": "3acf6576fe44a63dbb5e2b5e6e9c2474f454b26325793ae449d70c3b34c99246",
"docs/i18n/evidence/date-widgets/SECOND-PILOT.txt": "cc7ad4c51d6c9c071195641fec30a7cc1945e8e67c866610bfc9c416251b778c",
"docs/i18n/evidence/date-widgets/TESTS.txt": "f986879f8267b32d4fbc0099c71a09bffedffb5d0fb9b9f0d794a36bb494dee4",
"docs/i18n/DATE_WIDGET_LOCALISATION.md": "f327cb04165acbe70084d930adc5af86681622c3945f8c7c876a69afaea11184"
},
"export": {
"job-tracker-ui/out/404.html": "72e30d3b9ee3fd4130a19f867e5955be376cd9e431895bb6379a1779016ed8ab",
"job-tracker-ui/out/__next.__PAGE__.txt": "c8dd0ec833f803c8affdfd7d003a7d515b57f658e4aba36147476915fbb2d96c",
"job-tracker-ui/out/__next._full.txt": "7fe18a67ad3fcce522792c8f37a0384d024857ed2568ae828644804ef7e89cd3",
"job-tracker-ui/out/__next._tree.txt": "590ca89c962671f90b40c9646423d06404ba7d696f65a4ddd5c9c0f9f6804073",
"job-tracker-ui/out/_next/static/BIErlC84p7XkJNYoDBRWD/_buildManifest.js": "9476e0afd3f9da68a64e6ec58631191fd3eaca62c9f5a377e7517fe9a89e59ea",
"job-tracker-ui/out/_next/static/BIErlC84p7XkJNYoDBRWD/_clientMiddlewareManifest.js": "5483e3295dc35dfe29ea932efc85341c916f10ff78ff5b0e965e003710893a4a",
"job-tracker-ui/out/_next/static/BIErlC84p7XkJNYoDBRWD/_ssgManifest.js": "678f6ce2cb80b1fe72fc67e7412be6e2ab6ada083111b64f7c40d35e3cba5e00",
"job-tracker-ui/out/_next/static/chunks/0--vhwu1kmht5.js": "426fae50ff0485fb6af8e28a7d1751dfaefcb6118a24dab39b3a7099d38c18dd",
"job-tracker-ui/out/_next/static/chunks/0-_aq6ipzcshj.js": "39f90c215bf52aef6274ed3dcc0f8af32e21a37fe8845b3bca0d6ea60da6a8c2",
"job-tracker-ui/out/_next/static/chunks/00sdty8f_wo6_.js": "047077072852a0211c228c75d5e29836ba970771382ba0da4bac9fdd0bfac29f",
"job-tracker-ui/out/_next/static/chunks/021jk107jdn29.js": "aedfd5b422819006c59c634e5907edddb176beaea2d899a5cc9984285bb232d3",
"job-tracker-ui/out/_next/static/chunks/02pclekj6sxrn.js": "8de8b7bb4d26955b3f334c338dc6c2cdef52b134b6be2396e9c5cf85c3657448",
"job-tracker-ui/out/_next/static/chunks/05rgc5zsm9p5i.js": "1da7305ab9d8e95795327cce11a16db4c56d90429d7e1dea4c4088c085f20d69",
"job-tracker-ui/out/_next/static/chunks/0afg9bl7a8pj5.js": "bd0cec651950d5007f84273af5be8d8255806da4db54238f5c512442c7449459",
"job-tracker-ui/out/_next/static/chunks/0ah1fzqmie05a.js": "5c65505a7dcbac63ac71d254e619a4c800943bfb4fa16cd40c10cdbe7e9b6e31",
"job-tracker-ui/out/_next/static/chunks/0b3nrgli_w65x.js": "bf23e1a05a97de3a950e76eb206b2fbbe9003460891ecd5ab091155aa385badc",
"job-tracker-ui/out/_next/static/chunks/0bdt7ln111t48.js": "6184b1a61f22e2a640c17941634fdc75678ce93d142848d9f07f163e0d1fd201",
"job-tracker-ui/out/_next/static/chunks/0cz1d0mv5g_q7.js": "0973c1d64c88adc8e3c950410cb58b288f72118d5965b78049438deb8f2f9683",
"job-tracker-ui/out/_next/static/chunks/0og1crk78cn4a.js": "aa5031605cb2f46a612e94d9d71473f48a71aa4a5acfe8fe5f2dfc66d951ab40",
"job-tracker-ui/out/_next/static/chunks/0pvbhqkwp15b6.js": "f210cc1a8c203de6008b6137c0324afabb33f233c1ab56800fe9e6c963c0c34a",
"job-tracker-ui/out/_next/static/chunks/0q20g9o8x-k8z.js": "3d4d9086985419aa8866b032158196c774acb8bf318f3c8245508c8daa9c5148",
"job-tracker-ui/out/_next/static/chunks/0q_pwu9jxv4-d.js": "42a43b3d7245a413c7c29b4cbe87823037a6dbc637e2e282c92405470e0f575d",
"job-tracker-ui/out/_next/static/chunks/0qxm1h9uqfgpa.js": "a5af3192c76f3b8dac5c17afbe90d96a5c7652810ce69b7f39fbcf1f1231fd8c",
"job-tracker-ui/out/_next/static/chunks/0ta69108309yl.js": "07b8069d13ddaf7aaa24daf1070144a2e98b444ca162b628b8bfb8e1068d2967",
"job-tracker-ui/out/_next/static/chunks/0vc71o0a3bvk-.js": "12a1d8489f5ef9f9cdc09d56b91d881f73228a0c4743a72c6320f253bf638402",
"job-tracker-ui/out/_next/static/chunks/0zep-wknuuxm3.js": "cc13d0dc908a4b047a165a50a226b787ee7707135b3f2c5894aab8cbd618a29c",
"job-tracker-ui/out/_next/static/chunks/1-8ykqf0n88k2.js": "eb208124ec838cb9a9ae366745bd81fba1c35eeceac128c567bbb7977ebb6a2e",
"job-tracker-ui/out/_next/static/chunks/10qxrc5f9nore.js": "512bfc44077703fa4e03e15ca48acf3501231c5e2cb9bf7beb2ec30224fe287b",
"job-tracker-ui/out/_next/static/chunks/1435in_k_-dhc.js": "4757476512d5c4dd25f9264a18b304ed4824b23aff1c09e134fcbff21d9c36b6",
"job-tracker-ui/out/_next/static/chunks/145oicobmjvm7.js": "d6bbbdeed1381652dc7412e5ce673a077579ecd5cee8934cb6b533c814098764",
"job-tracker-ui/out/_next/static/chunks/14e7ovj0s3elm.js": "070567dd4d23fb9fa0b9ba132185a697af6578045d43b2b1c7bbb2a1594d935d",
"job-tracker-ui/out/_next/static/chunks/15saa2mllmdp7.js": "6664972f61e1669d2ca1ed9af467df87d316efeadf59496ce8c68d8c84fd7512",
"job-tracker-ui/out/_next/static/chunks/16lfece6cnlln.js": "716363ba044f30b0e70440756b4e5eafa2d25f2e1628e855e3bc4ab9eabee3d3",
"job-tracker-ui/out/_next/static/chunks/188aj_hr_p4eq.js": "f10e4af24bebfb8b9e7306a5688ef45f07f280c920dc01815ac578fbe18e2182",
"job-tracker-ui/out/_next/static/chunks/19ohoqj_a5joe.js": "20a17b75c133c7b3e389c290ec1bf09395700cda12ea3ab1210592a8b4c2d573",
"job-tracker-ui/out/_next/static/chunks/1avt7t-z_q80r.js": "8cbd919d971eaf46e508f9e6b073a9183a29457c3c42c9566ec74b018894c061",
"job-tracker-ui/out/_next/static/chunks/1ke8pzecwofrg.css": "32e9ab963ebadebcbe2c68dffb24cfbfc8cc24255ad91b48b7941fc777bf7439",
"job-tracker-ui/out/_next/static/chunks/1req80b9jj38r.js": "eee4d2e3789e1215195dba99ad0f09d34f8c83da848322b76054722233e9dae3",
"job-tracker-ui/out/_next/static/chunks/1svx1qnrof8xa.js": "111c502a1ca939f7bf971ed1b3e3cda011b130c313a7f7df742ae4b1e14486ae",
"job-tracker-ui/out/_next/static/chunks/1w3q8yrcjn2jk.js": "365200e241ee9290e93d7c2fdba169f2b0a8e774541e3cbf3f6f2781110fa644",
"job-tracker-ui/out/_next/static/chunks/1xn023_28rgzj.js": "4f7f2108d030a48d039930736c92a55710975dddf419fe673f84d92899b1f575",
"job-tracker-ui/out/_next/static/chunks/1xwc-b795syag.css": "f73c002233f931ca14d0ae58815c47ca9f2659f25b1547a6d1fdca12a2a00b3f",
"job-tracker-ui/out/_next/static/chunks/22wx8--1b9tnq.js": "3f7d8e762371262c6a740b4dac21725b424847319afac46055e97074a99ff5f5",
"job-tracker-ui/out/_next/static/chunks/274f8aifi_wup.js": "bc5bc5617b4acbbbb1c7966322c508bb5554024d78e71e523e27f7f5bba5a8ab",
"job-tracker-ui/out/_next/static/chunks/29jdblthf0w7a.js": "e84b4f7c225d58099aa89999544f7318cb23c0c71f2385c31b3c9181a62ab7f0",
"job-tracker-ui/out/_next/static/chunks/2edynwqicn6ym.js": "c694af15bb117955e6f7f0962f47495b2be017dfdea93b6d4ede4d5e4fcd7efd",
"job-tracker-ui/out/_next/static/chunks/2ha8nlh4rv92e.js": "5cb4b4cee708f35ed54ef6289269fa866afbaa64fd47bf9275ff691664d3132e",
"job-tracker-ui/out/_next/static/chunks/2php_9zkhprrb.js": "4f91e63b1181693e7a3b269b1b24004d9abee13413b8396a916799b8c01620e8",
"job-tracker-ui/out/_next/static/chunks/2pllop_q35dkw.js": "21c9a7c53763010317a2d09460066017e1fd192691e6a0bd83ed20ea6e1fe9dd",
"job-tracker-ui/out/_next/static/chunks/2s2gjr6ro25dj.js": "7489e2ac285a05b563c2bcb4eaf9274bf930f2a7385f17d6d5e6850064b88b10",
"job-tracker-ui/out/_next/static/chunks/2vt8x99fh3ag7.js": "99d883591b1bbcd3c49758bd0bdbbc46af66eae5ecf0a75657fa1585d29d8f4c",
"job-tracker-ui/out/_next/static/chunks/30-q9iimktiql.js": "db89d5da1f101216d7f99e9518dc7f5ed84f8b40ac84a6ea55f73c01bf1aca1b",
"job-tracker-ui/out/_next/static/chunks/315n7snxk3fgw.js": "1993429aafa0753d5e9fd1ca63a4f6079ce7f7f37faa44716191f59c972c4d41",
"job-tracker-ui/out/_next/static/chunks/33at73n-mzpir.js": "1605cc3311cc6ca3b2f44b2e0217fe5441a2799fa32b0b0c94d7634f4861ac83",
"job-tracker-ui/out/_next/static/chunks/357yqeklh7414.js": "0192ab7198ebbe299d34a934c504dd4b562d1cccebb6e9d4c5163930c881b14c",
"job-tracker-ui/out/_next/static/chunks/35pq4ixaltkev.js": "f1e9d53e2481de01d7b04aa938397c2545b7d7dc42ce3d0ac5cd3f1141bda182",
"job-tracker-ui/out/_next/static/chunks/3_4dnl1l3809g.js": "50288c2864a9e8c7ef25c93762edb86e4e617778737b5b5a028c53c5b93d3b70",
"job-tracker-ui/out/_next/static/chunks/3bqn9b34na0b9.js": "f1b8ebcc1b22251c33c9f3a5d11c1f893cad2da2e8744a441e790f7347aa9e73",
"job-tracker-ui/out/_next/static/chunks/3gkylew_qn7cw.js": "7e6c74c81be0c41ab433e36d65b914f62c48f332922e27f7cc953476f4dd78cb",
"job-tracker-ui/out/_next/static/chunks/3kwbf09xml2_g.js": "5d31587f866b3d52004ac4cb50a1c70961cb2fb60253e40d6374ba38ad9bdb9d",
"job-tracker-ui/out/_next/static/chunks/3la4obitihzpv.js": "9a5a57b46db389b98f1fd5baa86eba1a360300dae9d1ff3ffeae7bc3378135b9",
"job-tracker-ui/out/_next/static/chunks/3q4v10rrqy40n.js": "d87ff7b7a0f07d0d92eab3876481eb686a12d9c43d8b92cbe406aefa227a11c1",
"job-tracker-ui/out/_next/static/chunks/3xbcgmkebja05.js": "bb1e98db2657a4b067676ead2ea26172478a79c1b384a9f9fe6588e1481d33ee",
"job-tracker-ui/out/_next/static/chunks/turbopack-25fzg9ay_1ss-.js": "c40f39e993a30c96d6247bb3a7b581538bb046efe8ef8ec07bea3ba32291750f",
"job-tracker-ui/out/_not-found/__next._full.txt": "620a8e0060f623db56f9586c08c20827052d8e170217286b215fa74a263c69d1",
"job-tracker-ui/out/_not-found/__next._not-found/__PAGE__.txt": "01c749ccbd567d270280feb8ef04173ce82ac43db9599844101d7b7b12d0a2ef",
"job-tracker-ui/out/_not-found/__next._tree.txt": "bcf0e8ae1b690a70bdfed9f0178f337f86be732f4ed98d56a9d7ec0b92e944ee",
"job-tracker-ui/out/_not-found.html": "72e30d3b9ee3fd4130a19f867e5955be376cd9e431895bb6379a1779016ed8ab",
"job-tracker-ui/out/_not-found.txt": "620a8e0060f623db56f9586c08c20827052d8e170217286b215fa74a263c69d1",
"job-tracker-ui/out/favicon.ico": "8d4923e2c487805ea9f852a685b2cf0fb10512ded8290d2a5c37d1cc8fd0702a",
"job-tracker-ui/out/favicon.svg": "63ecf8f37f8bb146ecfae3917187c6b692d4e801641e8a314486a4007912f3a3",
"job-tracker-ui/out/index.html": "71e631255ccd0b7145f7737107f5c056be15656487194a732574e4c832a178a2",
"job-tracker-ui/out/index.txt": "7fe18a67ad3fcce522792c8f37a0384d024857ed2568ae828644804ef7e89cd3",
"job-tracker-ui/out/logo192.png": "bc652031597fabd19b9f14e22a71f809490d218037521b3b64d013418c1390b4",
"job-tracker-ui/out/logo512.png": "5129e9bc1ba812b711fd9a646d3a38a906d365a26ee685df183b8752458944d4",
"job-tracker-ui/out/manifest.json": "2667ce63c2e0835b7007239816a5597639321d333da396a78638dfa9e8a7c935",
"job-tracker-ui/out/mockups/dashboard.svg": "318be3adb1324c26eb30f05572a29528a3e38331c6a604652eef75b54f8c29dc",
"job-tracker-ui/out/mockups/pipeline.svg": "db2ef41faabeb90e7ce85413e168581686c54535d970fc7d8deda3888a479de4",
"job-tracker-ui/out/mockups/workspace.svg": "9bbab22349147c7b01d6e18c93c622a6f8aed6dedaf23715189ab54d60e1ddbd",
"job-tracker-ui/out/robots.txt": "e5ab0d231eeb01b4a982d1c79a6729cac9797ad15a69247e4f28ba6afc149b4c"
}
}
+15
View File
@@ -0,0 +1,15 @@
# Discovery title order follows UI language
10 November 2026. Baseline `aac0f3b3a9b452b48126ddaf26e1837eeadd2f6d`, branch `codex/i18n-discovery-order`.
The title sort used the browser's default collation and its memo omitted the UI language. Switching English to Bokmål left Å-prefixed titles before Z. The focused regression reproduced this on the unchanged baseline.
`JobDiscoveryPage` now passes its existing English/Bokmål display locale to `localeCompare` and includes that locale in the memo dependencies. The same locale still formats recorded timestamps. The change reaches ordinary and saved-search result arrays through their shared title sort.
The regression switches English → Bokmål → English with Å/Z fixtures, verifies each order, and checks that the role, municipality and title sort remain selected. Switching adds zero API reads and triggers no writes. Existing submitted search and provider contracts are retained.
24 tests in `job-discovery.test.tsx`, TypeScript without emit and ESLint on the two changed files passed. The initial red run selected one test and skipped the other 23. It is retained separately from the final suite result in `evidence/discovery-title-order/`.
Export build, rendered browser, native language/device and live-provider verification were not run in this isolated increment. Existing whole-application, European locale and final acceptance gates remain open. The root integration owner will review and publish this branch separately.
Rollback is the inverse of the two source/test changes. There are no schema, dependency or persisted-data changes.
+11
View File
@@ -0,0 +1,11 @@
# Import and conversion count wording
Source/verifier44112c7c, baseline50e16ff2. Existing plural keys remain unchanged. New `importedJob` and `convertedRow` keys select the one-item form in EN/NB; zero/multiple values retain existing plural wording. Import payloads, partial-success reporting, Last import count, CSV parser and native download bytes are unchanged. These are two finite literal-key choices, not unbounded runtime translation keys.
PASSED:50 cases/six suites (import/export lifetime, Settings lifetime, picker activation, catalogue parity/placeholders and language provider). Four new language-table cases cover zero/one/two conversion and one/two import messages; earlier35/39 cases overlap. RED receipts retain the two conversion and two import language failures before the fix. Build/TypeScript and scoped ESLint passed.
PASSED:two paired actual-UI profiles (390/Bokmål/dark touch and1440/English/light mouse), each converting zero/one/two rows. Six native JSON downloads and six accessibility scans passed, with exact output row counts/Unicode and visible singular/plural messages. No API writes, page exceptions or unavailable fixture reads; fonts blocked. This is a finite copy-focused matrix, not the full export/session matrix. The earlier36-download package retains its original source identity. Mobile Bokmål singular feedback was visually inspected.
Evidence: `evidence/import-conversion-counts/`; source/evidence/export content bindings: `IMPORT_CONVERSION_COUNT_SOURCE_MANIFEST.json`. Native gzip receipts preserve downloaded bytes. No physical CV/PDF, API, dependency, schema or additional locale changed.
Rollback reverts44112c7c and reruns affected copy/catalogue checks. Full visible-text, native-language/European, physical-device, integrated security and final release acceptance remain open. Historical188 dynamic reviews remain historical; this report accounts for the two changed finite choices on the current source.
@@ -0,0 +1,126 @@
{
"sourceContentCommit": "44112c7cbb480f96e0c9ca7a5dbed35650aa3a53",
"policy": "LF-normalised source and text evidence; raw PNG/gzip/zip/export. Content binding for offline UI verification; release candidate/image identity remains separate.",
"sources": {
"job-tracker-ui/src/App.tsx": "e0c50ec92954b90cf62b9d8b8e0d5b5802c2408e577e5e255a8d78c3dfdf7135",
"job-tracker-ui/src/components/SettingsView.tsx": "250b80b2d18f82ab63e7341c0779380345f386acc0d89161064d8e41439636d2",
"job-tracker-ui/src/components/ImportExportJobs.tsx": "1f3a9ed936bccd35e69bf05449f3615af5b7e8f3bf1fb52b99b39a3ae6cb253b",
"job-tracker-ui/src/sessionBoundary.ts": "09edca77c98294818d2ad0c409ce595f1b6ca0c1f60419fc1078b70f96f5b7b9",
"job-tracker-ui/src/api.ts": "f75ac7be07a743396843ddad5fd3371e2d5fd3d9c5d933e09e26fae2159f2378",
"job-tracker-ui/src/i18n/I18nProvider.tsx": "dfa8d95510a7dd872e1b9bbddf9bb4ab31da4d4b5e932b0099397d11e08ad108",
"job-tracker-ui/src/i18n/translations.ts": "1bef579f8bb593ed3a888780e9d3472531f93b5602a6e92d9df8fa062060c1e9",
"job-tracker-ui/src/job-export-lifetime.test.tsx": "c49f392f8fb15ade6f87bd1515f124432a3768c9b9ff0b5a3e4fd71bdeefa06c",
"job-tracker-ui/src/job-import.test.tsx": "2baa932bb464490d12cddfafcbea2d19795725519f380d7db803369a71c0288d",
"job-tracker-ui/src/settings-import-lifetime.test.tsx": "24af2a5954533087ddbff40659ff9f8dcf70805435dca772314c2be7d8bf6133",
"job-tracker-ui/src/file-picker-activation.test.tsx": "accb87f94a7b36373eec6583a391a17ede7e7779cf18d81d4a86b43e0bd6afbe",
"job-tracker-ui/scripts/verify-job-export-lifetime.mjs": "bea2e5a04b7cad4f2af6b1dd2e19c505261ab171dcd686b6662332aa2b838646",
"job-tracker-ui/src/translation-catalogue.test.ts": "f20e8099f2c4dda76574ea3ed1e95b743eef5da82507d7cbc3f9925d52356b98",
"job-tracker-ui/src/i18n-provider.test.tsx": "520b8559ebd0c206649008c98705c24bd8caac7e00a642984a75b11f7d851fbe",
"job-tracker-ui/package.json": "3745394e406cefa40d30a87e840bccb359be147bac6ffba736e2ae90898a8626",
"job-tracker-ui/package-lock.json": "bf31e90d7af03e8af059db2aaf893925b9e8881a30a1688034ed88ef45d4fcab"
},
"evidence": {
"docs/i18n/evidence/import-conversion-counts/1440-en-light-converted-0.gz": "50c15d921df28492e2d9bf2da5d1cd5c78ee416bdcf478074db63d76d2381cba",
"docs/i18n/evidence/import-conversion-counts/1440-en-light-converted-1.gz": "2117b71f51b07c27587ff99d40acd9ef43b538a62658ce52ea060ab4a3c2cba5",
"docs/i18n/evidence/import-conversion-counts/1440-en-light-converted-2.gz": "7f3553fe748c77febb7f3b312d152002e44c1608e05e99b05b7865dac0154326",
"docs/i18n/evidence/import-conversion-counts/1440-en-light-count-0.png": "06b4cae9dcda200767c296e923ed5e8eac7bdb53721c1f55ffda9bb0b7c06d63",
"docs/i18n/evidence/import-conversion-counts/1440-en-light-count-1.png": "7c764a401d219d2bd5452bea3380067d396ca0ffc052b06bdd2bfa705c346aea",
"docs/i18n/evidence/import-conversion-counts/1440-en-light-count-2.png": "8b901708e2b8c8bea11765970539b0d28383e30a5243dc6ddd4c59ddb8a87135",
"docs/i18n/evidence/import-conversion-counts/390-nb-dark-converted-0.gz": "50c15d921df28492e2d9bf2da5d1cd5c78ee416bdcf478074db63d76d2381cba",
"docs/i18n/evidence/import-conversion-counts/390-nb-dark-converted-1.gz": "2117b71f51b07c27587ff99d40acd9ef43b538a62658ce52ea060ab4a3c2cba5",
"docs/i18n/evidence/import-conversion-counts/390-nb-dark-converted-2.gz": "7f3553fe748c77febb7f3b312d152002e44c1608e05e99b05b7865dac0154326",
"docs/i18n/evidence/import-conversion-counts/390-nb-dark-count-0.png": "05b956b3267ccc9e1c928aaa987241c2d4f9db9f058a5d5de0124d1dc261d10c",
"docs/i18n/evidence/import-conversion-counts/390-nb-dark-count-1.png": "cc3403c35dcb011bde0a68d2b713c65a237abc6bd6fb449b0bbdb7029b32b3fb",
"docs/i18n/evidence/import-conversion-counts/390-nb-dark-count-2.png": "9292a818d04ccd97f709d1b6289b4cafbdf781b09a3e8156ed9cf0304a288154",
"docs/i18n/evidence/import-conversion-counts/BROWSER.json": "b7e83a199c70ff58c93211a1d04da0630470927b3f4c22cf6cc8a4056b10d871",
"docs/i18n/evidence/import-conversion-counts/BROWSER.txt": "be89f2d03cc906ed953e6f6fb4f90a936b67633b7947d8d59a5c091a0ee8a649",
"docs/i18n/evidence/import-conversion-counts/BUILD.txt": "b5633b37143e111a2fd97dc5b7b0fc244c7587fc5c0c11c245f6c4c1f2e98dbc",
"docs/i18n/evidence/import-conversion-counts/LINT.txt": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"docs/i18n/evidence/import-conversion-counts/RED_CONVERSION.txt": "411b9f558921d6f85643511b433c82b9289b94c874ca267a192040a965f70170",
"docs/i18n/evidence/import-conversion-counts/RED_IMPORT.txt": "148548165d5b601ea43c765a07d7f61f2d7b1fb5264377672a0bdd6c8fe9a32b",
"docs/i18n/evidence/import-conversion-counts/TESTS.txt": "a1efa7de41b86d6d48b70c97da55e9ef0e9de9fcce4ca9d5036787bcc198b8d7",
"docs/i18n/IMPORT_CONVERSION_COUNTS.md": "a6fcfb1b918bfd0cd9ba235d27675a8829621265f83d96dc09b93c792bbfdcba"
},
"export": {
"job-tracker-ui/out/404.html": "1ca05af72181de6df1a2b0c07fe5d151fb058344c07e204a78648db4ec1c6e64",
"job-tracker-ui/out/__next.__PAGE__.txt": "838e2b173fe18664741187b814d92cb9006a97f70f2e61d866e88090dbe531c8",
"job-tracker-ui/out/__next._full.txt": "dae993f53affb686262f8ae8e75718ef05b43cdf4b9b7391376fe8ca4fe41ccf",
"job-tracker-ui/out/__next._tree.txt": "ce646215db5c8507b39e758174f959ab2416b6d8fbe67b5f4d14103db564fc13",
"job-tracker-ui/out/_next/static/chunks/0--vhwu1kmht5.js": "426fae50ff0485fb6af8e28a7d1751dfaefcb6118a24dab39b3a7099d38c18dd",
"job-tracker-ui/out/_next/static/chunks/0-_aq6ipzcshj.js": "39f90c215bf52aef6274ed3dcc0f8af32e21a37fe8845b3bca0d6ea60da6a8c2",
"job-tracker-ui/out/_next/static/chunks/05rgc5zsm9p5i.js": "1da7305ab9d8e95795327cce11a16db4c56d90429d7e1dea4c4088c085f20d69",
"job-tracker-ui/out/_next/static/chunks/0afg9bl7a8pj5.js": "bd0cec651950d5007f84273af5be8d8255806da4db54238f5c512442c7449459",
"job-tracker-ui/out/_next/static/chunks/0ah1fzqmie05a.js": "5c65505a7dcbac63ac71d254e619a4c800943bfb4fa16cd40c10cdbe7e9b6e31",
"job-tracker-ui/out/_next/static/chunks/0b3nrgli_w65x.js": "bf23e1a05a97de3a950e76eb206b2fbbe9003460891ecd5ab091155aa385badc",
"job-tracker-ui/out/_next/static/chunks/0bdt7ln111t48.js": "6184b1a61f22e2a640c17941634fdc75678ce93d142848d9f07f163e0d1fd201",
"job-tracker-ui/out/_next/static/chunks/0cz1d0mv5g_q7.js": "0973c1d64c88adc8e3c950410cb58b288f72118d5965b78049438deb8f2f9683",
"job-tracker-ui/out/_next/static/chunks/0og1crk78cn4a.js": "aa5031605cb2f46a612e94d9d71473f48a71aa4a5acfe8fe5f2dfc66d951ab40",
"job-tracker-ui/out/_next/static/chunks/0pvbhqkwp15b6.js": "f210cc1a8c203de6008b6137c0324afabb33f233c1ab56800fe9e6c963c0c34a",
"job-tracker-ui/out/_next/static/chunks/0q20g9o8x-k8z.js": "3d4d9086985419aa8866b032158196c774acb8bf318f3c8245508c8daa9c5148",
"job-tracker-ui/out/_next/static/chunks/0q_pwu9jxv4-d.js": "42a43b3d7245a413c7c29b4cbe87823037a6dbc637e2e282c92405470e0f575d",
"job-tracker-ui/out/_next/static/chunks/0qxm1h9uqfgpa.js": "a5af3192c76f3b8dac5c17afbe90d96a5c7652810ce69b7f39fbcf1f1231fd8c",
"job-tracker-ui/out/_next/static/chunks/0ta69108309yl.js": "07b8069d13ddaf7aaa24daf1070144a2e98b444ca162b628b8bfb8e1068d2967",
"job-tracker-ui/out/_next/static/chunks/0v1q0j141eyli.js": "8180d4c9dae8da28079152624739b603d6d63c62554015b1bf9a82cd5bbdc5fb",
"job-tracker-ui/out/_next/static/chunks/0vo4_3wcjs1gy.js": "5e4d305fc346f9ecf48328ab0416397d8bd7c100f0de99dea16d2a1463b386a9",
"job-tracker-ui/out/_next/static/chunks/0zep-wknuuxm3.js": "cc13d0dc908a4b047a165a50a226b787ee7707135b3f2c5894aab8cbd618a29c",
"job-tracker-ui/out/_next/static/chunks/1-8ykqf0n88k2.js": "eb208124ec838cb9a9ae366745bd81fba1c35eeceac128c567bbb7977ebb6a2e",
"job-tracker-ui/out/_next/static/chunks/10qxrc5f9nore.js": "512bfc44077703fa4e03e15ca48acf3501231c5e2cb9bf7beb2ec30224fe287b",
"job-tracker-ui/out/_next/static/chunks/1435in_k_-dhc.js": "4757476512d5c4dd25f9264a18b304ed4824b23aff1c09e134fcbff21d9c36b6",
"job-tracker-ui/out/_next/static/chunks/145oicobmjvm7.js": "d6bbbdeed1381652dc7412e5ce673a077579ecd5cee8934cb6b533c814098764",
"job-tracker-ui/out/_next/static/chunks/14e7ovj0s3elm.js": "070567dd4d23fb9fa0b9ba132185a697af6578045d43b2b1c7bbb2a1594d935d",
"job-tracker-ui/out/_next/static/chunks/15saa2mllmdp7.js": "6664972f61e1669d2ca1ed9af467df87d316efeadf59496ce8c68d8c84fd7512",
"job-tracker-ui/out/_next/static/chunks/16lfece6cnlln.js": "716363ba044f30b0e70440756b4e5eafa2d25f2e1628e855e3bc4ab9eabee3d3",
"job-tracker-ui/out/_next/static/chunks/188aj_hr_p4eq.js": "f10e4af24bebfb8b9e7306a5688ef45f07f280c920dc01815ac578fbe18e2182",
"job-tracker-ui/out/_next/static/chunks/1avt7t-z_q80r.js": "8cbd919d971eaf46e508f9e6b073a9183a29457c3c42c9566ec74b018894c061",
"job-tracker-ui/out/_next/static/chunks/1ke8pzecwofrg.css": "32e9ab963ebadebcbe2c68dffb24cfbfc8cc24255ad91b48b7941fc777bf7439",
"job-tracker-ui/out/_next/static/chunks/1pcns9f7sl8yk.js": "1a1b889438e68c64984964627315e806548b753c40c6c012c706ca03edc3dfe5",
"job-tracker-ui/out/_next/static/chunks/1req80b9jj38r.js": "eee4d2e3789e1215195dba99ad0f09d34f8c83da848322b76054722233e9dae3",
"job-tracker-ui/out/_next/static/chunks/1svx1qnrof8xa.js": "111c502a1ca939f7bf971ed1b3e3cda011b130c313a7f7df742ae4b1e14486ae",
"job-tracker-ui/out/_next/static/chunks/1w3q8yrcjn2jk.js": "365200e241ee9290e93d7c2fdba169f2b0a8e774541e3cbf3f6f2781110fa644",
"job-tracker-ui/out/_next/static/chunks/1xn023_28rgzj.js": "4f7f2108d030a48d039930736c92a55710975dddf419fe673f84d92899b1f575",
"job-tracker-ui/out/_next/static/chunks/1xwc-b795syag.css": "f73c002233f931ca14d0ae58815c47ca9f2659f25b1547a6d1fdca12a2a00b3f",
"job-tracker-ui/out/_next/static/chunks/22wx8--1b9tnq.js": "3f7d8e762371262c6a740b4dac21725b424847319afac46055e97074a99ff5f5",
"job-tracker-ui/out/_next/static/chunks/24_cq-0_-oa1r.js": "f81cb5ffd0dd4a953d0dc48610226123cc99d577371809ed8c73b901a7bb988c",
"job-tracker-ui/out/_next/static/chunks/274f8aifi_wup.js": "bc5bc5617b4acbbbb1c7966322c508bb5554024d78e71e523e27f7f5bba5a8ab",
"job-tracker-ui/out/_next/static/chunks/2edynwqicn6ym.js": "c694af15bb117955e6f7f0962f47495b2be017dfdea93b6d4ede4d5e4fcd7efd",
"job-tracker-ui/out/_next/static/chunks/2ha8nlh4rv92e.js": "5cb4b4cee708f35ed54ef6289269fa866afbaa64fd47bf9275ff691664d3132e",
"job-tracker-ui/out/_next/static/chunks/2php_9zkhprrb.js": "4f91e63b1181693e7a3b269b1b24004d9abee13413b8396a916799b8c01620e8",
"job-tracker-ui/out/_next/static/chunks/2pllop_q35dkw.js": "21c9a7c53763010317a2d09460066017e1fd192691e6a0bd83ed20ea6e1fe9dd",
"job-tracker-ui/out/_next/static/chunks/2s2gjr6ro25dj.js": "7489e2ac285a05b563c2bcb4eaf9274bf930f2a7385f17d6d5e6850064b88b10",
"job-tracker-ui/out/_next/static/chunks/2sj7ufu_6n9af.js": "69d7471c01b8001d52abc848910a8c35457dac3aaa4ce66eae2a03ca29ad623b",
"job-tracker-ui/out/_next/static/chunks/2vo512u2siee8.js": "30cdfec178acfe49c1e0ae28fb23c67507bcf7f7b17008b48c05198f638c348d",
"job-tracker-ui/out/_next/static/chunks/30-q9iimktiql.js": "db89d5da1f101216d7f99e9518dc7f5ed84f8b40ac84a6ea55f73c01bf1aca1b",
"job-tracker-ui/out/_next/static/chunks/315n7snxk3fgw.js": "1993429aafa0753d5e9fd1ca63a4f6079ce7f7f37faa44716191f59c972c4d41",
"job-tracker-ui/out/_next/static/chunks/33at73n-mzpir.js": "1605cc3311cc6ca3b2f44b2e0217fe5441a2799fa32b0b0c94d7634f4861ac83",
"job-tracker-ui/out/_next/static/chunks/357yqeklh7414.js": "0192ab7198ebbe299d34a934c504dd4b562d1cccebb6e9d4c5163930c881b14c",
"job-tracker-ui/out/_next/static/chunks/35_dl1zkx1-zx.js": "69bb75afcbd9ea9c0b7e870eb2b5796bdb24d0db7810573302fa04a497cc2c8c",
"job-tracker-ui/out/_next/static/chunks/35pq4ixaltkev.js": "f1e9d53e2481de01d7b04aa938397c2545b7d7dc42ce3d0ac5cd3f1141bda182",
"job-tracker-ui/out/_next/static/chunks/3_4dnl1l3809g.js": "50288c2864a9e8c7ef25c93762edb86e4e617778737b5b5a028c53c5b93d3b70",
"job-tracker-ui/out/_next/static/chunks/3bqn9b34na0b9.js": "f1b8ebcc1b22251c33c9f3a5d11c1f893cad2da2e8744a441e790f7347aa9e73",
"job-tracker-ui/out/_next/static/chunks/3gkylew_qn7cw.js": "7e6c74c81be0c41ab433e36d65b914f62c48f332922e27f7cc953476f4dd78cb",
"job-tracker-ui/out/_next/static/chunks/3la4obitihzpv.js": "9a5a57b46db389b98f1fd5baa86eba1a360300dae9d1ff3ffeae7bc3378135b9",
"job-tracker-ui/out/_next/static/chunks/3nzgmz1t8r_64.js": "de2688cd6bfc8b411e9445d857cd1d776f9960f9d63e9f06c24cbc2a98040fca",
"job-tracker-ui/out/_next/static/chunks/3q4v10rrqy40n.js": "d87ff7b7a0f07d0d92eab3876481eb686a12d9c43d8b92cbe406aefa227a11c1",
"job-tracker-ui/out/_next/static/chunks/3xbcgmkebja05.js": "bb1e98db2657a4b067676ead2ea26172478a79c1b384a9f9fe6588e1481d33ee",
"job-tracker-ui/out/_next/static/chunks/turbopack-25fzg9ay_1ss-.js": "c40f39e993a30c96d6247bb3a7b581538bb046efe8ef8ec07bea3ba32291750f",
"job-tracker-ui/out/_next/static/KgOLT2oshx6dHtAKI5NUs/_buildManifest.js": "9476e0afd3f9da68a64e6ec58631191fd3eaca62c9f5a377e7517fe9a89e59ea",
"job-tracker-ui/out/_next/static/KgOLT2oshx6dHtAKI5NUs/_clientMiddlewareManifest.js": "5483e3295dc35dfe29ea932efc85341c916f10ff78ff5b0e965e003710893a4a",
"job-tracker-ui/out/_next/static/KgOLT2oshx6dHtAKI5NUs/_ssgManifest.js": "678f6ce2cb80b1fe72fc67e7412be6e2ab6ada083111b64f7c40d35e3cba5e00",
"job-tracker-ui/out/_not-found/__next._full.txt": "37dee5851fdf4b1bbaabdc143380d8dffdd07999a2c08756fd68b713ecc4826f",
"job-tracker-ui/out/_not-found/__next._not-found/__PAGE__.txt": "001d6c3334faac5f519229a9ad22a7f8b9999e853165ea57cefdf069bd79b554",
"job-tracker-ui/out/_not-found/__next._tree.txt": "023a4c313d4706642914da119ee1fc32ea6628de40a1e8231c294f1b5c6d02ad",
"job-tracker-ui/out/_not-found.html": "1ca05af72181de6df1a2b0c07fe5d151fb058344c07e204a78648db4ec1c6e64",
"job-tracker-ui/out/_not-found.txt": "37dee5851fdf4b1bbaabdc143380d8dffdd07999a2c08756fd68b713ecc4826f",
"job-tracker-ui/out/favicon.ico": "8d4923e2c487805ea9f852a685b2cf0fb10512ded8290d2a5c37d1cc8fd0702a",
"job-tracker-ui/out/favicon.svg": "63ecf8f37f8bb146ecfae3917187c6b692d4e801641e8a314486a4007912f3a3",
"job-tracker-ui/out/index.html": "85cacbcd2e4209f2fc1af7e22b6e0d7ef23b3d2fd8612b4a8b105b545a9f6586",
"job-tracker-ui/out/index.txt": "dae993f53affb686262f8ae8e75718ef05b43cdf4b9b7391376fe8ca4fe41ccf",
"job-tracker-ui/out/logo192.png": "bc652031597fabd19b9f14e22a71f809490d218037521b3b64d013418c1390b4",
"job-tracker-ui/out/logo512.png": "5129e9bc1ba812b711fd9a646d3a38a906d365a26ee685df183b8752458944d4",
"job-tracker-ui/out/manifest.json": "2667ce63c2e0835b7007239816a5597639321d333da396a78638dfa9e8a7c935",
"job-tracker-ui/out/mockups/dashboard.svg": "318be3adb1324c26eb30f05572a29528a3e38331c6a604652eef75b54f8c29dc",
"job-tracker-ui/out/mockups/pipeline.svg": "db2ef41faabeb90e7ce85413e168581686c54535d970fc7d8deda3888a479de4",
"job-tracker-ui/out/mockups/workspace.svg": "9bbab22349147c7b01d6e18c93c622a6f8aed6dedaf23715189ab54d60e1ddbd",
"job-tracker-ui/out/robots.txt": "e5ab0d231eeb01b4a982d1c79a6729cac9797ad15a69247e4f28ba6afc149b4c"
}
}
+19
View File
@@ -0,0 +1,19 @@
# Shared MUI widget locale continuation
Source and verifier: `cb9d0397` on `codex/a3-ui-10`.
The live application locale boundary now merges the installed Material UI Bokmål component defaults into the existing theme. English derives from the original parent theme. Existing component defaults and both colour schemes are retained; no dependency, backend preference contract or generated-content language changed. This fixes English Autocomplete Open/Clear labels observed in Bokmål date-widget screenshots and supplies the installed defaults to other Material widgets that use them. Explicit per-component labels still require their own coverage.
## Verification
- RED: the new real Autocomplete regression could not find Bokmål Open before the fix. Two earlier picker tests passed. Retained `RED.txt`.
- PASSED: 20 cases in four suites: real picker/shared controls, deterministic theme, loaded-theme states and Edit revision handling. The new case switches NB → EN → NB, retains the occupation selection and dark colour scheme. The Clear icon is normally hover-hidden; its title/ARIA label is checked directly. Intermediate test failures from querying this hidden icon by accessible role were corrected; these are not application failures or passing evidence.
- PASSED: production build, final TypeScript check and scoped ESLint. The final TypeScript check followed the test-selector correction; application source was unchanged after the build.
- PASSED: nine real-widget journeys / 27 accessibility captures / 18 intercepted fixture writes. Each of Los Angeles, Oslo and Kiritimati used EN/light/1440 mouse, NB/dark/390 touch and NB/light/768 touch. Every journey checked live Open/Clear labels, retained company selection, unchanged colour scheme, date selection/payloads, unsaved draft and no extra detail reads. Zero page errors/unavailable reads. External Google Fonts requests were blocked.
- The refreshed dark/mobile screenshot was visually inspected. This rerun overlaps the earlier flat-dialog matrix; do not add its counts to the earlier nine/27.
Evidence: `evidence/shared-widgets/`. Source/evidence/export manifest: `SHARED_WIDGET_SOURCE_MANIFEST.json`. Bindings describe scoped content, not a release image or whole-application acceptance.
## Limits and rollback
Native-language, physical-device/assistive-technology, all-route/default-string coverage and additional European locales remain open. API traffic and writes used intercepted synthetic fixtures, not real persistence or services. No live provider/model/security/recovery qualification is implied. Revert `cb9d0397` to roll back the locale merge and its regressions; there is no migration or data change.
@@ -0,0 +1,136 @@
{
"sourceContentCommit": "cb9d039786d1ce32e85dc857c55e6e8a3c2007bc",
"policy": "LF-normalised source and text evidence; raw PNG/gzip/zip/export. Content binding for offline UI verification; release candidate/image identity remains separate.",
"sources": {
"job-tracker-ui/src/App.tsx": "e0c50ec92954b90cf62b9d8b8e0d5b5802c2408e577e5e255a8d78c3dfdf7135",
"job-tracker-ui/src/ClientApp.tsx": "327cf5f9cf213c4d8146bf6fa2c0c0d20462b71e03ed0d73105823c5f26b0424",
"job-tracker-ui/src/DateLocalizationProvider.tsx": "cd46fdfda8c145d7c634e1ed6a714ffeb898a5ef30d705aa019390c9b8456870",
"job-tracker-ui/src/i18n/I18nProvider.tsx": "dfa8d95510a7dd872e1b9bbddf9bb4ab31da4d4b5e932b0099397d11e08ad108",
"job-tracker-ui/src/i18n/translations.ts": "a0b706a859264773418ec3f1275332e97add913de65c4d6efaef04b23ae8e1c5",
"job-tracker-ui/src/components/AddJobModal.tsx": "e3b846dc276e8d2a8084bf6e7e670a0639a46eea0788e90afa81722e6028f369",
"job-tracker-ui/src/components/EditJobDialog.tsx": "30a43317aff528e26f3bbf65c14b61d7f9171c66ee4f8255b5b56584aca9e88b",
"job-tracker-ui/src/date-picker-localisation.test.tsx": "68b157072455df53b08341f872d3e9651b2650c51b36f94e710631a2e2a8ed51",
"job-tracker-ui/scripts/verify-job-date-widgets.mjs": "e8364113e659860465e5fe88a3a5a3692b2fb81a7413311a27dc399a3da4e6ce",
"job-tracker-ui/package.json": "3745394e406cefa40d30a87e840bccb359be147bac6ffba736e2ae90898a8626",
"job-tracker-ui/package-lock.json": "bf31e90d7af03e8af059db2aaf893925b9e8881a30a1688034ed88ef45d4fcab"
},
"evidence": {
"docs/i18n/evidence/shared-widgets/add-America-Los_Angeles-en-1440-light.png": "438d9c4a58e0d0cd4310deecc0185e29f8df2c81761282f5a5132354d35baa55",
"docs/i18n/evidence/shared-widgets/add-America-Los_Angeles-nb-390-dark.png": "deca21bc4a1bd9267a601e57176d9ae9cd56cf4adac4246d87fba3b8459f10f9",
"docs/i18n/evidence/shared-widgets/add-America-Los_Angeles-nb-768-light.png": "69d2f6c99585714a57c62e8bdb4b24201251852293569a268ab45799250dec7b",
"docs/i18n/evidence/shared-widgets/add-Europe-Oslo-en-1440-light.png": "1233effc1a3866f05723da0502bce17acbaed73f8bb0e231686c68c281866891",
"docs/i18n/evidence/shared-widgets/add-Europe-Oslo-nb-390-dark.png": "deca21bc4a1bd9267a601e57176d9ae9cd56cf4adac4246d87fba3b8459f10f9",
"docs/i18n/evidence/shared-widgets/add-Europe-Oslo-nb-768-light.png": "69d2f6c99585714a57c62e8bdb4b24201251852293569a268ab45799250dec7b",
"docs/i18n/evidence/shared-widgets/add-Pacific-Kiritimati-en-1440-light.png": "1233effc1a3866f05723da0502bce17acbaed73f8bb0e231686c68c281866891",
"docs/i18n/evidence/shared-widgets/add-Pacific-Kiritimati-nb-390-dark.png": "deca21bc4a1bd9267a601e57176d9ae9cd56cf4adac4246d87fba3b8459f10f9",
"docs/i18n/evidence/shared-widgets/add-Pacific-Kiritimati-nb-768-light.png": "69d2f6c99585714a57c62e8bdb4b24201251852293569a268ab45799250dec7b",
"docs/i18n/evidence/shared-widgets/BROWSER.json": "f2ef5b1520df17f47b2599c61126c1f0b9b1c76e05e6430f933b222faa0d33d2",
"docs/i18n/evidence/shared-widgets/BROWSER.txt": "3577da81fe4e2847fdc52aba5591b74e2b3bfc741e27e07c3d6024f24ad9d827",
"docs/i18n/evidence/shared-widgets/BUILD.txt": "3dfbb92baa539582d9767cb481ef3fb0709f8b73bb15f79143e1a697087f42f5",
"docs/i18n/evidence/shared-widgets/calendar-America-Los_Angeles-en-1440-light.png": "a5a799597cbd3f9d09e445e30994674c7d2a6102a2707d16bfd8409ea12a9c36",
"docs/i18n/evidence/shared-widgets/calendar-America-Los_Angeles-nb-390-dark.png": "738229a9056084a73ace7a611d50573fb8642ea331d4e05045948bd152f4fb7e",
"docs/i18n/evidence/shared-widgets/calendar-America-Los_Angeles-nb-768-light.png": "bbbc82f449c9a10b24130f45b6dca0d7872abb261ca988e6303b1e0bcabf8769",
"docs/i18n/evidence/shared-widgets/calendar-Europe-Oslo-en-1440-light.png": "df09fdde0e79da9f2c4c330af99da58be033dcc43eb2ea9801d83dbf9a55cc6f",
"docs/i18n/evidence/shared-widgets/calendar-Europe-Oslo-nb-390-dark.png": "8b0eb35161b29b232aeab3d8df0ae4a37d2897154282db6a85e0467d33f358e3",
"docs/i18n/evidence/shared-widgets/calendar-Europe-Oslo-nb-768-light.png": "ca67e1657feb25d0cc082e66219f74397b0df375705e2e36761e62f2f15e943e",
"docs/i18n/evidence/shared-widgets/calendar-Pacific-Kiritimati-en-1440-light.png": "df09fdde0e79da9f2c4c330af99da58be033dcc43eb2ea9801d83dbf9a55cc6f",
"docs/i18n/evidence/shared-widgets/calendar-Pacific-Kiritimati-nb-390-dark.png": "8b0eb35161b29b232aeab3d8df0ae4a37d2897154282db6a85e0467d33f358e3",
"docs/i18n/evidence/shared-widgets/calendar-Pacific-Kiritimati-nb-768-light.png": "ca67e1657feb25d0cc082e66219f74397b0df375705e2e36761e62f2f15e943e",
"docs/i18n/evidence/shared-widgets/edit-America-Los_Angeles-en-1440-light.png": "8a997149bb79bf466c77672e39ab57ccf3062808b59cdafc22e2dcad04886b89",
"docs/i18n/evidence/shared-widgets/edit-America-Los_Angeles-nb-390-dark.png": "af756c322d271dd51f45381d814354d51ffd18c6ff71f4ed5cf0ec206e5fd1f2",
"docs/i18n/evidence/shared-widgets/edit-America-Los_Angeles-nb-768-light.png": "7c100cde0a2387b8a266de98385008661271fb1abf2983e609e12212edfae71e",
"docs/i18n/evidence/shared-widgets/edit-Europe-Oslo-en-1440-light.png": "dd7da891f399ee529795a46e2ac2d00574e67ed83b89d7f3b5c9fcd610e04b5e",
"docs/i18n/evidence/shared-widgets/edit-Europe-Oslo-nb-390-dark.png": "af756c322d271dd51f45381d814354d51ffd18c6ff71f4ed5cf0ec206e5fd1f2",
"docs/i18n/evidence/shared-widgets/edit-Europe-Oslo-nb-768-light.png": "7c100cde0a2387b8a266de98385008661271fb1abf2983e609e12212edfae71e",
"docs/i18n/evidence/shared-widgets/edit-Pacific-Kiritimati-en-1440-light.png": "dd7da891f399ee529795a46e2ac2d00574e67ed83b89d7f3b5c9fcd610e04b5e",
"docs/i18n/evidence/shared-widgets/edit-Pacific-Kiritimati-nb-390-dark.png": "af756c322d271dd51f45381d814354d51ffd18c6ff71f4ed5cf0ec206e5fd1f2",
"docs/i18n/evidence/shared-widgets/edit-Pacific-Kiritimati-nb-768-light.png": "7c100cde0a2387b8a266de98385008661271fb1abf2983e609e12212edfae71e",
"docs/i18n/evidence/shared-widgets/LINT.txt": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"docs/i18n/evidence/shared-widgets/RED.txt": "8b75af1801eac16e865cd938a4b762d9940b73f7e2962bde083032339176c880",
"docs/i18n/evidence/shared-widgets/TESTS.txt": "8e80218d6b3d046027e4b4c7060118f09f46fb506e875da3988ac92ff9827920",
"docs/i18n/evidence/shared-widgets/TYPECHECK.txt": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"docs/i18n/SHARED_WIDGET_LOCALISATION.md": "3febd17654c56c30f15da5cd52dc2742fb6bd26cab08d16f0d51b7d8e8cb5844"
},
"export": {
"job-tracker-ui/out/404.html": "4ae9ef34461000f4a03bafb9cdeb1bbcc0867d3aa30bb89b27488070075374de",
"job-tracker-ui/out/__next.__PAGE__.txt": "5199837e56cb0008e446034ab4c9cd9f8098a43543f3325d1a5569f322956a1e",
"job-tracker-ui/out/__next._full.txt": "a76c71f64a3c36a6677d03ed67f708d5ad15546020dbe23623128034f481df88",
"job-tracker-ui/out/__next._tree.txt": "424e23705aaf521bf96b4135fbaed0ea31a95d47ec741646e6a4c2561582c864",
"job-tracker-ui/out/_next/static/chunks/0--vhwu1kmht5.js": "426fae50ff0485fb6af8e28a7d1751dfaefcb6118a24dab39b3a7099d38c18dd",
"job-tracker-ui/out/_next/static/chunks/0-_aq6ipzcshj.js": "39f90c215bf52aef6274ed3dcc0f8af32e21a37fe8845b3bca0d6ea60da6a8c2",
"job-tracker-ui/out/_next/static/chunks/00sdty8f_wo6_.js": "047077072852a0211c228c75d5e29836ba970771382ba0da4bac9fdd0bfac29f",
"job-tracker-ui/out/_next/static/chunks/02pclekj6sxrn.js": "8de8b7bb4d26955b3f334c338dc6c2cdef52b134b6be2396e9c5cf85c3657448",
"job-tracker-ui/out/_next/static/chunks/05rgc5zsm9p5i.js": "1da7305ab9d8e95795327cce11a16db4c56d90429d7e1dea4c4088c085f20d69",
"job-tracker-ui/out/_next/static/chunks/0afg9bl7a8pj5.js": "bd0cec651950d5007f84273af5be8d8255806da4db54238f5c512442c7449459",
"job-tracker-ui/out/_next/static/chunks/0ah1fzqmie05a.js": "5c65505a7dcbac63ac71d254e619a4c800943bfb4fa16cd40c10cdbe7e9b6e31",
"job-tracker-ui/out/_next/static/chunks/0b3nrgli_w65x.js": "bf23e1a05a97de3a950e76eb206b2fbbe9003460891ecd5ab091155aa385badc",
"job-tracker-ui/out/_next/static/chunks/0bdt7ln111t48.js": "6184b1a61f22e2a640c17941634fdc75678ce93d142848d9f07f163e0d1fd201",
"job-tracker-ui/out/_next/static/chunks/0cz1d0mv5g_q7.js": "0973c1d64c88adc8e3c950410cb58b288f72118d5965b78049438deb8f2f9683",
"job-tracker-ui/out/_next/static/chunks/0og1crk78cn4a.js": "aa5031605cb2f46a612e94d9d71473f48a71aa4a5acfe8fe5f2dfc66d951ab40",
"job-tracker-ui/out/_next/static/chunks/0pvbhqkwp15b6.js": "f210cc1a8c203de6008b6137c0324afabb33f233c1ab56800fe9e6c963c0c34a",
"job-tracker-ui/out/_next/static/chunks/0q20g9o8x-k8z.js": "3d4d9086985419aa8866b032158196c774acb8bf318f3c8245508c8daa9c5148",
"job-tracker-ui/out/_next/static/chunks/0q_pwu9jxv4-d.js": "42a43b3d7245a413c7c29b4cbe87823037a6dbc637e2e282c92405470e0f575d",
"job-tracker-ui/out/_next/static/chunks/0qxm1h9uqfgpa.js": "a5af3192c76f3b8dac5c17afbe90d96a5c7652810ce69b7f39fbcf1f1231fd8c",
"job-tracker-ui/out/_next/static/chunks/0ta69108309yl.js": "07b8069d13ddaf7aaa24daf1070144a2e98b444ca162b628b8bfb8e1068d2967",
"job-tracker-ui/out/_next/static/chunks/0vo4_3wcjs1gy.js": "5e4d305fc346f9ecf48328ab0416397d8bd7c100f0de99dea16d2a1463b386a9",
"job-tracker-ui/out/_next/static/chunks/0y95onyb03k05.js": "2e60db02cc316f7a4bd3debd020fe01f403394a1636efb4d02114f1b6f5a3879",
"job-tracker-ui/out/_next/static/chunks/0zep-wknuuxm3.js": "cc13d0dc908a4b047a165a50a226b787ee7707135b3f2c5894aab8cbd618a29c",
"job-tracker-ui/out/_next/static/chunks/1-8ykqf0n88k2.js": "eb208124ec838cb9a9ae366745bd81fba1c35eeceac128c567bbb7977ebb6a2e",
"job-tracker-ui/out/_next/static/chunks/10qxrc5f9nore.js": "512bfc44077703fa4e03e15ca48acf3501231c5e2cb9bf7beb2ec30224fe287b",
"job-tracker-ui/out/_next/static/chunks/1435in_k_-dhc.js": "4757476512d5c4dd25f9264a18b304ed4824b23aff1c09e134fcbff21d9c36b6",
"job-tracker-ui/out/_next/static/chunks/145oicobmjvm7.js": "d6bbbdeed1381652dc7412e5ce673a077579ecd5cee8934cb6b533c814098764",
"job-tracker-ui/out/_next/static/chunks/14e7ovj0s3elm.js": "070567dd4d23fb9fa0b9ba132185a697af6578045d43b2b1c7bbb2a1594d935d",
"job-tracker-ui/out/_next/static/chunks/15saa2mllmdp7.js": "6664972f61e1669d2ca1ed9af467df87d316efeadf59496ce8c68d8c84fd7512",
"job-tracker-ui/out/_next/static/chunks/16lfece6cnlln.js": "716363ba044f30b0e70440756b4e5eafa2d25f2e1628e855e3bc4ab9eabee3d3",
"job-tracker-ui/out/_next/static/chunks/188aj_hr_p4eq.js": "f10e4af24bebfb8b9e7306a5688ef45f07f280c920dc01815ac578fbe18e2182",
"job-tracker-ui/out/_next/static/chunks/1avt7t-z_q80r.js": "8cbd919d971eaf46e508f9e6b073a9183a29457c3c42c9566ec74b018894c061",
"job-tracker-ui/out/_next/static/chunks/1ke8pzecwofrg.css": "32e9ab963ebadebcbe2c68dffb24cfbfc8cc24255ad91b48b7941fc777bf7439",
"job-tracker-ui/out/_next/static/chunks/1req80b9jj38r.js": "eee4d2e3789e1215195dba99ad0f09d34f8c83da848322b76054722233e9dae3",
"job-tracker-ui/out/_next/static/chunks/1svx1qnrof8xa.js": "111c502a1ca939f7bf971ed1b3e3cda011b130c313a7f7df742ae4b1e14486ae",
"job-tracker-ui/out/_next/static/chunks/1w3q8yrcjn2jk.js": "365200e241ee9290e93d7c2fdba169f2b0a8e774541e3cbf3f6f2781110fa644",
"job-tracker-ui/out/_next/static/chunks/1xn023_28rgzj.js": "4f7f2108d030a48d039930736c92a55710975dddf419fe673f84d92899b1f575",
"job-tracker-ui/out/_next/static/chunks/1xwc-b795syag.css": "f73c002233f931ca14d0ae58815c47ca9f2659f25b1547a6d1fdca12a2a00b3f",
"job-tracker-ui/out/_next/static/chunks/22wx8--1b9tnq.js": "3f7d8e762371262c6a740b4dac21725b424847319afac46055e97074a99ff5f5",
"job-tracker-ui/out/_next/static/chunks/274f8aifi_wup.js": "bc5bc5617b4acbbbb1c7966322c508bb5554024d78e71e523e27f7f5bba5a8ab",
"job-tracker-ui/out/_next/static/chunks/29jdblthf0w7a.js": "e84b4f7c225d58099aa89999544f7318cb23c0c71f2385c31b3c9181a62ab7f0",
"job-tracker-ui/out/_next/static/chunks/2edynwqicn6ym.js": "c694af15bb117955e6f7f0962f47495b2be017dfdea93b6d4ede4d5e4fcd7efd",
"job-tracker-ui/out/_next/static/chunks/2ha8nlh4rv92e.js": "5cb4b4cee708f35ed54ef6289269fa866afbaa64fd47bf9275ff691664d3132e",
"job-tracker-ui/out/_next/static/chunks/2php_9zkhprrb.js": "4f91e63b1181693e7a3b269b1b24004d9abee13413b8396a916799b8c01620e8",
"job-tracker-ui/out/_next/static/chunks/2pllop_q35dkw.js": "21c9a7c53763010317a2d09460066017e1fd192691e6a0bd83ed20ea6e1fe9dd",
"job-tracker-ui/out/_next/static/chunks/2s2gjr6ro25dj.js": "7489e2ac285a05b563c2bcb4eaf9274bf930f2a7385f17d6d5e6850064b88b10",
"job-tracker-ui/out/_next/static/chunks/2sj7ufu_6n9af.js": "69d7471c01b8001d52abc848910a8c35457dac3aaa4ce66eae2a03ca29ad623b",
"job-tracker-ui/out/_next/static/chunks/30-q9iimktiql.js": "db89d5da1f101216d7f99e9518dc7f5ed84f8b40ac84a6ea55f73c01bf1aca1b",
"job-tracker-ui/out/_next/static/chunks/315n7snxk3fgw.js": "1993429aafa0753d5e9fd1ca63a4f6079ce7f7f37faa44716191f59c972c4d41",
"job-tracker-ui/out/_next/static/chunks/33at73n-mzpir.js": "1605cc3311cc6ca3b2f44b2e0217fe5441a2799fa32b0b0c94d7634f4861ac83",
"job-tracker-ui/out/_next/static/chunks/357yqeklh7414.js": "0192ab7198ebbe299d34a934c504dd4b562d1cccebb6e9d4c5163930c881b14c",
"job-tracker-ui/out/_next/static/chunks/35pq4ixaltkev.js": "f1e9d53e2481de01d7b04aa938397c2545b7d7dc42ce3d0ac5cd3f1141bda182",
"job-tracker-ui/out/_next/static/chunks/3_4dnl1l3809g.js": "50288c2864a9e8c7ef25c93762edb86e4e617778737b5b5a028c53c5b93d3b70",
"job-tracker-ui/out/_next/static/chunks/3bqn9b34na0b9.js": "f1b8ebcc1b22251c33c9f3a5d11c1f893cad2da2e8744a441e790f7347aa9e73",
"job-tracker-ui/out/_next/static/chunks/3gkylew_qn7cw.js": "7e6c74c81be0c41ab433e36d65b914f62c48f332922e27f7cc953476f4dd78cb",
"job-tracker-ui/out/_next/static/chunks/3i8h6eaa02hty.js": "8b0f0c73a472565f2f1317d33e0753984103d5a2fe7b0efd902fc180124664c9",
"job-tracker-ui/out/_next/static/chunks/3la4obitihzpv.js": "9a5a57b46db389b98f1fd5baa86eba1a360300dae9d1ff3ffeae7bc3378135b9",
"job-tracker-ui/out/_next/static/chunks/3owtzqjyid5zf.js": "5f9cbb7c0cddd0c781137b970d5a4fceb386dbb259c8121dd54b33d76160d9ea",
"job-tracker-ui/out/_next/static/chunks/3q4v10rrqy40n.js": "d87ff7b7a0f07d0d92eab3876481eb686a12d9c43d8b92cbe406aefa227a11c1",
"job-tracker-ui/out/_next/static/chunks/3xbcgmkebja05.js": "bb1e98db2657a4b067676ead2ea26172478a79c1b384a9f9fe6588e1481d33ee",
"job-tracker-ui/out/_next/static/chunks/turbopack-25fzg9ay_1ss-.js": "c40f39e993a30c96d6247bb3a7b581538bb046efe8ef8ec07bea3ba32291750f",
"job-tracker-ui/out/_next/static/SRhzLufk4DPQ7WreBxm3P/_buildManifest.js": "9476e0afd3f9da68a64e6ec58631191fd3eaca62c9f5a377e7517fe9a89e59ea",
"job-tracker-ui/out/_next/static/SRhzLufk4DPQ7WreBxm3P/_clientMiddlewareManifest.js": "5483e3295dc35dfe29ea932efc85341c916f10ff78ff5b0e965e003710893a4a",
"job-tracker-ui/out/_next/static/SRhzLufk4DPQ7WreBxm3P/_ssgManifest.js": "678f6ce2cb80b1fe72fc67e7412be6e2ab6ada083111b64f7c40d35e3cba5e00",
"job-tracker-ui/out/_not-found/__next._full.txt": "4f462f81bf53f3a23cabbc7b5a21bdacef0bf52e97ab9a8e16759a48de5ca183",
"job-tracker-ui/out/_not-found/__next._not-found/__PAGE__.txt": "df0ac445303b7cf8af0e2f127ab0cdce7920953cca6e0cf7138986d22fc6ac64",
"job-tracker-ui/out/_not-found/__next._tree.txt": "94b338bfbc252b36b4fbd58de80824a958d9f942dd44348667dcaa1c6b53af28",
"job-tracker-ui/out/_not-found.html": "4ae9ef34461000f4a03bafb9cdeb1bbcc0867d3aa30bb89b27488070075374de",
"job-tracker-ui/out/_not-found.txt": "4f462f81bf53f3a23cabbc7b5a21bdacef0bf52e97ab9a8e16759a48de5ca183",
"job-tracker-ui/out/favicon.ico": "8d4923e2c487805ea9f852a685b2cf0fb10512ded8290d2a5c37d1cc8fd0702a",
"job-tracker-ui/out/favicon.svg": "63ecf8f37f8bb146ecfae3917187c6b692d4e801641e8a314486a4007912f3a3",
"job-tracker-ui/out/index.html": "e020a26d1d7eb7bbadd2d5b0b848374d871f65d7be8c718e8908b44b92d536e6",
"job-tracker-ui/out/index.txt": "a76c71f64a3c36a6677d03ed67f708d5ad15546020dbe23623128034f481df88",
"job-tracker-ui/out/logo192.png": "bc652031597fabd19b9f14e22a71f809490d218037521b3b64d013418c1390b4",
"job-tracker-ui/out/logo512.png": "5129e9bc1ba812b711fd9a646d3a38a906d365a26ee685df183b8752458944d4",
"job-tracker-ui/out/manifest.json": "2667ce63c2e0835b7007239816a5597639321d333da396a78638dfa9e8a7c935",
"job-tracker-ui/out/mockups/dashboard.svg": "318be3adb1324c26eb30f05572a29528a3e38331c6a604652eef75b54f8c29dc",
"job-tracker-ui/out/mockups/pipeline.svg": "db2ef41faabeb90e7ce85413e168581686c54535d970fc7d8deda3888a479de4",
"job-tracker-ui/out/mockups/workspace.svg": "9bbab22349147c7b01d6e18c93c622a6f8aed6dedaf23715189ab54d60e1ddbd",
"job-tracker-ui/out/robots.txt": "e5ab0d231eeb01b4a982d1c79a6729cac9797ad15a69247e4f28ba6afc149b4c"
}
}
@@ -0,0 +1,9 @@
> job-tracker-ui@0.1.0 test
> jest --runInBand job-date-picker-calendar.test.tsx
Test Suites: 1 passed, 1 total
Tests: 5 passed, 5 total
Snapshots: 0 total
Time: 6.688 s
Ran all test suites matching job-date-picker-calendar.test.tsx.
@@ -0,0 +1,9 @@
> job-tracker-ui@0.1.0 test
> jest --runInBand job-date-picker-calendar.test.tsx
Test Suites: 1 passed, 1 total
Tests: 5 passed, 5 total
Snapshots: 0 total
Time: 6.561 s
Ran all test suites matching job-date-picker-calendar.test.tsx.
@@ -0,0 +1,9 @@
> job-tracker-ui@0.1.0 test
> jest --runInBand job-date-picker-calendar.test.tsx
Test Suites: 1 passed, 1 total
Tests: 5 passed, 5 total
Snapshots: 0 total
Time: 7.132 s
Ran all test suites matching job-date-picker-calendar.test.tsx.
@@ -0,0 +1,10 @@
> job-tracker-ui@0.1.0 test
> jest --runInBand job-date-picker-calendar.test.tsx edit-job-revision.test.tsx quick-capture.test.tsx file-picker-activation.test.tsx
Test Suites: 4 passed, 4 total
Tests: 21 passed, 21 total
Snapshots: 0 total
Time: 15.02 s
Ran all test suites matching job-date-picker-calendar.test.tsx|edit-job-revision.test.tsx|quick-capture.test.tsx|file-picker-activation.test.tsx.
@@ -0,0 +1,105 @@
> job-tracker-ui@0.1.0 test
> jest --runInBand job-date-picker-calendar.test.tsx
FAIL src/job-date-picker-calendar.test.tsx
● en Add date-only picker round trip retains the selected day and existing creation payload
expect(element).toHaveTextContent()
Expected element to have text content:
2026-11-23
Received:
2026-11-22
39 | render(wrap(<AddJobModal open initialUrl="https://example.test/job" onClose={jest.fn()} onCreated={jest.fn()} />));
40 | await screen.findByDisplayValue("Teacher");
> 41 | expect(screen.getByLabelText(copy.addJobModalDeadline)).toHaveTextContent("2026-11-23");
| ^
42 | fireEvent.click(screen.getByRole("button", { name: `${copy.addJobModalDeadline} clear` }));
43 | expect(screen.getByLabelText(copy.addJobModalDeadline)).toBeEmptyDOMElement();
44 | fireEvent.click(screen.getByRole("button", { name: `${copy.addJobModalDeadline} invalid` }));
at toHaveTextContent (src/job-date-picker-calendar.test.tsx:41:59)
● nb Add date-only picker round trip retains the selected day and existing creation payload
expect(element).toHaveTextContent()
Expected element to have text content:
2026-11-23
Received:
2026-11-22
39 | render(wrap(<AddJobModal open initialUrl="https://example.test/job" onClose={jest.fn()} onCreated={jest.fn()} />));
40 | await screen.findByDisplayValue("Teacher");
> 41 | expect(screen.getByLabelText(copy.addJobModalDeadline)).toHaveTextContent("2026-11-23");
| ^
42 | fireEvent.click(screen.getByRole("button", { name: `${copy.addJobModalDeadline} clear` }));
43 | expect(screen.getByLabelText(copy.addJobModalDeadline)).toBeEmptyDOMElement();
44 | fireEvent.click(screen.getByRole("button", { name: `${copy.addJobModalDeadline} invalid` }));
at toHaveTextContent (src/job-date-picker-calendar.test.tsx:41:59)
● en Edit date-only picker round trip preserves all five date controls and revision payload
expect(element).toHaveTextContent()
Expected element to have text content:
2026-11-23
Received:
2026-11-22
59 | render(wrap(<EditJobDialog open jobId={1} onClose={jest.fn()} onSaved={jest.fn()} />));
60 | await screen.findByDisplayValue("Teacher");
> 61 | expect(screen.getByLabelText(copy.editJobDeadline)).toHaveTextContent("2026-11-23");
| ^
62 | fireEvent.click(screen.getByRole("button", { name: `${copy.editJobDeadline} clear` }));
63 | fireEvent.click(screen.getByRole("button", { name: `${copy.editJobDeadline} invalid` }));
64 | expect(screen.getByLabelText(copy.editJobDeadline)).toBeEmptyDOMElement();
at toHaveTextContent (src/job-date-picker-calendar.test.tsx:61:55)
● nb Edit date-only picker round trip preserves all five date controls and revision payload
expect(element).toHaveTextContent()
Expected element to have text content:
2026-11-23
Received:
2026-11-22
59 | render(wrap(<EditJobDialog open jobId={1} onClose={jest.fn()} onSaved={jest.fn()} />));
60 | await screen.findByDisplayValue("Teacher");
> 61 | expect(screen.getByLabelText(copy.editJobDeadline)).toHaveTextContent("2026-11-23");
| ^
62 | fireEvent.click(screen.getByRole("button", { name: `${copy.editJobDeadline} clear` }));
63 | fireEvent.click(screen.getByRole("button", { name: `${copy.editJobDeadline} invalid` }));
64 | expect(screen.getByLabelText(copy.editJobDeadline)).toBeEmptyDOMElement();
at toHaveTextContent (src/job-date-picker-calendar.test.tsx:61:55)
● Edit retains its existing UTC calendar-day loading policy for a legacy offset timestamp
expect(element).toHaveTextContent()
Expected element to have text content:
2026-11-24
Received:
2026-11-23
75 | render(wrap(<EditJobDialog open jobId={1} onClose={jest.fn()} onSaved={jest.fn()} />));
76 | await screen.findByDisplayValue("Teacher");
> 77 | expect(screen.getByLabelText(translations.en.editJobDeadline)).toHaveTextContent("2026-11-24");
| ^
78 | expect(client.put).not.toHaveBeenCalled();
79 | });
80 |
at Object.toHaveTextContent (src/job-date-picker-calendar.test.tsx:77:66)
Test Suites: 1 failed, 1 total
Tests: 5 failed, 5 total
Snapshots: 0 total
Time: 4.077 s
Ran all test suites matching job-date-picker-calendar.test.tsx.
@@ -0,0 +1,86 @@
> job-tracker-ui@0.1.0 test
> jest --runInBand job-date-picker-calendar.test.tsx
FAIL src/job-date-picker-calendar.test.tsx
● en Add date-only picker round trip retains the selected day and existing creation payload
expect(element).toHaveTextContent()
Expected element to have text content:
2026-11-23
Received:
2026-11-22
46 | for (const label of [copy.addJobModalDateApplied, copy.addJobModalDeadline]) {
47 | fireEvent.click(screen.getByRole("button", { name: `${label} choose` }));
> 48 | expect(screen.getByLabelText(label)).toHaveTextContent("2026-11-23");
| ^
49 | }
50 | fireEvent.click(screen.getByRole("button", { name: copy.continue }));
51 | for (let step = 0; step < 3; step++) fireEvent.click(screen.getByRole("button", { name: copy.skipAndContinue }));
at toHaveTextContent (src/job-date-picker-calendar.test.tsx:48:42)
● nb Add date-only picker round trip retains the selected day and existing creation payload
expect(element).toHaveTextContent()
Expected element to have text content:
2026-11-23
Received:
2026-11-22
46 | for (const label of [copy.addJobModalDateApplied, copy.addJobModalDeadline]) {
47 | fireEvent.click(screen.getByRole("button", { name: `${label} choose` }));
> 48 | expect(screen.getByLabelText(label)).toHaveTextContent("2026-11-23");
| ^
49 | }
50 | fireEvent.click(screen.getByRole("button", { name: copy.continue }));
51 | for (let step = 0; step < 3; step++) fireEvent.click(screen.getByRole("button", { name: copy.skipAndContinue }));
at toHaveTextContent (src/job-date-picker-calendar.test.tsx:48:42)
● en Edit date-only picker round trip preserves all five date controls and revision payload
expect(element).toHaveTextContent()
Expected element to have text content:
2026-11-23
Received:
2026-11-22
65 | for (const label of [copy.editJobAppliedOn, copy.editJobStatusChangedOn, copy.editJobReplyReceivedOn, copy.editJobFollowUpOn, copy.editJobDeadline]) {
66 | fireEvent.click(screen.getByRole("button", { name: `${label} choose` }));
> 67 | expect(screen.getByLabelText(label)).toHaveTextContent("2026-11-23");
| ^
68 | }
69 | fireEvent.click(screen.getByRole("button", { name: copy.save }));
70 | await waitFor(() => expect(client.put).toHaveBeenCalledWith("/jobapplications/1", expect.objectContaining({ expectedVersion: 7, dateApplied: "2026-11-23", deadline: "2026-11-23", followUpAt: "2026-11-23", responseDate: "2026-11-23" })));
at toHaveTextContent (src/job-date-picker-calendar.test.tsx:67:42)
● nb Edit date-only picker round trip preserves all five date controls and revision payload
expect(element).toHaveTextContent()
Expected element to have text content:
2026-11-23
Received:
2026-11-22
65 | for (const label of [copy.editJobAppliedOn, copy.editJobStatusChangedOn, copy.editJobReplyReceivedOn, copy.editJobFollowUpOn, copy.editJobDeadline]) {
66 | fireEvent.click(screen.getByRole("button", { name: `${label} choose` }));
> 67 | expect(screen.getByLabelText(label)).toHaveTextContent("2026-11-23");
| ^
68 | }
69 | fireEvent.click(screen.getByRole("button", { name: copy.save }));
70 | await waitFor(() => expect(client.put).toHaveBeenCalledWith("/jobapplications/1", expect.objectContaining({ expectedVersion: 7, dateApplied: "2026-11-23", deadline: "2026-11-23", followUpAt: "2026-11-23", responseDate: "2026-11-23" })));
at toHaveTextContent (src/job-date-picker-calendar.test.tsx:67:42)
Test Suites: 1 failed, 1 total
Tests: 4 failed, 1 passed, 5 total
Snapshots: 0 total
Time: 4.882 s
Ran all test suites matching job-date-picker-calendar.test.tsx.
@@ -0,0 +1,86 @@
> job-tracker-ui@0.1.0 test
> jest --runInBand job-date-picker-calendar.test.tsx
FAIL src/job-date-picker-calendar.test.tsx
● en Add date-only picker round trip retains the selected day and existing creation payload
expect(element).toHaveTextContent()
Expected element to have text content:
2026-11-23
Received:
2026-11-22
46 | for (const label of [copy.addJobModalDateApplied, copy.addJobModalDeadline]) {
47 | fireEvent.click(screen.getByRole("button", { name: `${label} choose` }));
> 48 | expect(screen.getByLabelText(label)).toHaveTextContent("2026-11-23");
| ^
49 | }
50 | fireEvent.click(screen.getByRole("button", { name: copy.continue }));
51 | for (let step = 0; step < 3; step++) fireEvent.click(screen.getByRole("button", { name: copy.skipAndContinue }));
at toHaveTextContent (src/job-date-picker-calendar.test.tsx:48:42)
● nb Add date-only picker round trip retains the selected day and existing creation payload
expect(element).toHaveTextContent()
Expected element to have text content:
2026-11-23
Received:
2026-11-22
46 | for (const label of [copy.addJobModalDateApplied, copy.addJobModalDeadline]) {
47 | fireEvent.click(screen.getByRole("button", { name: `${label} choose` }));
> 48 | expect(screen.getByLabelText(label)).toHaveTextContent("2026-11-23");
| ^
49 | }
50 | fireEvent.click(screen.getByRole("button", { name: copy.continue }));
51 | for (let step = 0; step < 3; step++) fireEvent.click(screen.getByRole("button", { name: copy.skipAndContinue }));
at toHaveTextContent (src/job-date-picker-calendar.test.tsx:48:42)
● en Edit date-only picker round trip preserves all five date controls and revision payload
expect(element).toHaveTextContent()
Expected element to have text content:
2026-11-23
Received:
2026-11-22
65 | for (const label of [copy.editJobAppliedOn, copy.editJobStatusChangedOn, copy.editJobReplyReceivedOn, copy.editJobFollowUpOn, copy.editJobDeadline]) {
66 | fireEvent.click(screen.getByRole("button", { name: `${label} choose` }));
> 67 | expect(screen.getByLabelText(label)).toHaveTextContent("2026-11-23");
| ^
68 | }
69 | fireEvent.click(screen.getByRole("button", { name: copy.save }));
70 | await waitFor(() => expect(client.put).toHaveBeenCalledWith("/jobapplications/1", expect.objectContaining({ expectedVersion: 7, dateApplied: "2026-11-23", deadline: "2026-11-23", followUpAt: "2026-11-23", responseDate: "2026-11-23" })));
at toHaveTextContent (src/job-date-picker-calendar.test.tsx:67:42)
● nb Edit date-only picker round trip preserves all five date controls and revision payload
expect(element).toHaveTextContent()
Expected element to have text content:
2026-11-23
Received:
2026-11-22
65 | for (const label of [copy.editJobAppliedOn, copy.editJobStatusChangedOn, copy.editJobReplyReceivedOn, copy.editJobFollowUpOn, copy.editJobDeadline]) {
66 | fireEvent.click(screen.getByRole("button", { name: `${label} choose` }));
> 67 | expect(screen.getByLabelText(label)).toHaveTextContent("2026-11-23");
| ^
68 | }
69 | fireEvent.click(screen.getByRole("button", { name: copy.save }));
70 | await waitFor(() => expect(client.put).toHaveBeenCalledWith("/jobapplications/1", expect.objectContaining({ expectedVersion: 7, dateApplied: "2026-11-23", deadline: "2026-11-23", followUpAt: "2026-11-23", responseDate: "2026-11-23" })));
at toHaveTextContent (src/job-date-picker-calendar.test.tsx:67:42)
Test Suites: 1 failed, 1 total
Tests: 4 failed, 1 passed, 5 total
Snapshots: 0 total
Time: 4.669 s
Ran all test suites matching job-date-picker-calendar.test.tsx.
@@ -0,0 +1,4 @@
> job-tracker-ui@0.1.0 typecheck
> tsc --noEmit --incremental false
@@ -0,0 +1,9 @@
> job-tracker-ui@0.1.0 test
> jest --runInBand job-date-picker-calendar.test.tsx
Test Suites: 1 passed, 1 total
Tests: 7 passed, 7 total
Snapshots: 0 total
Time: 8.432 s
Ran all test suites matching job-date-picker-calendar.test.tsx.
@@ -0,0 +1,9 @@
> job-tracker-ui@0.1.0 test
> jest --runInBand job-date-picker-calendar.test.tsx
Test Suites: 1 passed, 1 total
Tests: 7 passed, 7 total
Snapshots: 0 total
Time: 8.287 s
Ran all test suites matching job-date-picker-calendar.test.tsx.
@@ -0,0 +1,9 @@
> job-tracker-ui@0.1.0 test
> jest --runInBand job-date-picker-calendar.test.tsx
Test Suites: 1 passed, 1 total
Tests: 7 passed, 7 total
Snapshots: 0 total
Time: 8.955 s
Ran all test suites matching job-date-picker-calendar.test.tsx.
@@ -0,0 +1,29 @@
> job-tracker-ui@0.1.0 test
> jest --runInBand job-date-picker-calendar.test.tsx -t Add defaults and successful reset
FAIL src/job-date-picker-calendar.test.tsx
● Add defaults and successful reset use local today at 2026-11-23T00:30:00Z
expect(element).toHaveTextContent()
Expected element to have text content:
2026-11-22
Received:
2026-11-23
43 | const view = render(form(true));
44 | fireEvent.click(screen.getByRole("button", { name: translations.en.addJobModalEnterManually }));
> 45 | expect(screen.getByLabelText(translations.en.addJobModalDateApplied)).toHaveTextContent(expected);
| ^
46 | fireEvent.click(screen.getByRole("button", { name: `${translations.en.addJobModalDateApplied} choose` }));
47 | fireEvent.change(screen.getByRole("combobox", { name: translations.en.company }), { target: { value: "Synthetic school" } });
48 | fireEvent.change(screen.getByLabelText(translations.en.addJobModalJobTitle), { target: { value: "Teacher" } });
at toHaveTextContent (src/job-date-picker-calendar.test.tsx:45:73)
Test Suites: 1 failed, 1 total
Tests: 1 failed, 5 skipped, 1 passed, 7 total
Snapshots: 0 total
Time: 4.694 s
Ran all test suites matching job-date-picker-calendar.test.tsx with tests matching "Add defaults and successful reset".
@@ -0,0 +1,29 @@
> job-tracker-ui@0.1.0 test
> jest --runInBand job-date-picker-calendar.test.tsx -t Add defaults and successful reset
FAIL src/job-date-picker-calendar.test.tsx
● Add defaults and successful reset use local today at 2026-11-23T23:30:00Z
expect(element).toHaveTextContent()
Expected element to have text content:
2026-11-24
Received:
2026-11-23
43 | const view = render(form(true));
44 | fireEvent.click(screen.getByRole("button", { name: translations.en.addJobModalEnterManually }));
> 45 | expect(screen.getByLabelText(translations.en.addJobModalDateApplied)).toHaveTextContent(expected);
| ^
46 | fireEvent.click(screen.getByRole("button", { name: `${translations.en.addJobModalDateApplied} choose` }));
47 | fireEvent.change(screen.getByRole("combobox", { name: translations.en.company }), { target: { value: "Synthetic school" } });
48 | fireEvent.change(screen.getByLabelText(translations.en.addJobModalJobTitle), { target: { value: "Teacher" } });
at toHaveTextContent (src/job-date-picker-calendar.test.tsx:45:73)
Test Suites: 1 failed, 1 total
Tests: 1 failed, 5 skipped, 1 passed, 7 total
Snapshots: 0 total
Time: 4.977 s
Ran all test suites matching job-date-picker-calendar.test.tsx with tests matching "Add defaults and successful reset".
@@ -0,0 +1,29 @@
> job-tracker-ui@0.1.0 test
> jest --runInBand job-date-picker-calendar.test.tsx -t Add defaults and successful reset
FAIL src/job-date-picker-calendar.test.tsx
● Add defaults and successful reset use local today at 2026-11-23T23:30:00Z
expect(element).toHaveTextContent()
Expected element to have text content:
2026-11-24
Received:
2026-11-23
43 | const view = render(form(true));
44 | fireEvent.click(screen.getByRole("button", { name: translations.en.addJobModalEnterManually }));
> 45 | expect(screen.getByLabelText(translations.en.addJobModalDateApplied)).toHaveTextContent(expected);
| ^
46 | fireEvent.click(screen.getByRole("button", { name: `${translations.en.addJobModalDateApplied} choose` }));
47 | fireEvent.change(screen.getByRole("combobox", { name: translations.en.company }), { target: { value: "Synthetic school" } });
48 | fireEvent.change(screen.getByLabelText(translations.en.addJobModalJobTitle), { target: { value: "Teacher" } });
at toHaveTextContent (src/job-date-picker-calendar.test.tsx:45:73)
Test Suites: 1 failed, 1 total
Tests: 1 failed, 5 skipped, 1 passed, 7 total
Snapshots: 0 total
Time: 4.542 s
Ran all test suites matching job-date-picker-calendar.test.tsx with tests matching "Add defaults and successful reset".
@@ -0,0 +1,4 @@
> job-tracker-ui@0.1.0 typecheck
> tsc --noEmit --incremental false
@@ -0,0 +1,516 @@
{
"journeys": [
{
"zone": "America/Los_Angeles",
"language": "en",
"width": 1440,
"touch": false,
"theme": "light",
"sevenCalendarSelections": "PASS",
"localeDraftRetention": "PASS",
"localToday": 22,
"fixtureSaveCreate": "PASS"
},
{
"zone": "America/Los_Angeles",
"language": "nb",
"width": 390,
"touch": true,
"theme": "dark",
"sevenCalendarSelections": "PASS",
"localeDraftRetention": "PASS",
"localToday": 22,
"fixtureSaveCreate": "PASS"
},
{
"zone": "Europe/Oslo",
"language": "en",
"width": 1440,
"touch": false,
"theme": "light",
"sevenCalendarSelections": "PASS",
"localeDraftRetention": "PASS",
"localToday": 23,
"fixtureSaveCreate": "PASS"
},
{
"zone": "Europe/Oslo",
"language": "nb",
"width": 390,
"touch": true,
"theme": "dark",
"sevenCalendarSelections": "PASS",
"localeDraftRetention": "PASS",
"localToday": 23,
"fixtureSaveCreate": "PASS"
},
{
"zone": "Pacific/Kiritimati",
"language": "en",
"width": 1440,
"touch": false,
"theme": "light",
"sevenCalendarSelections": "PASS",
"localeDraftRetention": "PASS",
"localToday": 23,
"fixtureSaveCreate": "PASS"
},
{
"zone": "Pacific/Kiritimati",
"language": "nb",
"width": 390,
"touch": true,
"theme": "dark",
"sevenCalendarSelections": "PASS",
"localeDraftRetention": "PASS",
"localToday": 23,
"fixtureSaveCreate": "PASS"
}
],
"captures": [
{
"zone": "America/Los_Angeles",
"language": "en",
"state": "edit",
"violations": []
},
{
"zone": "America/Los_Angeles",
"language": "en",
"state": "add",
"violations": []
},
{
"zone": "America/Los_Angeles",
"language": "nb",
"state": "edit",
"violations": []
},
{
"zone": "America/Los_Angeles",
"language": "nb",
"state": "add",
"violations": []
},
{
"zone": "Europe/Oslo",
"language": "en",
"state": "edit",
"violations": []
},
{
"zone": "Europe/Oslo",
"language": "en",
"state": "add",
"violations": []
},
{
"zone": "Europe/Oslo",
"language": "nb",
"state": "edit",
"violations": []
},
{
"zone": "Europe/Oslo",
"language": "nb",
"state": "add",
"violations": []
},
{
"zone": "Pacific/Kiritimati",
"language": "en",
"state": "edit",
"violations": []
},
{
"zone": "Pacific/Kiritimati",
"language": "en",
"state": "add",
"violations": []
},
{
"zone": "Pacific/Kiritimati",
"language": "nb",
"state": "edit",
"violations": []
},
{
"zone": "Pacific/Kiritimati",
"language": "nb",
"state": "add",
"violations": []
}
],
"writes": [
{
"zone": "America/Los_Angeles",
"language": "en",
"method": "PUT",
"body": {
"expectedVersion": 7,
"jobTitle": "Retain teacher draft",
"companyId": 1,
"status": "Applied",
"statusChangedAt": null,
"responseReceived": true,
"responseDate": "2026-11-24",
"location": null,
"salary": null,
"salaryMin": null,
"salaryMax": null,
"salaryCurrency": null,
"salaryPeriod": null,
"nextAction": null,
"followUpAt": "2026-11-24",
"notes": null,
"description": null,
"translatedDescription": null,
"descriptionLanguage": null,
"tags": null,
"deadline": "2026-11-24",
"coverLetterText": null,
"dateApplied": "2026-11-24",
"jobUrl": null
}
},
{
"zone": "America/Los_Angeles",
"language": "en",
"method": "POST",
"body": {
"jobTitle": "Teacher application",
"companyId": 1,
"status": "Saved",
"location": "",
"salary": "",
"salaryMin": null,
"salaryMax": null,
"salaryCurrency": null,
"salaryPeriod": null,
"nextAction": null,
"followUpAt": null,
"jobUrl": "",
"source": null,
"countryCode": null,
"description": null,
"translatedDescription": null,
"descriptionLanguage": null,
"tags": null,
"deadline": "2026-11-24",
"notes": "",
"coverLetterText": null,
"dateApplied": "2026-11-24"
}
},
{
"zone": "America/Los_Angeles",
"language": "nb",
"method": "PUT",
"body": {
"expectedVersion": 7,
"jobTitle": "Retain teacher draft",
"companyId": 1,
"status": "Applied",
"statusChangedAt": null,
"responseReceived": true,
"responseDate": "2026-11-24",
"location": null,
"salary": null,
"salaryMin": null,
"salaryMax": null,
"salaryCurrency": null,
"salaryPeriod": null,
"nextAction": null,
"followUpAt": "2026-11-24",
"notes": null,
"description": null,
"translatedDescription": null,
"descriptionLanguage": null,
"tags": null,
"deadline": "2026-11-24",
"coverLetterText": null,
"dateApplied": "2026-11-24",
"jobUrl": null
}
},
{
"zone": "America/Los_Angeles",
"language": "nb",
"method": "POST",
"body": {
"jobTitle": "Teacher application",
"companyId": 1,
"status": "Saved",
"location": "",
"salary": "",
"salaryMin": null,
"salaryMax": null,
"salaryCurrency": null,
"salaryPeriod": null,
"nextAction": null,
"followUpAt": null,
"jobUrl": "",
"source": null,
"countryCode": null,
"description": null,
"translatedDescription": null,
"descriptionLanguage": null,
"tags": null,
"deadline": "2026-11-24",
"notes": "",
"coverLetterText": null,
"dateApplied": "2026-11-24"
}
},
{
"zone": "Europe/Oslo",
"language": "en",
"method": "PUT",
"body": {
"expectedVersion": 7,
"jobTitle": "Retain teacher draft",
"companyId": 1,
"status": "Applied",
"statusChangedAt": null,
"responseReceived": true,
"responseDate": "2026-11-24",
"location": null,
"salary": null,
"salaryMin": null,
"salaryMax": null,
"salaryCurrency": null,
"salaryPeriod": null,
"nextAction": null,
"followUpAt": "2026-11-24",
"notes": null,
"description": null,
"translatedDescription": null,
"descriptionLanguage": null,
"tags": null,
"deadline": "2026-11-24",
"coverLetterText": null,
"dateApplied": "2026-11-24",
"jobUrl": null
}
},
{
"zone": "Europe/Oslo",
"language": "en",
"method": "POST",
"body": {
"jobTitle": "Teacher application",
"companyId": 1,
"status": "Saved",
"location": "",
"salary": "",
"salaryMin": null,
"salaryMax": null,
"salaryCurrency": null,
"salaryPeriod": null,
"nextAction": null,
"followUpAt": null,
"jobUrl": "",
"source": null,
"countryCode": null,
"description": null,
"translatedDescription": null,
"descriptionLanguage": null,
"tags": null,
"deadline": "2026-11-24",
"notes": "",
"coverLetterText": null,
"dateApplied": "2026-11-24"
}
},
{
"zone": "Europe/Oslo",
"language": "nb",
"method": "PUT",
"body": {
"expectedVersion": 7,
"jobTitle": "Retain teacher draft",
"companyId": 1,
"status": "Applied",
"statusChangedAt": null,
"responseReceived": true,
"responseDate": "2026-11-24",
"location": null,
"salary": null,
"salaryMin": null,
"salaryMax": null,
"salaryCurrency": null,
"salaryPeriod": null,
"nextAction": null,
"followUpAt": "2026-11-24",
"notes": null,
"description": null,
"translatedDescription": null,
"descriptionLanguage": null,
"tags": null,
"deadline": "2026-11-24",
"coverLetterText": null,
"dateApplied": "2026-11-24",
"jobUrl": null
}
},
{
"zone": "Europe/Oslo",
"language": "nb",
"method": "POST",
"body": {
"jobTitle": "Teacher application",
"companyId": 1,
"status": "Saved",
"location": "",
"salary": "",
"salaryMin": null,
"salaryMax": null,
"salaryCurrency": null,
"salaryPeriod": null,
"nextAction": null,
"followUpAt": null,
"jobUrl": "",
"source": null,
"countryCode": null,
"description": null,
"translatedDescription": null,
"descriptionLanguage": null,
"tags": null,
"deadline": "2026-11-24",
"notes": "",
"coverLetterText": null,
"dateApplied": "2026-11-24"
}
},
{
"zone": "Pacific/Kiritimati",
"language": "en",
"method": "PUT",
"body": {
"expectedVersion": 7,
"jobTitle": "Retain teacher draft",
"companyId": 1,
"status": "Applied",
"statusChangedAt": null,
"responseReceived": true,
"responseDate": "2026-11-24",
"location": null,
"salary": null,
"salaryMin": null,
"salaryMax": null,
"salaryCurrency": null,
"salaryPeriod": null,
"nextAction": null,
"followUpAt": "2026-11-24",
"notes": null,
"description": null,
"translatedDescription": null,
"descriptionLanguage": null,
"tags": null,
"deadline": "2026-11-24",
"coverLetterText": null,
"dateApplied": "2026-11-24",
"jobUrl": null
}
},
{
"zone": "Pacific/Kiritimati",
"language": "en",
"method": "POST",
"body": {
"jobTitle": "Teacher application",
"companyId": 1,
"status": "Saved",
"location": "",
"salary": "",
"salaryMin": null,
"salaryMax": null,
"salaryCurrency": null,
"salaryPeriod": null,
"nextAction": null,
"followUpAt": null,
"jobUrl": "",
"source": null,
"countryCode": null,
"description": null,
"translatedDescription": null,
"descriptionLanguage": null,
"tags": null,
"deadline": "2026-11-24",
"notes": "",
"coverLetterText": null,
"dateApplied": "2026-11-24"
}
},
{
"zone": "Pacific/Kiritimati",
"language": "nb",
"method": "PUT",
"body": {
"expectedVersion": 7,
"jobTitle": "Retain teacher draft",
"companyId": 1,
"status": "Applied",
"statusChangedAt": null,
"responseReceived": true,
"responseDate": "2026-11-24",
"location": null,
"salary": null,
"salaryMin": null,
"salaryMax": null,
"salaryCurrency": null,
"salaryPeriod": null,
"nextAction": null,
"followUpAt": "2026-11-24",
"notes": null,
"description": null,
"translatedDescription": null,
"descriptionLanguage": null,
"tags": null,
"deadline": "2026-11-24",
"coverLetterText": null,
"dateApplied": "2026-11-24",
"jobUrl": null
}
},
{
"zone": "Pacific/Kiritimati",
"language": "nb",
"method": "POST",
"body": {
"jobTitle": "Teacher application",
"companyId": 1,
"status": "Saved",
"location": "",
"salary": "",
"salaryMin": null,
"salaryMax": null,
"salaryCurrency": null,
"salaryPeriod": null,
"nextAction": null,
"followUpAt": null,
"jobUrl": "",
"source": null,
"countryCode": null,
"description": null,
"translatedDescription": null,
"descriptionLanguage": null,
"tags": null,
"deadline": "2026-11-24",
"notes": "",
"coverLetterText": null,
"dateApplied": "2026-11-24"
}
}
],
"errors": [],
"unavailableReads": [],
"blockedExternal": [
"https://fonts.googleapis.com",
"https://fonts.googleapis.com",
"https://fonts.googleapis.com",
"https://fonts.googleapis.com",
"https://fonts.googleapis.com",
"https://fonts.googleapis.com"
]
}
@@ -0,0 +1,7 @@
Checking America-Los_Angeles-en
Checking America-Los_Angeles-nb
Checking Europe-Oslo-en
Checking Europe-Oslo-nb
Checking Pacific-Kiritimati-en
Checking Pacific-Kiritimati-nb
PASS 6 real-widget journeys, 12 axe captures, 12 intercepted fixture writes
@@ -0,0 +1,235 @@
[
{
tag: 'INPUT',
role: 'combobox',
label: null,
value: 'Synthetic school',
text: ''
},
{ tag: 'BUTTON', role: null, label: 'Clear', value: '', text: '' },
{ tag: 'BUTTON', role: null, label: 'Open', value: '', text: '' },
{ tag: 'INPUT', role: null, label: null, value: 'Teacher', text: '' },
{
tag: 'DIV',
role: 'group',
label: null,
value: undefined,
text: '11/23/2026Søkt dato'
},
{
tag: 'SPAN',
role: 'spinbutton',
label: 'Month',
value: undefined,
text: '11'
},
{
tag: 'SPAN',
role: 'spinbutton',
label: 'Day',
value: undefined,
text: '23'
},
{
tag: 'SPAN',
role: 'spinbutton',
label: 'Year',
value: undefined,
text: '2026'
},
{
tag: 'BUTTON',
role: null,
label: 'Choose date, selected date is Nov 23, 2026',
value: '',
text: ''
},
{
tag: 'INPUT',
role: null,
label: null,
value: '11/23/2026',
text: ''
},
{ tag: 'INPUT', role: null, label: null, value: '', text: '' },
{ tag: 'INPUT', role: null, label: null, value: 'Applied', text: '' },
{
tag: 'DIV',
role: 'group',
label: null,
value: undefined,
text: '11/11/2026Status endret'
},
{
tag: 'SPAN',
role: 'spinbutton',
label: 'Month',
value: undefined,
text: '11'
},
{
tag: 'SPAN',
role: 'spinbutton',
label: 'Day',
value: undefined,
text: '11'
},
{
tag: 'SPAN',
role: 'spinbutton',
label: 'Year',
value: undefined,
text: '2026'
},
{
tag: 'BUTTON',
role: null,
label: 'Choose date, selected date is Nov 11, 2026',
value: '',
text: ''
},
{
tag: 'INPUT',
role: null,
label: null,
value: '11/11/2026',
text: ''
},
{ tag: 'INPUT', role: null, label: null, value: 'on', text: '' },
{
tag: 'DIV',
role: 'group',
label: null,
value: undefined,
text: '11/23/2026Svar mottatt dato'
},
{
tag: 'SPAN',
role: 'spinbutton',
label: 'Month',
value: undefined,
text: '11'
},
{
tag: 'SPAN',
role: 'spinbutton',
label: 'Day',
value: undefined,
text: '23'
},
{
tag: 'SPAN',
role: 'spinbutton',
label: 'Year',
value: undefined,
text: '2026'
},
{
tag: 'BUTTON',
role: null,
label: 'Choose date, selected date is Nov 23, 2026',
value: '',
text: ''
},
{
tag: 'INPUT',
role: null,
label: null,
value: '11/23/2026',
text: ''
},
{ tag: 'INPUT', role: null, label: null, value: '', text: '' },
{
tag: 'DIV',
role: 'group',
label: null,
value: undefined,
text: '11/23/2026Følg opp den'
},
{
tag: 'SPAN',
role: 'spinbutton',
label: 'Month',
value: undefined,
text: '11'
},
{
tag: 'SPAN',
role: 'spinbutton',
label: 'Day',
value: undefined,
text: '23'
},
{
tag: 'SPAN',
role: 'spinbutton',
label: 'Year',
value: undefined,
text: '2026'
},
{
tag: 'BUTTON',
role: null,
label: 'Choose date, selected date is Nov 23, 2026',
value: '',
text: ''
},
{
tag: 'INPUT',
role: null,
label: null,
value: '11/23/2026',
text: ''
},
{ tag: 'INPUT', role: null, label: null, value: '', text: '' },
{ tag: 'INPUT', role: null, label: null, value: '', text: '' },
{ tag: 'INPUT', role: null, label: null, value: '', text: '' },
{ tag: 'INPUT', role: null, label: null, value: '', text: '' },
{ tag: 'INPUT', role: null, label: null, value: '', text: '' },
{
tag: 'DIV',
role: 'group',
label: null,
value: undefined,
text: '11/23/2026Frist'
},
{
tag: 'SPAN',
role: 'spinbutton',
label: 'Month',
value: undefined,
text: '11'
},
{
tag: 'SPAN',
role: 'spinbutton',
label: 'Day',
value: undefined,
text: '23'
},
{
tag: 'SPAN',
role: 'spinbutton',
label: 'Year',
value: undefined,
text: '2026'
},
{
tag: 'BUTTON',
role: null,
label: 'Choose date, selected date is Nov 23, 2026',
value: '',
text: ''
},
{
tag: 'INPUT',
role: null,
label: null,
value: '11/23/2026',
text: ''
},
{ tag: 'INPUT', role: null, label: null, value: '', text: '' },
{ tag: 'INPUT', role: 'combobox', label: null, value: '', text: '' },
{ tag: 'BUTTON', role: null, label: null, value: '', text: 'Avbryt' },
{ tag: 'BUTTON', role: null, label: null, value: '', text: 'Lagre' }
]
@@ -0,0 +1,552 @@
{
"journeys": [
{
"zone": "America/Los_Angeles",
"language": "en",
"width": 1440,
"touch": false,
"theme": "light",
"sevenCalendarSelections": "PASS",
"localeDraftRetention": "PASS",
"localToday": 22,
"fixtureSaveCreate": "PASS"
},
{
"zone": "America/Los_Angeles",
"language": "nb",
"width": 390,
"touch": true,
"theme": "dark",
"sevenCalendarSelections": "PASS",
"localeDraftRetention": "PASS",
"localToday": 22,
"fixtureSaveCreate": "PASS"
},
{
"zone": "Europe/Oslo",
"language": "en",
"width": 1440,
"touch": false,
"theme": "light",
"sevenCalendarSelections": "PASS",
"localeDraftRetention": "PASS",
"localToday": 23,
"fixtureSaveCreate": "PASS"
},
{
"zone": "Europe/Oslo",
"language": "nb",
"width": 390,
"touch": true,
"theme": "dark",
"sevenCalendarSelections": "PASS",
"localeDraftRetention": "PASS",
"localToday": 23,
"fixtureSaveCreate": "PASS"
},
{
"zone": "Pacific/Kiritimati",
"language": "en",
"width": 1440,
"touch": false,
"theme": "light",
"sevenCalendarSelections": "PASS",
"localeDraftRetention": "PASS",
"localToday": 23,
"fixtureSaveCreate": "PASS"
},
{
"zone": "Pacific/Kiritimati",
"language": "nb",
"width": 390,
"touch": true,
"theme": "dark",
"sevenCalendarSelections": "PASS",
"localeDraftRetention": "PASS",
"localToday": 23,
"fixtureSaveCreate": "PASS"
}
],
"captures": [
{
"zone": "America/Los_Angeles",
"language": "en",
"state": "calendar",
"violations": []
},
{
"zone": "America/Los_Angeles",
"language": "en",
"state": "edit",
"violations": []
},
{
"zone": "America/Los_Angeles",
"language": "en",
"state": "add",
"violations": []
},
{
"zone": "America/Los_Angeles",
"language": "nb",
"state": "calendar",
"violations": []
},
{
"zone": "America/Los_Angeles",
"language": "nb",
"state": "edit",
"violations": []
},
{
"zone": "America/Los_Angeles",
"language": "nb",
"state": "add",
"violations": []
},
{
"zone": "Europe/Oslo",
"language": "en",
"state": "calendar",
"violations": []
},
{
"zone": "Europe/Oslo",
"language": "en",
"state": "edit",
"violations": []
},
{
"zone": "Europe/Oslo",
"language": "en",
"state": "add",
"violations": []
},
{
"zone": "Europe/Oslo",
"language": "nb",
"state": "calendar",
"violations": []
},
{
"zone": "Europe/Oslo",
"language": "nb",
"state": "edit",
"violations": []
},
{
"zone": "Europe/Oslo",
"language": "nb",
"state": "add",
"violations": []
},
{
"zone": "Pacific/Kiritimati",
"language": "en",
"state": "calendar",
"violations": []
},
{
"zone": "Pacific/Kiritimati",
"language": "en",
"state": "edit",
"violations": []
},
{
"zone": "Pacific/Kiritimati",
"language": "en",
"state": "add",
"violations": []
},
{
"zone": "Pacific/Kiritimati",
"language": "nb",
"state": "calendar",
"violations": []
},
{
"zone": "Pacific/Kiritimati",
"language": "nb",
"state": "edit",
"violations": []
},
{
"zone": "Pacific/Kiritimati",
"language": "nb",
"state": "add",
"violations": []
}
],
"writes": [
{
"zone": "America/Los_Angeles",
"language": "en",
"method": "PUT",
"body": {
"expectedVersion": 7,
"jobTitle": "Retain teacher draft",
"companyId": 1,
"status": "Applied",
"statusChangedAt": null,
"responseReceived": true,
"responseDate": "2026-11-24",
"location": null,
"salary": null,
"salaryMin": null,
"salaryMax": null,
"salaryCurrency": null,
"salaryPeriod": null,
"nextAction": null,
"followUpAt": "2026-11-24",
"notes": null,
"description": null,
"translatedDescription": null,
"descriptionLanguage": null,
"tags": null,
"deadline": "2026-11-24",
"coverLetterText": null,
"dateApplied": "2026-11-24",
"jobUrl": null
}
},
{
"zone": "America/Los_Angeles",
"language": "en",
"method": "POST",
"body": {
"jobTitle": "Teacher application",
"companyId": 1,
"status": "Saved",
"location": "",
"salary": "",
"salaryMin": null,
"salaryMax": null,
"salaryCurrency": null,
"salaryPeriod": null,
"nextAction": null,
"followUpAt": null,
"jobUrl": "",
"source": null,
"countryCode": null,
"description": null,
"translatedDescription": null,
"descriptionLanguage": null,
"tags": null,
"deadline": "2026-11-24",
"notes": "",
"coverLetterText": null,
"dateApplied": "2026-11-24"
}
},
{
"zone": "America/Los_Angeles",
"language": "nb",
"method": "PUT",
"body": {
"expectedVersion": 7,
"jobTitle": "Retain teacher draft",
"companyId": 1,
"status": "Applied",
"statusChangedAt": null,
"responseReceived": true,
"responseDate": "2026-11-24",
"location": null,
"salary": null,
"salaryMin": null,
"salaryMax": null,
"salaryCurrency": null,
"salaryPeriod": null,
"nextAction": null,
"followUpAt": "2026-11-24",
"notes": null,
"description": null,
"translatedDescription": null,
"descriptionLanguage": null,
"tags": null,
"deadline": "2026-11-24",
"coverLetterText": null,
"dateApplied": "2026-11-24",
"jobUrl": null
}
},
{
"zone": "America/Los_Angeles",
"language": "nb",
"method": "POST",
"body": {
"jobTitle": "Teacher application",
"companyId": 1,
"status": "Saved",
"location": "",
"salary": "",
"salaryMin": null,
"salaryMax": null,
"salaryCurrency": null,
"salaryPeriod": null,
"nextAction": null,
"followUpAt": null,
"jobUrl": "",
"source": null,
"countryCode": null,
"description": null,
"translatedDescription": null,
"descriptionLanguage": null,
"tags": null,
"deadline": "2026-11-24",
"notes": "",
"coverLetterText": null,
"dateApplied": "2026-11-24"
}
},
{
"zone": "Europe/Oslo",
"language": "en",
"method": "PUT",
"body": {
"expectedVersion": 7,
"jobTitle": "Retain teacher draft",
"companyId": 1,
"status": "Applied",
"statusChangedAt": null,
"responseReceived": true,
"responseDate": "2026-11-24",
"location": null,
"salary": null,
"salaryMin": null,
"salaryMax": null,
"salaryCurrency": null,
"salaryPeriod": null,
"nextAction": null,
"followUpAt": "2026-11-24",
"notes": null,
"description": null,
"translatedDescription": null,
"descriptionLanguage": null,
"tags": null,
"deadline": "2026-11-24",
"coverLetterText": null,
"dateApplied": "2026-11-24",
"jobUrl": null
}
},
{
"zone": "Europe/Oslo",
"language": "en",
"method": "POST",
"body": {
"jobTitle": "Teacher application",
"companyId": 1,
"status": "Saved",
"location": "",
"salary": "",
"salaryMin": null,
"salaryMax": null,
"salaryCurrency": null,
"salaryPeriod": null,
"nextAction": null,
"followUpAt": null,
"jobUrl": "",
"source": null,
"countryCode": null,
"description": null,
"translatedDescription": null,
"descriptionLanguage": null,
"tags": null,
"deadline": "2026-11-24",
"notes": "",
"coverLetterText": null,
"dateApplied": "2026-11-24"
}
},
{
"zone": "Europe/Oslo",
"language": "nb",
"method": "PUT",
"body": {
"expectedVersion": 7,
"jobTitle": "Retain teacher draft",
"companyId": 1,
"status": "Applied",
"statusChangedAt": null,
"responseReceived": true,
"responseDate": "2026-11-24",
"location": null,
"salary": null,
"salaryMin": null,
"salaryMax": null,
"salaryCurrency": null,
"salaryPeriod": null,
"nextAction": null,
"followUpAt": "2026-11-24",
"notes": null,
"description": null,
"translatedDescription": null,
"descriptionLanguage": null,
"tags": null,
"deadline": "2026-11-24",
"coverLetterText": null,
"dateApplied": "2026-11-24",
"jobUrl": null
}
},
{
"zone": "Europe/Oslo",
"language": "nb",
"method": "POST",
"body": {
"jobTitle": "Teacher application",
"companyId": 1,
"status": "Saved",
"location": "",
"salary": "",
"salaryMin": null,
"salaryMax": null,
"salaryCurrency": null,
"salaryPeriod": null,
"nextAction": null,
"followUpAt": null,
"jobUrl": "",
"source": null,
"countryCode": null,
"description": null,
"translatedDescription": null,
"descriptionLanguage": null,
"tags": null,
"deadline": "2026-11-24",
"notes": "",
"coverLetterText": null,
"dateApplied": "2026-11-24"
}
},
{
"zone": "Pacific/Kiritimati",
"language": "en",
"method": "PUT",
"body": {
"expectedVersion": 7,
"jobTitle": "Retain teacher draft",
"companyId": 1,
"status": "Applied",
"statusChangedAt": null,
"responseReceived": true,
"responseDate": "2026-11-24",
"location": null,
"salary": null,
"salaryMin": null,
"salaryMax": null,
"salaryCurrency": null,
"salaryPeriod": null,
"nextAction": null,
"followUpAt": "2026-11-24",
"notes": null,
"description": null,
"translatedDescription": null,
"descriptionLanguage": null,
"tags": null,
"deadline": "2026-11-24",
"coverLetterText": null,
"dateApplied": "2026-11-24",
"jobUrl": null
}
},
{
"zone": "Pacific/Kiritimati",
"language": "en",
"method": "POST",
"body": {
"jobTitle": "Teacher application",
"companyId": 1,
"status": "Saved",
"location": "",
"salary": "",
"salaryMin": null,
"salaryMax": null,
"salaryCurrency": null,
"salaryPeriod": null,
"nextAction": null,
"followUpAt": null,
"jobUrl": "",
"source": null,
"countryCode": null,
"description": null,
"translatedDescription": null,
"descriptionLanguage": null,
"tags": null,
"deadline": "2026-11-24",
"notes": "",
"coverLetterText": null,
"dateApplied": "2026-11-24"
}
},
{
"zone": "Pacific/Kiritimati",
"language": "nb",
"method": "PUT",
"body": {
"expectedVersion": 7,
"jobTitle": "Retain teacher draft",
"companyId": 1,
"status": "Applied",
"statusChangedAt": null,
"responseReceived": true,
"responseDate": "2026-11-24",
"location": null,
"salary": null,
"salaryMin": null,
"salaryMax": null,
"salaryCurrency": null,
"salaryPeriod": null,
"nextAction": null,
"followUpAt": "2026-11-24",
"notes": null,
"description": null,
"translatedDescription": null,
"descriptionLanguage": null,
"tags": null,
"deadline": "2026-11-24",
"coverLetterText": null,
"dateApplied": "2026-11-24",
"jobUrl": null
}
},
{
"zone": "Pacific/Kiritimati",
"language": "nb",
"method": "POST",
"body": {
"jobTitle": "Teacher application",
"companyId": 1,
"status": "Saved",
"location": "",
"salary": "",
"salaryMin": null,
"salaryMax": null,
"salaryCurrency": null,
"salaryPeriod": null,
"nextAction": null,
"followUpAt": null,
"jobUrl": "",
"source": null,
"countryCode": null,
"description": null,
"translatedDescription": null,
"descriptionLanguage": null,
"tags": null,
"deadline": "2026-11-24",
"notes": "",
"coverLetterText": null,
"dateApplied": "2026-11-24"
}
}
],
"errors": [],
"unavailableReads": [],
"blockedExternal": [
"https://fonts.googleapis.com",
"https://fonts.googleapis.com",
"https://fonts.googleapis.com",
"https://fonts.googleapis.com",
"https://fonts.googleapis.com",
"https://fonts.googleapis.com"
]
}
@@ -0,0 +1,7 @@
Checking America-Los_Angeles-en
Checking America-Los_Angeles-nb
Checking Europe-Oslo-en
Checking Europe-Oslo-nb
Checking Pacific-Kiritimati-en
Checking Pacific-Kiritimati-nb
PASS 6 real-widget journeys, 18 axe captures, 12 intercepted fixture writes
+23
View File
@@ -0,0 +1,23 @@
> job-tracker-ui@0.1.0 build
> node --max-old-space-size=4096 ./node_modules/next/dist/bin/next build
▲ Next.js 16.4.1 (Turbopack)
✓ Running next.config.js took 24ms
Creating an optimized production build ...
✓ Compiled successfully in 392ms
Running TypeScript ...
Finished TypeScript in 4.6s ...
Collecting page data using 4 workers ...
Generating static pages using 4 workers (0/3) ...
✓ Generating static pages using 4 workers (3/3) in 804ms
Finalizing page optimization ...
Route (app)
┌ ○ /
└ ○ /_not-found
○ (Static) prerendered as static content
@@ -0,0 +1,52 @@
> job-tracker-ui@0.1.0 build
> node --max-old-space-size=4096 ./node_modules/next/dist/bin/next build
▲ Next.js 16.4.1 (Turbopack)
✓ Running next.config.js took 26ms
Creating an optimized production build ...
✓ Compiled successfully in 975ms
Running TypeScript ...
src/date-picker-localisation.test.tsx(24,56): error TS2769: No overload matches this call.
Overload 1 of 2, '(role: ByRoleMatcher, options?: ByRoleOptions | undefined): HTMLElement', gave the following error.
Object literal may only specify known properties, and 'exact' does not exist in type 'ByRoleOptions'.
Overload 2 of 2, '(role: ByRoleMatcher, options?: ByRoleOptions | undefined): HTMLElement', gave the following error.
Object literal may only specify known properties, and 'exact' does not exist in type 'ByRoleOptions'.
src/date-picker-localisation.test.tsx(25,58): error TS2769: No overload matches this call.
Overload 1 of 2, '(role: ByRoleMatcher, options?: ByRoleOptions | undefined): HTMLElement', gave the following error.
Object literal may only specify known properties, and 'exact' does not exist in type 'ByRoleOptions'.
Overload 2 of 2, '(role: ByRoleMatcher, options?: ByRoleOptions | undefined): HTMLElement', gave the following error.
Object literal may only specify known properties, and 'exact' does not exist in type 'ByRoleOptions'.
src/date-picker-localisation.test.tsx(26,55): error TS2769: No overload matches this call.
Overload 1 of 2, '(role: ByRoleMatcher, options?: ByRoleOptions | undefined): HTMLElement', gave the following error.
Object literal may only specify known properties, and 'exact' does not exist in type 'ByRoleOptions'.
Overload 2 of 2, '(role: ByRoleMatcher, options?: ByRoleOptions | undefined): HTMLElement', gave the following error.
Object literal may only specify known properties, and 'exact' does not exist in type 'ByRoleOptions'.
src/date-picker-localisation.test.tsx(35,56): error TS2769: No overload matches this call.
Overload 1 of 2, '(role: ByRoleMatcher, options?: ByRoleOptions | undefined): HTMLElement', gave the following error.
Object literal may only specify known properties, and 'exact' does not exist in type 'ByRoleOptions'.
Overload 2 of 2, '(role: ByRoleMatcher, options?: ByRoleOptions | undefined): HTMLElement', gave the following error.
Object literal may only specify known properties, and 'exact' does not exist in type 'ByRoleOptions'.
src/date-picker-localisation.test.tsx(36,64): error TS2769: No overload matches this call.
Overload 1 of 2, '(role: ByRoleMatcher, options?: ByRoleOptions | undefined): HTMLElement', gave the following error.
Object literal may only specify known properties, and 'exact' does not exist in type 'ByRoleOptions'.
Overload 2 of 2, '(role: ByRoleMatcher, options?: ByRoleOptions | undefined): HTMLElement', gave the following error.
Object literal may only specify known properties, and 'exact' does not exist in type 'ByRoleOptions'.
src/date-picker-localisation.test.tsx(37,56): error TS2769: No overload matches this call.
Overload 1 of 2, '(role: ByRoleMatcher, options?: ByRoleOptions | undefined): HTMLElement', gave the following error.
Object literal may only specify known properties, and 'exact' does not exist in type 'ByRoleOptions'.
Overload 2 of 2, '(role: ByRoleMatcher, options?: ByRoleOptions | undefined): HTMLElement', gave the following error.
Object literal may only specify known properties, and 'exact' does not exist in type 'ByRoleOptions'.
src/date-picker-localisation.test.tsx(39,65): error TS2769: No overload matches this call.
Overload 1 of 2, '(role: ByRoleMatcher, options?: ByRoleOptions | undefined): HTMLElement', gave the following error.
Object literal may only specify known properties, and 'exact' does not exist in type 'ByRoleOptions'.
Overload 2 of 2, '(role: ByRoleMatcher, options?: ByRoleOptions | undefined): HTMLElement', gave the following error.
Object literal may only specify known properties, and 'exact' does not exist in type 'ByRoleOptions'.
src/date-picker-localisation.test.tsx(40,56): error TS2769: No overload matches this call.
Overload 1 of 2, '(role: ByRoleMatcher, options?: ByRoleOptions | undefined): HTMLElement', gave the following error.
Object literal may only specify known properties, and 'exact' does not exist in type 'ByRoleOptions'.
Overload 2 of 2, '(role: ByRoleMatcher, options?: ByRoleOptions | undefined): HTMLElement', gave the following error.
Object literal may only specify known properties, and 'exact' does not exist in type 'ByRoleOptions'.
Failed to type check.
@@ -0,0 +1,10 @@
{
"journeys": [],
"captures": [],
"writes": [],
"errors": [],
"unavailableReads": [],
"blockedExternal": [
"https://fonts.googleapis.com"
]
}
@@ -0,0 +1,136 @@
Checking America-Los_Angeles-en
node:internal/modules/run_main:107
triggerUncaughtException(
^
ExpectError: expect(locator).toHaveText(expected) failed
Locator: getByRole('dialog', { name: 'Edit job', exact: true }).getByRole('group', { name: 'Frist', exact: true }).getByRole('spinbutton', { name: 'Dag', exact: true })
Expected: "23"
Timeout: 5000ms
Error: element(s) not found
Call log:
- Expect "to.have.text" with timeout 5000ms
- waiting for getByRole('dialog', { name: 'Edit job', exact: true }).getByRole('group', { name: 'Frist', exact: true }).getByRole('spinbutton', { name: 'Dag', exact: true })
at captureRawStack (C:\Users\Connor\.codex\worktrees\direct-roadmap-integration\Job tracker\job-tracker-ui\node_modules\playwright-core\lib\coreBundle.js:9681:17)
at callMatcherAsStep (C:\Users\Connor\.codex\worktrees\direct-roadmap-integration\Job tracker\job-tracker-ui\node_modules\playwright\lib\matchers\expect.js:13310:57)
at Object.toHaveText (C:\Users\Connor\.codex\worktrees\direct-roadmap-integration\Job tracker\job-tracker-ui\node_modules\playwright\lib\matchers\expect.js:13300:23)
at assertDay (C:\Users\Connor\.codex\worktrees\direct-roadmap-integration\Job tracker\job-tracker-ui\scripts\verify-job-date-widgets.mjs:56:105)
at C:\Users\Connor\.codex\worktrees\direct-roadmap-integration\Job tracker\job-tracker-ui\scripts\verify-job-date-widgets.mjs:84:11 {
matcherResult: {
name: 'toHaveText',
expected: '23',
message: 'expect(locator).toHaveText(expected) failed\n' +
'\n' +
"Locator: getByRole('dialog', { name: 'Edit job', exact: true }).getByRole('group', { name: 'Frist', exact: true }).getByRole('spinbutton', { name: 'Dag', exact: true })\n" +
'Expected: "23"\n' +
'Timeout: 5000ms\n' +
'Error: element(s) not found\n' +
'\n' +
'Call log:\n' +
' - Expect "to.have.text" with timeout 5000ms\n' +
" - waiting for getByRole('dialog', { name: 'Edit job', exact: true }).getByRole('group', { name: 'Frist', exact: true }).getByRole('spinbutton', { name: 'Dag', exact: true })\n",
pass: false,
actual: undefined,
log: [
' - Expect "to.have.text" with timeout 5000ms',
" - waiting for getByRole('dialog', { name: 'Edit job', exact: true }).getByRole('group', { name: 'Frist', exact: true }).getByRole('spinbutton', { name: 'Dag', exact: true })"
],
timeout: 5000,
ariaSnapshot: '- dialog "Rediger jobb":\n' +
' - heading "Rediger jobb" [level=2]\n' +
' - paragraph: Oppdater jobbdetaljer, status i tidslinjen, dokumenter og notater fra ett redigeringsområde.\n' +
' - text: Søknadsdetaljer Selskap\n' +
' - combobox "Selskap": Synthetic school\n' +
' - button "Open"\n' +
' - text: Stillingstittel\n' +
' - textbox "Stillingstittel": Retain teacher draft\n' +
' - text: Søkt dato\n' +
' - group "Søkt dato":\n' +
' - spinbutton "Dag": "23"\n' +
' - text: .\n' +
' - spinbutton "Måned": "11"\n' +
' - text: .\n' +
' - spinbutton "År": "2026"\n' +
' - button "Velg dato, valgt dato er 23. nov. 2026"\n' +
' - text: Jobb-URL\n' +
' - textbox "Jobb-URL"\n' +
' - text: Statusoppdatering Gjeldende status\n' +
' - combobox "Gjeldende status Søkt": Søkt\n' +
' - text: Status endret\n' +
' - group "Status endret":\n' +
' - spinbutton "Dag": "23"\n' +
' - text: .\n' +
' - spinbutton "Måned": "11"\n' +
' - text: .\n' +
' - spinbutton "År": "2026"\n' +
' - button "Velg dato, valgt dato er 23. nov. 2026"\n' +
' - paragraph: Brukes bare når du endrer statusen.\n' +
' - checkbox "Svar mottatt" [checked]\n' +
' - text: Svar mottatt Svar mottatt dato\n' +
' - group "Svar mottatt dato":\n' +
' - spinbutton "Dag": "23"\n' +
' - text: .\n' +
' - spinbutton "Måned": "11"\n' +
' - text: .\n' +
' - spinbutton "År": "2026"\n' +
' - button "Velg dato, valgt dato er 23. nov. 2026"\n' +
' - text: Neste handling\n' +
' - textbox "Neste handling"\n' +
' - text: Følg opp den\n' +
' - group "Følg opp den":\n' +
' - spinbutton "Dag": "23"\n' +
' - text: .\n' +
' - spinbutton "Måned": "11"\n' +
' - text: .\n' +
' - spinbutton "År": "2026"\n' +
' - button "Velg dato, valgt dato er 23. nov. 2026"\n' +
' - text: Rolledetaljer Sted\n' +
' - textbox "Sted"\n' +
' - text: Lønn\n' +
' - textbox "Lønn"\n' +
' - text: Lønn fra\n' +
' - spinbutton "Lønn fra"\n' +
' - text: Lønn til\n' +
' - spinbutton "Lønn til"\n' +
' - text: Valuta\n' +
' - textbox "Valuta"\n' +
' - text: Per\n' +
' - combobox "Per":\n' +
' - option [selected]\n' +
' - option "År"\n' +
' - option "Måned"\n' +
' - option "Time"\n' +
' - text: Frist\n' +
' - group "Frist":\n' +
' - spinbutton "Dag": "23"\n' +
' - text: .\n' +
' - spinbutton "Måned": "11"\n' +
' - text: .\n' +
' - spinbutton "År": "2026"\n' +
' - button "Velg dato, valgt dato er 23. nov. 2026"\n' +
' - text: Språk i beskrivelse\n' +
' - textbox "Språk i beskrivelse"\n' +
' - text: Etiketter\n' +
' - combobox "Etiketter"\n' +
' - text: Notater\n' +
' - textbox "Notater"\n' +
' - paragraph: 0 tegn\n' +
' - text: Beskrivelse (original)\n' +
' - textbox "Beskrivelse (original)"\n' +
' - paragraph: 0 tegn\n' +
' - text: Oversatt beskrivelse\n' +
' - textbox "Oversatt beskrivelse"\n' +
' - paragraph: 0 tegn\n' +
' - text: Søknadsbrev\n' +
' - textbox "Søknadsbrev"\n' +
' - paragraph: 0 tegn\n' +
' - text: Vedleggssjekkliste CV mangler Søknadsbrev mangler Portefølje valgfri\n' +
' - button "Avbryt"\n' +
' - button "Lagre"'
}
}
Node.js v24.19.0
@@ -0,0 +1,10 @@
> job-tracker-ui@0.1.0 test
> jest --runInBand date-picker-localisation.test.tsx edit-job-revision.test.tsx job-date-picker-calendar.test.tsx theme-state.test.tsx
Test Suites: 4 passed, 4 total
Tests: 20 passed, 20 total
Snapshots: 0 total
Time: 12.354 s, estimated 16 s
Ran all test suites matching date-picker-localisation.test.tsx|edit-job-revision.test.tsx|job-date-picker-calendar.test.tsx|theme-state.test.tsx.
+4
View File
@@ -0,0 +1,4 @@
> job-tracker-ui@0.1.0 lint
> eslint . --max-warnings=0
+180
View File
@@ -0,0 +1,180 @@
{
"journeys": [
{
"zone": "America/Los_Angeles",
"language": "en",
"width": 1440,
"touch": false,
"theme": "light",
"sevenCalendarSelections": "PASS",
"localeDraftRetention": "PASS",
"localToday": 22,
"fixtureSaveCreate": "PASS"
},
{
"zone": "America/Los_Angeles",
"language": "nb",
"width": 390,
"touch": true,
"theme": "dark",
"sevenCalendarSelections": "PASS",
"localeDraftRetention": "PASS",
"localToday": 22,
"fixtureSaveCreate": "PASS"
}
],
"captures": [
{
"zone": "America/Los_Angeles",
"language": "en",
"state": "edit",
"violations": []
},
{
"zone": "America/Los_Angeles",
"language": "en",
"state": "add",
"violations": []
},
{
"zone": "America/Los_Angeles",
"language": "nb",
"state": "edit",
"violations": []
},
{
"zone": "America/Los_Angeles",
"language": "nb",
"state": "add",
"violations": []
}
],
"writes": [
{
"zone": "America/Los_Angeles",
"language": "en",
"method": "PUT",
"body": {
"expectedVersion": 7,
"jobTitle": "Retain teacher draft",
"companyId": 1,
"status": "Applied",
"statusChangedAt": null,
"responseReceived": true,
"responseDate": "2026-11-24",
"location": null,
"salary": null,
"salaryMin": null,
"salaryMax": null,
"salaryCurrency": null,
"salaryPeriod": null,
"nextAction": null,
"followUpAt": "2026-11-24",
"notes": null,
"description": null,
"translatedDescription": null,
"descriptionLanguage": null,
"tags": null,
"deadline": "2026-11-24",
"coverLetterText": null,
"dateApplied": "2026-11-24",
"jobUrl": null
}
},
{
"zone": "America/Los_Angeles",
"language": "en",
"method": "POST",
"body": {
"jobTitle": "Teacher application",
"companyId": 1,
"status": "Saved",
"location": "",
"salary": "",
"salaryMin": null,
"salaryMax": null,
"salaryCurrency": null,
"salaryPeriod": null,
"nextAction": null,
"followUpAt": null,
"jobUrl": "",
"source": null,
"countryCode": null,
"description": null,
"translatedDescription": null,
"descriptionLanguage": null,
"tags": null,
"deadline": "2026-11-24",
"notes": "",
"coverLetterText": null,
"dateApplied": "2026-11-24"
}
},
{
"zone": "America/Los_Angeles",
"language": "nb",
"method": "PUT",
"body": {
"expectedVersion": 7,
"jobTitle": "Retain teacher draft",
"companyId": 1,
"status": "Applied",
"statusChangedAt": null,
"responseReceived": true,
"responseDate": "2026-11-24",
"location": null,
"salary": null,
"salaryMin": null,
"salaryMax": null,
"salaryCurrency": null,
"salaryPeriod": null,
"nextAction": null,
"followUpAt": "2026-11-24",
"notes": null,
"description": null,
"translatedDescription": null,
"descriptionLanguage": null,
"tags": null,
"deadline": "2026-11-24",
"coverLetterText": null,
"dateApplied": "2026-11-24",
"jobUrl": null
}
},
{
"zone": "America/Los_Angeles",
"language": "nb",
"method": "POST",
"body": {
"jobTitle": "Teacher application",
"companyId": 1,
"status": "Saved",
"location": "",
"salary": "",
"salaryMin": null,
"salaryMax": null,
"salaryCurrency": null,
"salaryPeriod": null,
"nextAction": null,
"followUpAt": null,
"jobUrl": "",
"source": null,
"countryCode": null,
"description": null,
"translatedDescription": null,
"descriptionLanguage": null,
"tags": null,
"deadline": "2026-11-24",
"notes": "",
"coverLetterText": null,
"dateApplied": "2026-11-24"
}
}
],
"errors": [],
"unavailableReads": [],
"blockedExternal": [
"https://fonts.googleapis.com",
"https://fonts.googleapis.com"
]
}
@@ -0,0 +1,3 @@
Checking America-Los_Angeles-en
Checking America-Los_Angeles-nb
PASS 2 real-widget journeys, 4 axe captures, 4 intercepted fixture writes
+598
View File
@@ -0,0 +1,598 @@
> job-tracker-ui@0.1.0 test
> jest --runInBand date-picker-localisation.test.tsx
FAIL src/date-picker-localisation.test.tsx
● Bokmål localizes date sections, calendar controls and displayed format
TestingLibraryElementError: Unable to find an accessible element with the role "spinbutton" and name "Dag"
Here are the accessible roles:
textbox:
Name "Unsaved title":
<input
aria-label="Unsaved title"
value="Teacher draft"
/>
--------------------------------------------------
button:
Name "Bokmål":
<button />
Name "English":
<button />
Name "Choose date, selected date is Nov 23, 2026":
<button
aria-label="Choose date, selected date is Nov 23, 2026"
class="MuiButtonBase-root MuiIconButton-root MuiIconButton-edgeEnd MuiIconButton-sizeMedium css-1ysp02-MuiButtonBase-root-MuiIconButton-root"
tabindex="0"
type="button"
/>
--------------------------------------------------
group:
Name "Deadline":
<div
aria-invalid="false"
aria-labelledby="_r_0_-label"
class="MuiPickersInputBase-root MuiPickersOutlinedInput-root MuiPickersInputBase-colorPrimary MuiPickersInputBase-adornedEnd css-189if4m-MuiPickersInputBase-root-MuiPickersOutlinedInput-root"
role="group"
/>
--------------------------------------------------
spinbutton:
Name "Month":
<span
aria-disabled="false"
aria-label="Month"
aria-readonly="false"
aria-valuemax="12"
aria-valuemin="1"
aria-valuenow="11"
aria-valuetext="November"
autocapitalize="none"
autocorrect="off"
class="MuiPickersSectionList-sectionContent MuiPickersInputBase-sectionContent css-1jpopwz-MuiPickersSectionList-sectionContent-MuiPickersInputBase-sectionContent"
contenteditable="true"
inputmode="numeric"
role="spinbutton"
spellcheck="false"
tabindex="0"
/>
Name "Day":
<span
aria-disabled="false"
aria-label="Day"
aria-readonly="false"
aria-valuemax="30"
aria-valuemin="1"
aria-valuenow="23"
aria-valuetext="23rd"
autocapitalize="none"
autocorrect="off"
class="MuiPickersSectionList-sectionContent MuiPickersInputBase-sectionContent css-1jpopwz-MuiPickersSectionList-sectionContent-MuiPickersInputBase-sectionContent"
contenteditable="true"
inputmode="numeric"
role="spinbutton"
spellcheck="false"
tabindex="-1"
/>
Name "Year":
<span
aria-disabled="false"
aria-label="Year"
aria-readonly="false"
aria-valuemax="9999"
aria-valuemin="0"
aria-valuenow="2026"
autocapitalize="none"
autocorrect="off"
class="MuiPickersSectionList-sectionContent MuiPickersInputBase-sectionContent css-1jpopwz-MuiPickersSectionList-sectionContent-MuiPickersInputBase-sectionContent"
contenteditable="true"
inputmode="numeric"
role="spinbutton"
spellcheck="false"
tabindex="-1"
/>
--------------------------------------------------
Ignored nodes: comments, script, style
<body>
<div>
<input
aria-label="Unsaved title"
value="Teacher draft"
/>
<button>
Bokmål
</button>
<button>
English
</button>
<div
class="MuiFormControl-root MuiPickersTextField-root css-eqc3kq-MuiFormControl-root-MuiPickersTextField-root"
>
<label
class="MuiFormLabel-root MuiInputLabel-root MuiInputLabel-formControl MuiInputLabel-animated MuiInputLabel-shrink MuiInputLabel-outlined MuiFormLabel-colorPrimary MuiFormLabel-filled MuiInputLabel-root MuiInputLabel-formControl MuiInputLabel-animated MuiInputLabel-shrink MuiInputLabel-outlined css-18yassc-MuiFormLabel-root-MuiInputLabel-root"
data-shrink="true"
for="_r_0_"
id="_r_0_-label"
>
Deadline
</label>
<div
aria-invalid="false"
aria-labelledby="_r_0_-label"
class="MuiPickersInputBase-root MuiPickersOutlinedInput-root MuiPickersInputBase-colorPrimary MuiPickersInputBase-adornedEnd css-189if4m-MuiPickersInputBase-root-MuiPickersOutlinedInput-root"
role="group"
>
<div
class="MuiPickersSectionList-root MuiPickersInputBase-sectionsContainer css-w175m5-MuiPickersSectionList-root-MuiPickersInputBase-sectionsContainer-MuiPickersOutlinedInput-sectionsContainer"
contenteditable="false"
tabindex="0"
>
<span
class="MuiPickersSectionList-section css-joz0rk-MuiPickersSectionList-section-MuiPickersInputBase-section"
data-sectionindex="0"
>
<span
class="MuiPickersInputBase-sectionBefore css-w1q93y-MuiPickersSectionList-sectionSeparator-MuiPickersInputBase-separator"
/>
<span
aria-disabled="false"
aria-label="Month"
aria-readonly="false"
aria-valuemax="12"
aria-valuemin="1"
aria-valuenow="11"
aria-valuetext="November"
autocapitalize="none"
autocorrect="off"
class="MuiPickersSectionList-sectionContent MuiPickersInputBase-sectionContent css-1jpopwz-MuiPickersSectionList-sectionContent-MuiPickersInputBase-sectionContent"
contenteditable="true"
inputmode="numeric"
role="spinbutton"
spellcheck="false"
tabindex="0"
>
11
</span>
<span
class="MuiPickersInputBase-sectionAfter css-w1q93y-MuiPickersSectionList-sectionSeparator-MuiPickersInputBase-separator"
>
/
</span>
</span>
<span
class="MuiPickersSectionList-section css-joz0rk-MuiPickersSectionList-section-MuiPickersInputBase-section"
data-sectionindex="1"
>
<span
class="MuiPickersInputBase-sectionBefore css-w1q93y-MuiPickersSectionList-sectionSeparator-MuiPickersInputBase-separator"
/>
<span
aria-disabled="false"
aria-label="Day"
aria-readonly="false"
aria-valuemax="30"
aria-valuemin="1"
aria-valuenow="23"
aria-valuetext="23rd"
autocapitalize="none"
autocorrect="off"
class="MuiPickersSectionList-sectionContent MuiPickersInputBase-sectionContent css-1jpopwz-MuiPickersSectionList-sectionContent-MuiPickersInputBase-sectionContent"
contenteditable="true"
inputmode="numeric"
role="spinbutton"
spellcheck="false"
tabindex="-1"
>
23
</span>
<span
class="MuiPickersInputBase-sectionAfter css-w1q93y-MuiPickersSectionList-sectionSeparator-MuiPickersInputBase-separator"
>
/
</span>
</span>
<span
class="MuiPickersSectionList-section css-joz0rk-MuiPickersSectionList-section-MuiPickersInputBase-section"
data-sectionindex="2"
>
<span
class="MuiPickersInputBase-sectionBefore css-w1q93y-MuiPickersSectionList-sectionSeparator-MuiPickersInputBase-separator"
/>
<span
aria-disabled="false"
aria-label="Year"
aria-readonly="false"
aria-valuemax="9999"
aria-valuemin="0"
aria-valuenow="2026"
autocapitalize="none"
autocorrect="off"
class="MuiPickersSectionList-sectionContent MuiPickersInputBase-sectionContent css-1jpopwz-MuiPickersSectionList-sectionContent-MuiPickersInputBase-sectionContent"
contenteditable="true"
inputmode="numeric"
role="spinbutton"
spellcheck="false"
tabindex="-1"
>
2026
</span>
<span
class="MuiPickersInputBase-sectionAfter css-w1q93y-MuiPickersSectionList-sectionSeparator-MuiPickersInputBase-separator"
/>
</span>
</div>
<div
class="MuiInputAdornment-root MuiInputAdornment-positionEnd MuiInputAdornment-outlined MuiInputAdornment-sizeMedium css-elo8k2-MuiInputAdornment-root"
>
<button
aria-label="Choose date, selected date is Nov 23, 2026"
class="MuiButtonBase-root MuiIconButton-root MuiIconButton-edgeEnd MuiIconButton-sizeMedium css-1ysp02-MuiButtonBase-root-MuiIconButton-root"
tabindex="0"
type="button"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root MuiSvgIcon-fontSizeMedium css-1umw9bq-MuiSvgIcon-root"
data-testid="CalendarIcon"
focusable="false"
viewBox="0 0 24 24"
>
<path
d="M17 12h-5v5h5v-5zM16 1v2H8V1H6v2H5c-1.11 0-1.99.9-1.99 2L3 19c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2h-1V1h-2zm3 18H5V8h14v11z"
/>
</svg>
</button>
</div>
<fieldset
aria-hidden="true"
class="MuiPickersOutlinedInput-notchedOutline css-lqwr9g-MuiPickersOutlinedInput-notchedOutline"
>
<legend
class="css-kg5swy"
>
<span
class="css-w48gsk"
>
Deadline
</span>
</legend>
</fieldset>
<input
aria-hidden="true"
class="MuiPickersInputBase-input MuiPickersOutlinedInput-input css-31lcvl-MuiPickersInputBase-input"
id="_r_0_"
tabindex="-1"
value="11/23/2026"
/>
</div>
</div>
</div>
</body>
22 | localStorage.setItem("uiLanguage", "nb");
23 | render(<I18nProvider><DateLocalizationProvider><Form /></DateLocalizationProvider></I18nProvider>);
> 24 | expect(screen.getByRole("spinbutton", { name: "Dag", exact: true })).toHaveTextContent("23");
| ^
25 | expect(screen.getByRole("spinbutton", { name: "Måned", exact: true })).toHaveTextContent("11");
26 | expect(screen.getByRole("spinbutton", { name: "År", exact: true })).toHaveTextContent("2026");
27 | expect(screen.getByRole("button", { name: /^Velg dato, valgt dato er/ })).toBeVisible();
at Object.getElementError (node_modules/@testing-library/dom/dist/config.js:37:19)
at node_modules/@testing-library/dom/dist/query-helpers.js:76:38
at node_modules/@testing-library/dom/dist/query-helpers.js:52:17
at node_modules/@testing-library/dom/dist/query-helpers.js:95:19
at Object.getByRole (src/date-picker-localisation.test.tsx:24:17)
● live UI language changes retain the selected day and unsaved form draft
TestingLibraryElementError: Unable to find an accessible element with the role "spinbutton" and name "Dag"
Here are the accessible roles:
textbox:
Name "Unsaved title":
<input
aria-label="Unsaved title"
value="Teacher draft"
/>
--------------------------------------------------
button:
Name "Bokmål":
<button />
Name "English":
<button />
Name "Choose date, selected date is Nov 23, 2026":
<button
aria-label="Choose date, selected date is Nov 23, 2026"
class="MuiButtonBase-root MuiIconButton-root MuiIconButton-edgeEnd MuiIconButton-sizeMedium css-1ysp02-MuiButtonBase-root-MuiIconButton-root"
tabindex="0"
type="button"
/>
--------------------------------------------------
group:
Name "Deadline":
<div
aria-invalid="false"
aria-labelledby="_r_3_-label"
class="MuiPickersInputBase-root MuiPickersOutlinedInput-root MuiPickersInputBase-colorPrimary MuiPickersInputBase-adornedEnd css-189if4m-MuiPickersInputBase-root-MuiPickersOutlinedInput-root"
role="group"
/>
--------------------------------------------------
spinbutton:
Name "Month":
<span
aria-disabled="false"
aria-label="Month"
aria-readonly="false"
aria-valuemax="12"
aria-valuemin="1"
aria-valuenow="11"
aria-valuetext="November"
autocapitalize="none"
autocorrect="off"
class="MuiPickersSectionList-sectionContent MuiPickersInputBase-sectionContent css-1jpopwz-MuiPickersSectionList-sectionContent-MuiPickersInputBase-sectionContent"
contenteditable="true"
inputmode="numeric"
role="spinbutton"
spellcheck="false"
tabindex="0"
/>
Name "Day":
<span
aria-disabled="false"
aria-label="Day"
aria-readonly="false"
aria-valuemax="30"
aria-valuemin="1"
aria-valuenow="23"
aria-valuetext="23rd"
autocapitalize="none"
autocorrect="off"
class="MuiPickersSectionList-sectionContent MuiPickersInputBase-sectionContent css-1jpopwz-MuiPickersSectionList-sectionContent-MuiPickersInputBase-sectionContent"
contenteditable="true"
inputmode="numeric"
role="spinbutton"
spellcheck="false"
tabindex="-1"
/>
Name "Year":
<span
aria-disabled="false"
aria-label="Year"
aria-readonly="false"
aria-valuemax="9999"
aria-valuemin="0"
aria-valuenow="2026"
autocapitalize="none"
autocorrect="off"
class="MuiPickersSectionList-sectionContent MuiPickersInputBase-sectionContent css-1jpopwz-MuiPickersSectionList-sectionContent-MuiPickersInputBase-sectionContent"
contenteditable="true"
inputmode="numeric"
role="spinbutton"
spellcheck="false"
tabindex="-1"
/>
--------------------------------------------------
Ignored nodes: comments, script, style
<body>
<div>
<input
aria-label="Unsaved title"
value="Teacher draft"
/>
<button>
Bokmål
</button>
<button>
English
</button>
<div
class="MuiFormControl-root MuiPickersTextField-root css-eqc3kq-MuiFormControl-root-MuiPickersTextField-root"
>
<label
class="MuiFormLabel-root MuiInputLabel-root MuiInputLabel-formControl MuiInputLabel-animated MuiInputLabel-shrink MuiInputLabel-outlined MuiFormLabel-colorPrimary MuiFormLabel-filled MuiInputLabel-root MuiInputLabel-formControl MuiInputLabel-animated MuiInputLabel-shrink MuiInputLabel-outlined css-18yassc-MuiFormLabel-root-MuiInputLabel-root"
data-shrink="true"
for="_r_3_"
id="_r_3_-label"
>
Deadline
</label>
<div
aria-invalid="false"
aria-labelledby="_r_3_-label"
class="MuiPickersInputBase-root MuiPickersOutlinedInput-root MuiPickersInputBase-colorPrimary MuiPickersInputBase-adornedEnd css-189if4m-MuiPickersInputBase-root-MuiPickersOutlinedInput-root"
role="group"
>
<div
class="MuiPickersSectionList-root MuiPickersInputBase-sectionsContainer css-w175m5-MuiPickersSectionList-root-MuiPickersInputBase-sectionsContainer-MuiPickersOutlinedInput-sectionsContainer"
contenteditable="false"
tabindex="0"
>
<span
class="MuiPickersSectionList-section css-joz0rk-MuiPickersSectionList-section-MuiPickersInputBase-section"
data-sectionindex="0"
>
<span
class="MuiPickersInputBase-sectionBefore css-w1q93y-MuiPickersSectionList-sectionSeparator-MuiPickersInputBase-separator"
/>
<span
aria-disabled="false"
aria-label="Month"
aria-readonly="false"
aria-valuemax="12"
aria-valuemin="1"
aria-valuenow="11"
aria-valuetext="November"
autocapitalize="none"
autocorrect="off"
class="MuiPickersSectionList-sectionContent MuiPickersInputBase-sectionContent css-1jpopwz-MuiPickersSectionList-sectionContent-MuiPickersInputBase-sectionContent"
contenteditable="true"
inputmode="numeric"
role="spinbutton"
spellcheck="false"
tabindex="0"
>
11
</span>
<span
class="MuiPickersInputBase-sectionAfter css-w1q93y-MuiPickersSectionList-sectionSeparator-MuiPickersInputBase-separator"
>
/
</span>
</span>
<span
class="MuiPickersSectionList-section css-joz0rk-MuiPickersSectionList-section-MuiPickersInputBase-section"
data-sectionindex="1"
>
<span
class="MuiPickersInputBase-sectionBefore css-w1q93y-MuiPickersSectionList-sectionSeparator-MuiPickersInputBase-separator"
/>
<span
aria-disabled="false"
aria-label="Day"
aria-readonly="false"
aria-valuemax="30"
aria-valuemin="1"
aria-valuenow="23"
aria-valuetext="23rd"
autocapitalize="none"
autocorrect="off"
class="MuiPickersSectionList-sectionContent MuiPickersInputBase-sectionContent css-1jpopwz-MuiPickersSectionList-sectionContent-MuiPickersInputBase-sectionContent"
contenteditable="true"
inputmode="numeric"
role="spinbutton"
spellcheck="false"
tabindex="-1"
>
23
</span>
<span
class="MuiPickersInputBase-sectionAfter css-w1q93y-MuiPickersSectionList-sectionSeparator-MuiPickersInputBase-separator"
>
/
</span>
</span>
<span
class="MuiPickersSectionList-section css-joz0rk-MuiPickersSectionList-section-MuiPickersInputBase-section"
data-sectionindex="2"
>
<span
class="MuiPickersInputBase-sectionBefore css-w1q93y-MuiPickersSectionList-sectionSeparator-MuiPickersInputBase-separator"
/>
<span
aria-disabled="false"
aria-label="Year"
aria-readonly="false"
aria-valuemax="9999"
aria-valuemin="0"
aria-valuenow="2026"
autocapitalize="none"
autocorrect="off"
class="MuiPickersSectionList-sectionContent MuiPickersInputBase-sectionContent css-1jpopwz-MuiPickersSectionList-sectionContent-MuiPickersInputBase-sectionContent"
contenteditable="true"
inputmode="numeric"
role="spinbutton"
spellcheck="false"
tabindex="-1"
>
2026
</span>
<span
class="MuiPickersInputBase-sectionAfter css-w1q93y-MuiPickersSectionList-sectionSeparator-MuiPickersInputBase-separator"
/>
</span>
</div>
<div
class="MuiInputAdornment-root MuiInputAdornment-positionEnd MuiInputAdornment-outlined MuiInputAdornment-sizeMedium css-elo8k2-MuiInputAdornment-root"
>
<button
aria-label="Choose date, selected date is Nov 23, 2026"
class="MuiButtonBase-root MuiIconButton-root MuiIconButton-edgeEnd MuiIconButton-sizeMedium css-1ysp02-MuiButtonBase-root-MuiIconButton-root"
tabindex="0"
type="button"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root MuiSvgIcon-fontSizeMedium css-1umw9bq-MuiSvgIcon-root"
data-testid="CalendarIcon"
focusable="false"
viewBox="0 0 24 24"
>
<path
d="M17 12h-5v5h5v-5zM16 1v2H8V1H6v2H5c-1.11 0-1.99.9-1.99 2L3 19c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2h-1V1h-2zm3 18H5V8h14v11z"
/>
</svg>
</button>
</div>
<fieldset
aria-hidden="true"
class="MuiPickersOutlinedInput-notchedOutline css-lqwr9g-MuiPickersOutlinedInput-notchedOutline"
>
<legend
class="css-kg5swy"
>
<span
class="css-w48gsk"
>
Deadline
</span>
</legend>
</fieldset>
<input
aria-hidden="true"
class="MuiPickersInputBase-input MuiPickersOutlinedInput-input css-31lcvl-MuiPickersInputBase-input"
id="_r_3_"
tabindex="-1"
value="11/23/2026"
/>
</div>
</div>
</div>
</body>
35 | expect(screen.getByRole("spinbutton", { name: "Day", exact: true })).toHaveTextContent("23");
36 | fireEvent.click(screen.getByRole("button", { name: "Bokmål", exact: true }));
> 37 | expect(screen.getByRole("spinbutton", { name: "Dag", exact: true })).toHaveTextContent("23");
| ^
38 | expect(title).toHaveValue("Retain this draft");
39 | fireEvent.click(screen.getByRole("button", { name: "English", exact: true }));
40 | expect(screen.getByRole("spinbutton", { name: "Day", exact: true })).toHaveTextContent("23");
at Object.getElementError (node_modules/@testing-library/dom/dist/config.js:37:19)
at node_modules/@testing-library/dom/dist/query-helpers.js:76:38
at node_modules/@testing-library/dom/dist/query-helpers.js:52:17
at node_modules/@testing-library/dom/dist/query-helpers.js:95:19
at Object.getByRole (src/date-picker-localisation.test.tsx:37:17)
Test Suites: 1 failed, 1 total
Tests: 2 failed, 2 total
Snapshots: 0 total
Time: 3.687 s
Ran all test suites matching date-picker-localisation.test.tsx.
@@ -0,0 +1,49 @@
{
"journeys": [],
"captures": [
{
"zone": "America/Los_Angeles",
"language": "en",
"state": "edit",
"violations": []
}
],
"writes": [
{
"zone": "America/Los_Angeles",
"language": "en",
"method": "PUT",
"body": {
"expectedVersion": 7,
"jobTitle": "Retain teacher draft",
"companyId": 1,
"status": "Applied",
"statusChangedAt": null,
"responseReceived": true,
"responseDate": "2026-11-24",
"location": null,
"salary": null,
"salaryMin": null,
"salaryMax": null,
"salaryCurrency": null,
"salaryPeriod": null,
"nextAction": null,
"followUpAt": "2026-11-24",
"notes": null,
"description": null,
"translatedDescription": null,
"descriptionLanguage": null,
"tags": null,
"deadline": "2026-11-24",
"coverLetterText": null,
"dateApplied": "2026-11-24",
"jobUrl": null
}
}
],
"errors": [],
"unavailableReads": [],
"blockedExternal": [
"https://fonts.googleapis.com"
]
}
@@ -0,0 +1,17 @@
Checking America-Los_Angeles-en
node:internal/modules/run_main:107
triggerUncaughtException(
^
locator.click: Timeout 30000ms exceeded.
Call log:
- waiting for getByRole('dialog', { name: 'Add Job', exact: true }).getByRole('button', { name: 'Enter details manually', exact: true })
at C:\Users\Connor\.codex\worktrees\direct-roadmap-integration\Job tracker\job-tracker-ui\scripts\verify-job-date-widgets.mjs:100:89 {
log: [
" - waiting for getByRole('dialog', { name: 'Add Job', exact: true }).getByRole('button', { name: 'Enter details manually', exact: true })"
],
name: 'TimeoutError'
}
Node.js v24.19.0
+10
View File
@@ -0,0 +1,10 @@
> job-tracker-ui@0.1.0 test
> jest --runInBand date-picker-localisation.test.tsx locale-fetch-stability.test.tsx app-shell-navigation.test.tsx edit-job-revision.test.tsx job-date-picker-calendar.test.tsx theme-state.test.tsx
Test Suites: 6 passed, 6 total
Tests: 25 passed, 25 total
Snapshots: 0 total
Time: 14.463 s
Ran all test suites matching date-picker-localisation.test.tsx|locale-fetch-stability.test.tsx|app-shell-navigation.test.tsx|edit-job-revision.test.tsx|job-date-picker-calendar.test.tsx|theme-state.test.tsx.
Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

@@ -0,0 +1,33 @@
> job-tracker-ui@0.1.0 test
> jest --runInBand job-discovery.test.tsx -t title sorting follows UI language changes
FAIL src/job-discovery.test.tsx
● title sorting follows UI language changes without refetching or changing discovery filters
expect(received).toEqual(expected) // deep equality
- Expected - 1
+ Received + 1
Array [
- "Zoologist",
"Åker agronom",
+ "Zoologist",
]
115 |
116 | act(() => { window.dispatchEvent(new StorageEvent("storage", { key: "uiLanguage", newValue: "nb" })); });
> 117 | expect(titles()).toEqual(["Zoologist", "Åker agronom"]);
| ^
118 | expect(screen.getByLabelText("Rolle eller bedrift")).toHaveValue("science");
119 | expect(screen.getByLabelText("Kommune")).toHaveValue("Oslo");
120 | expect(screen.getByRole("combobox", { name: translations.nb.jobDiscoverySort })).toHaveTextContent(translations.nb.jobDiscoverySortTitle);
at Object.toEqual (src/job-discovery.test.tsx:117:20)
Test Suites: 1 failed, 1 total
Tests: 1 failed, 23 skipped, 24 total
Snapshots: 0 total
Time: 5.888 s
Ran all test suites matching job-discovery.test.tsx with tests matching "title sorting follows UI language changes".
@@ -0,0 +1,9 @@
> job-tracker-ui@0.1.0 test
> jest --runInBand job-discovery.test.tsx
Test Suites: 1 passed, 1 total
Tests: 24 passed, 24 total
Snapshots: 0 total
Time: 8.499 s
Ran all test suites matching job-discovery.test.tsx.
@@ -0,0 +1,4 @@
> job-tracker-ui@0.1.0 typecheck
> tsc --noEmit --incremental false
Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

@@ -0,0 +1,117 @@
{
"journeys": [
{
"name": "390-nb-dark",
"counts": [
0,
1,
2
],
"downloads": 3
},
{
"name": "1440-en-light",
"counts": [
0,
1,
2
],
"downloads": 3
}
],
"downloads": [
{
"name": "390-nb-dark",
"kind": "converted",
"expectedCount": 0,
"filename": "390-nb-dark-converted-0.gz",
"bytes": 2,
"rawSha256": "4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945",
"gzipSha256": "50c15d921df28492e2d9bf2da5d1cd5c78ee416bdcf478074db63d76d2381cba"
},
{
"name": "390-nb-dark",
"kind": "converted",
"expectedCount": 1,
"filename": "390-nb-dark-converted-1.gz",
"bytes": 235,
"rawSha256": "6af2ec936c07ea767b72978f5e31f17bd750fb942628b6765a5befb544a60bf5",
"gzipSha256": "2117b71f51b07c27587ff99d40acd9ef43b538a62658ce52ea060ab4a3c2cba5"
},
{
"name": "390-nb-dark",
"kind": "converted",
"expectedCount": 2,
"filename": "390-nb-dark-converted-2.gz",
"bytes": 468,
"rawSha256": "18a8f3b661dc979e030446ecb3977e92fa083e81a73e96847811ba26a861409a",
"gzipSha256": "7f3553fe748c77febb7f3b312d152002e44c1608e05e99b05b7865dac0154326"
},
{
"name": "1440-en-light",
"kind": "converted",
"expectedCount": 0,
"filename": "1440-en-light-converted-0.gz",
"bytes": 2,
"rawSha256": "4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945",
"gzipSha256": "50c15d921df28492e2d9bf2da5d1cd5c78ee416bdcf478074db63d76d2381cba"
},
{
"name": "1440-en-light",
"kind": "converted",
"expectedCount": 1,
"filename": "1440-en-light-converted-1.gz",
"bytes": 235,
"rawSha256": "6af2ec936c07ea767b72978f5e31f17bd750fb942628b6765a5befb544a60bf5",
"gzipSha256": "2117b71f51b07c27587ff99d40acd9ef43b538a62658ce52ea060ab4a3c2cba5"
},
{
"name": "1440-en-light",
"kind": "converted",
"expectedCount": 2,
"filename": "1440-en-light-converted-2.gz",
"bytes": 468,
"rawSha256": "18a8f3b661dc979e030446ecb3977e92fa083e81a73e96847811ba26a861409a",
"gzipSha256": "7f3553fe748c77febb7f3b312d152002e44c1608e05e99b05b7865dac0154326"
}
],
"captures": [
{
"name": "390-nb-dark",
"count": 0,
"violations": []
},
{
"name": "390-nb-dark",
"count": 1,
"violations": []
},
{
"name": "390-nb-dark",
"count": 2,
"violations": []
},
{
"name": "1440-en-light",
"count": 0,
"violations": []
},
{
"name": "1440-en-light",
"count": 1,
"violations": []
},
{
"name": "1440-en-light",
"count": 2,
"violations": []
}
],
"errors": [],
"writes": [],
"unavailableReads": [],
"blockedExternal": [
"https://fonts.googleapis.com",
"https://fonts.googleapis.com"
]
}
@@ -0,0 +1,3 @@
Checking 390-nb-dark
Checking 1440-en-light
PASS 2 configurations, 6 native downloads, 6 accessibility scans; no API writes

Some files were not shown because too many files have changed in this diff Show More