Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .env.production.example
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Production deployments intentionally have no working secret defaults.
# Copy this file to .env.production, replace every CHANGE-ME value, keep the
# result out of version control, and pin a released OSApplyTrack version.
OSAPPLYTRACK_VERSION=1.68.5
OSAPPLYTRACK_VERSION=1.68.6
POSTGRES_USER=applytrack
POSTGRES_PASSWORD=CHANGE-ME-use-a-long-random-password
POSTGRES_DB=applytrack
Expand Down
1 change: 1 addition & 0 deletions BACKLOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ review items and unsupported forms still need human attention.
- [x] [#450 — Workday tenants that open step 1 on Create Account: the saved password is typed into Create Account and the sign-in is judged refused (finastra.wd3)](https://github.com/CryptoJones/OSApplyTrack/issues/450) (after #448 the creator made, proved and saved a finastra.wd3 account, and the re-run was "refused": Finastra's step 1 opens on Create Account — the same email and password boxes as Sign In plus Verify New Password and a "Sign In" link — so the saved account was typed into Create Account and the link pressed as the sign-in button, flipping the form empty. **1.68.3:** the way in waits up to 30 s for whatever step 1 shows, turns a Create Account over to Sign In through its link before any typing, and the same guard runs where the walk's own sign-ins start; a fixture variant that opens on Create Account, drawn late, signs in through the link and reaches Review.)
- [x] [#453 — Workday My Information hands back the same five fields every round (finastra.wd3)](https://github.com/CryptoJones/OSApplyTrack/issues/453) (on 1.68.3 the run signed in and filled the page, then handed back résumé, email, phone type, city and "How Did You Hear About Us?" on every round. Four causes, replayed from makemake: Workday's search box says "Search" in its placeholder and discovery read it as a site search and dropped it, while the sweep named it anyway; the sweep keyed a box by id and discovery by name, so City was unmapped under `address--city` and never drafted under `city`; Phone Device Type was discovered before its menu was read and the phone rule drafted the number; the email is drawn as text with no box. **1.68.4:** a Workday search box is a question and is typed into key by key before Enter; the sweep keys name then id as discovery does; an email already shown on the page is answered; the drafter answers the kind of phone (Mobile), where the posting was seen (LinkedIn, or the job-board option) instead of leaving it to the person, and the city and state of the résumé's location as their own boxes, with US abbreviations expanded for a fixed list.)
- [x] [#455 — Workday My Experience: the filler counts the saved rows before they are drawn and adds blank ones (finastra.wd3)](https://github.com/CryptoJones/OSApplyTrack/issues/455) (on 1.68.4 the first visit to My Experience filled three jobs and four schools; every visit after pressed Add Another again and handed back the blank rows, because a saved draft draws the block and its Add button half a second before its rows — more over pluto's proxy — and the filler counted none. Racing the late rows also put the wrong school and field on two education rows, and the search box took the first option ("Accounting") when nothing read as the field. **1.68.5:** a block's rows are waited for until their count has held still; held education rows are completed, never added to, as jobs already were; a search box with no match is left empty unless it is required; a date spinbutton that still disagrees after typing is cleared and typed once more.)
- [x] [#457 — Workday walk reads a re-rendered My Experience as a page turn; certificate degrees map to a term Finastra does not offer; a fresh row's start month lands as 12 (finastra.wd3)](https://github.com/CryptoJones/OSApplyTrack/issues/457) (on 1.68.5 My Experience was refilled cleanly and the log said pages 3, 4 and 5 were turned, yet the screenshot was My Experience with "Degree is required" on three certificate rows and the first job starting 12/2026: Save and Continue on a step with errors draws the same step again, and the walk judged a turn by the address or the body text; certificates mapped to "Post-diploma studies", which Finastra's menu — Associates to Certificate and High School — does not offer; a row filled while still drawing ran month and year together; and stale packet questions from #455's blank rows plus the résumé already on the draft counted as unmapped. **1.68.6:** a Workday turn is read from the progress bar's active step and the same step means the page held; certificates and micro-credentials are "Certificate" and a school whose degree the menu lacks is removed again; a new row is filled once drawn and a held row matched by title gets its dates put right; on a Workday page a question naming a row box is never unmapped and the résumé on file is answered.)
- [x] [#417 — Agent: a résumé dropzone whose wrapping label is only "Click or drag & drop PDF" is never recognised as the résumé (Ethos) — fixed in 1.67.0: dropzone text never names a file field; its row's title does](https://github.com/CryptoJones/OSApplyTrack/issues/417)
- [x] [#418 — Agent: an application form folded behind "Easy Apply" is never revealed (N-iX) — fixed in 1.67.0: "Easy/Quick/One-click Apply" open the form (N-iX itself then ends at a reCAPTCHA v2 handoff)](https://github.com/CryptoJones/OSApplyTrack/issues/418)
- [x] [#419 — Agent: a footer newsletter signup (mc4wp) is discovered as an application question (Tecbrains) — fixed in 1.67.0: newsletter forms are page widgets](https://github.com/CryptoJones/OSApplyTrack/issues/419)
Expand Down
23 changes: 14 additions & 9 deletions api/ApplyTrack.Api.Tests/BrowserSubmitterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1529,14 +1529,14 @@ Click the object that does not fit the column pattern
<script>
let j = 0, s = 0;
const addJob = (title, company, m, y) => { const n = ++j; document.getElementById('jobs').insertAdjacentHTML('beforeend',
`<div role="group" aria-label="Work Experience ${n}"><label for="we${n}t">Job Title</label><input id="we${n}t" value="${title}" />` +
`<div role="group" aria-label="Work Experience ${n}"><button type="button" aria-label="Delete Work Experience ${n}" onclick="this.parentElement.remove()">Delete</button><label for="we${n}t">Job Title</label><input id="we${n}t" value="${title}" />` +
`<label for="we${n}c">Company</label><input id="we${n}c" value="${company}" />` +
`<div role="group" aria-label="From"><input role="spinbutton" aria-label="Month" id="we${n}m" value="${m}" /><input role="spinbutton" aria-label="Year" id="we${n}y" value="${y}" /></div>` +
`<div role="group" aria-label="To"><input role="spinbutton" aria-label="Month" id="we${n}tm" /><input role="spinbutton" aria-label="Year" id="we${n}ty" /></div>` +
`<label><input type="checkbox" id="we${n}h" /> I currently work here</label><label for="we${n}d">Role Description</label><textarea id="we${n}d"></textarea></div>`);
document.querySelectorAll('[data-automation-id=add-button]')[0].textContent = 'Add Another'; };
const addSchool = (school) => { const n = ++s; document.getElementById('schools').insertAdjacentHTML('beforeend',
`<div role="group" aria-label="Education ${n}"><label for="ed${n}s">School or University</label><input id="ed${n}s" value="${school}" />` +
`<div role="group" aria-label="Education ${n}"><button type="button" aria-label="Delete Education ${n}" onclick="this.parentElement.remove()">Delete</button><label for="ed${n}s">School or University</label><input id="ed${n}s" value="${school}" />` +
`<button type="button" aria-haspopup="listbox" aria-label="Degree Select One Required" id="ed${n}g" onclick="menu(this, ['Select One','Bachelors','Masters'])">Select One</button>` +
`<label for="ed${n}f">Field of Study</label><div data-uxi-multiselect-id="ed${n}"><ul role="listbox" data-automation-id="selectedItemList" id="ed${n}chips"></ul>` +
`<input id="ed${n}f" data-uxi-widget-type="selectinput" data-uxi-multiselect-id="ed${n}" placeholder="Search" onkeydown="if(event.key==='Enter'){chip('ed${n}chips',this.value);this.value='';event.preventDefault()}" /></div></div>`);
Expand All @@ -1545,7 +1545,8 @@ Click the object that does not fit the column pattern
let opened = null;
const menu = (btn, options) => { opened = btn; const m = document.getElementById('menu'); m.innerHTML = ''; for (const o of options) { const li = document.createElement('li'); li.setAttribute('role', 'option'); li.textContent = o; li.onclick = () => { opened.textContent = o; m.style.display = 'none'; }; m.appendChild(li); } m.style.display = 'block'; };
// The draft's own rows, drawn after the blocks: a job with its dates, a school with no degree chosen.
setTimeout(() => { addJob('Principal Software Architect', 'Ronin 48', '3', '2026'); addSchool('Eastern University'); }, 1500);
// The held job's month is wrong (12 for Mar, #457); the held school is right.
setTimeout(() => { addJob('Principal Software Architect', 'Ronin 48', '12', '2026'); addSchool('Eastern University'); }, 1500);
</script></body></html>
""";

Expand Down Expand Up @@ -1612,7 +1613,7 @@ Click the object that does not fit the column pattern
<input data-automation-id="beecatcher" style="position:absolute;left:-9999px" tabindex="-1" />
</div>
<div id="step1" class="step" style="display:none">
<ol><li>current step 1 of 4 My Information</li></ol>
<ol><li data-automation-id="progressBarActiveStep">current step 1 of 4 My Information</li></ol>
<h3>My Information</h3>
<label for="source--source">How Did You Hear About Us?*</label>
<div data-uxi-multiselect-id="m1">
Expand All @@ -1634,7 +1635,7 @@ Click the object that does not fit the column pattern
<button type="button" data-automation-id="pageFooterNextButton" onclick="next1()">Save and Continue</button>
</div>
<div id="step2" class="step" style="display:none">
<ol><li>current step 2 of 4 Application Questions</li></ol>
<ol><li data-automation-id="progressBarActiveStep">current step 2 of 4 Application Questions</li></ol>
<h3>Application Questions</h3>
<label for="q1">Are you legally authorized to work in the country where this position is located?*</label>
<button type="button" id="q1" name="legallyAuthorized" aria-haspopup="listbox" aria-label="Are you legally authorized to work in the country where this position is located? Select One Required"
Expand All @@ -1644,7 +1645,7 @@ Click the object that does not fit the column pattern
<button type="button" data-automation-id="pageFooterNextButton" onclick="next2()">Save and Continue</button>
</div>
<div id="step3" class="step" style="display:none">
<ol><li>current step 3 of 4 Voluntary Disclosures</li></ol>
<ol><li data-automation-id="progressBarActiveStep">current step 3 of 4 Voluntary Disclosures</li></ol>
<h3>Voluntary Disclosures</h3>
<label for="eth">What is your ethnicity?</label>
<button type="button" id="eth" name="ethnicity" aria-haspopup="listbox" aria-label="What is your ethnicity? Select One Required"
Expand All @@ -1654,7 +1655,7 @@ Click the object that does not fit the column pattern
<button type="button" data-automation-id="pageFooterNextButton" onclick="next3()">Save and Continue</button>
</div>
<div id="step4" class="step" style="display:none">
<ol><li>current step 4 of 4 Review</li></ol>
<ol><li data-automation-id="progressBarActiveStep">current step 4 of 4 Review</li></ol>
<h3>Review</h3>
<p>Everything you entered, for review.</p>
<button type="button" data-automation-id="pageFooterNextButton" onclick="submit()">Submit</button>
Expand Down Expand Up @@ -3243,7 +3244,8 @@ public async Task Workday_experience_rows_drawn_late_are_completed_not_duplicate
new("Ronin 48", "Principal Software Architect", "Mar 2026 - Present", ["Designing the platform."]),
new("CrowdStrike", "Senior Technical Support Engineer", "Aug 2022 - Mar 2024", []),
],
Education = [new("Eastern University", "MS", "Applied AI", "2026 – Present"), new("University of Maryland University College", "Bachelor of Science", "CIS", "")],
// The menu offers no Certificate: the micro-credential's row is added and removed again (#457).
Education = [new("Eastern University", "MS", "Applied AI", "2026 – Present"), new("University of Maryland University College", "Bachelor of Science", "CIS", ""), new("Rochester Institute of Technology", "MicroMaster's", "Cybersecurity", "")],
};
var playwright = await Microsoft.Playwright.Playwright.CreateAsync();
await using var browser = await playwright.Chromium.ConnectAsync(_ws);
Expand All @@ -3254,14 +3256,17 @@ public async Task Workday_experience_rows_drawn_late_are_completed_not_duplicate

Assert.Equal(2, await page.GetByRole(Microsoft.Playwright.AriaRole.Group, new() { NameRegex = new System.Text.RegularExpressions.Regex(@"^Work Experience \d+$") }).CountAsync());
Assert.Equal("Principal Software Architect", await page.Locator("#we1t").InputValueAsync()); // held, untouched
Assert.Equal("3", await page.Locator("#we1m").InputValueAsync());
Assert.Equal("3", await page.Locator("#we1m").InputValueAsync()); // the held 12 put right: the row is this job by its title
Assert.True(await page.Locator("#we1h").IsCheckedAsync());
Assert.Equal("CrowdStrike", await page.Locator("#we2c").InputValueAsync());
Assert.Equal("8", await page.Locator("#we2m").InputValueAsync());
Assert.Equal(2, await page.GetByRole(Microsoft.Playwright.AriaRole.Group, new() { NameRegex = new System.Text.RegularExpressions.Regex(@"^Education \d+$") }).CountAsync());
Assert.Equal("Masters", await page.Locator("#ed1g").InnerTextAsync()); // the held school's blank degree
Assert.Equal("University of Maryland University College", await page.Locator("#ed2s").InputValueAsync());
Assert.Equal("Bachelors", await page.Locator("#ed2g").InnerTextAsync());
Assert.Equal(0, await page.Locator("#ed3s").CountAsync()); // the certificate row went
Assert.Equal("Certificate", BrowserSubmitter.WorkdayDegree("Graduate Certificate"));
Assert.Equal("Certificate", BrowserSubmitter.WorkdayDegree("MicroMaster’s"));
Assert.Contains("workExperience-1--companyName", filled);
Assert.Contains("education-0--degree", filled);
Assert.DoesNotContain("workExperience-0--jobTitle", filled);
Expand Down
Loading
Loading