Skip to content

Instantly share code, notes, and snippets.

@a-laughlin
Last active November 2, 2023 17:46
Show Gist options
  • Save a-laughlin/50fb58a14562b1278725b1b1a02c09c8 to your computer and use it in GitHub Desktop.
Save a-laughlin/50fb58a14562b1278725b1b1a02c09c8 to your computer and use it in GitHub Desktop.
Automating Workday Resume Data Entry
javascript:(async ()=>{
const positions = [
{
company:'Slalom Build (Consulting)',
title:'Lead/Principal Engineer',
fromDate:'01/2023',
toDate:new Date().toLocaleDateString('en-US', { month: '2-digit', year: 'numeric' }),
description:
`- Architecting and building greenfield SaaS app infrastructure and initial features for diverse clients and tech stacks
- Leading a team of 5 engineers
- note: accepted a position here, then market conditions evaporated projects. Hence applying elsewhere`,
},
{
company:'HubSpot (Marketing and Sales SaaS)',
title:'Technical Lead - Payments',
fromDate:'01/2022',
toDate:'12/2022',
description:
`- Delivered high-quality features used by millions with React, REST, Apollo GraphQL, Component Libraries, and TypeScript, while collaborating on infrastructure updates in accessibility, quality, and performance
- Planned and mentored team on migrations from JavaScript to TypeScript and Enzyme to Testing Library`,
},
{
company:'SwitchFly (Travel and Loyalty Integrations)',
title:'Engineering Lead',
fromDate:'01/2020',
toDate:'12/2021',
description:
`- Led two engineering teams, fostering psychologically safe, inclusive, and growth-oriented environments while individually delivering infrastructure code, tests, and features for over 1 million end users
- Initiated code change cycle measurement and improvements that saved 66 minutes per developer per day and increased developer satisfaction. Via developer tools (Vite), test execution (Jest), workflow automation (gitlab config, Vite plugins, scripts), and UI render architecture
- Conceived and led successful long-running front end skills training series for engineers company-wide
- Selected to own, re-architect, and re-write our frontend infrastructure, component library, and features from Ember to React. Used Typescript, React, Component Library and Design System in Storybook and Figma, Material UI, Vite, Node.js, FP-TS, CI/CD, GitLab, Jest Integration Tests with React Testing Library, Playwright E2E, AWS Cloud Development Kit (AWS-CDK), AWS AppSync GraphQL, AWS Lambda, AWS DynamoDB`,
},
{
company:'CU Boulder Computer Science',
title:'Student - second degree with a focus on AI/ML for wellbeing',
fromDate:'01/2018',
toDate:'05/2022',
description:
`- Led four three-month remote team projects of 2-4 software engineering and data science students
- 2x exceeded and 2x met project goals through spearheading creation and adoption of agile processes, developer tooling, CI/CD selection and configuration, communication/coordination tools, and tech stacks including Python, Scala, Node.js, TypeScript, React, Flask, Express, GraphQL, PostgreSQL, AI, ML, NLP`,
},
{
company:'Efficacy (Hybrid Desktop/PaaS productivity app)',
title:'Startup Founder',
fromDate:'01/2016',
toDate:'12/2017',
description:
`- Responsible for every aspect of product design and development, including concept, market selection, business model, GUI design, testing, code architecture, and tech (React, Redux, RxJS, Electron, NodeJS)
- Reduced task switching time on frequent activities by 98% (2min to 2sec)`,
},
{
company:'Attend (Event Management SaaS)',
title:'Senior Software Engineer',
fromDate:'01/2014',
toDate:'12/2015',
description:'Feature delivery w/ JavaScript, Angular.js, Jasmine, Protractor, CI, Scrum, CI, GitLab',
},
{
company:'Happathon (Real-time city wellbeing metrics)',
title:'Nonprofit Startup Cofounder',
fromDate:'01/2012',
toDate:'12/2014',
description:'',
},
{
company:'MIT Media Lab',
title:'Software Engineer',
fromDate:'01/2012',
toDate:'12/2013',
description:'New UI for million+ user Scratch community',
},
{
company:'Save the Children - International Humanitarian Nonprofit',
title:'Developer / Analyst',
fromDate:'01/2008',
toDate:'12/2011',
description:
`- Responsible for data quality, analytics, JavaScript architecture, implementation, testing, and vendor integration across 7 content management systems, 100 domains, and 1000+ pages
- Accelerated code release cycles by 99% (10hrs to 2min) through custom CMS scripts`,
},
];
// set up Testing Library
globalThis.workday_automation ??= {
tl:await import('https://cdn.jsdelivr.net/npm/@testing-library/dom@9.3.3/+esm'),
user:(await import('https://cdn.jsdelivr.net/npm/@testing-library/user-event@14.5.1/+esm')).default
};
const {tl, user} = globalThis.workday_automation;
tl.configure({testIdAttribute: 'data-automation-id'});
// clean up previous attempts
let deleteButtons = tl.screen.queryAllByRole('button',{name:/^Delete Work/});
// for some reason for...of loop doesn't always delete all the buttons. Likely a race condition. Not worth figuring out. Just keep deleting one by one while they exist.
while (deleteButtons.length){
await user.click(deleteButtons.at(-1));
await tl.waitFor(()=>{
const currentButtons = tl.screen.queryAllByRole('button',{name:/^Delete Work/});
if (currentButtons.length > deleteButtons.length) throw new Error(`this shouldn't happen`);
if (currentButtons.length === deleteButtons.length) throw new Error('waiting for delete button removal');
deleteButtons = currentButtons;
},{timeout:2000});
}
await tl.screen.findByRole('button',{name:/^Add Work/},{timeout:20000,interval:200});
// create and fill resume sections
for (let p, pNum=-1; p = positions[++pNum];){
user.click(await tl.screen.findByRole('button', {name:/^Add (Another )?Work/}));
const {findByLabelText} = tl.within(await tl.screen.findByTestId(`workExperience-${pNum + 1}`));
const currentDate = new Date();
await user.type(await findByLabelText(/^Job Title/), p.title);
await user.type(await findByLabelText(/^Company/), p.company);
const fromContainer = await findByLabelText(/^From/);
const fromMonth = await tl.within(fromContainer).findByRole('spinbutton',{name:'Month'});
const fromYear = await tl.within(fromContainer).findByRole('spinbutton',{name:'Year'});
const [fromMonthNum, fromYearNum] = p.fromDate.split('/') || `1/${currentDate.getFullYear()}`;
await user.click(fromMonth);
// typing in the values fails with Workday's from/to date selector components; use arrow keys instead.
await user.keyboard('[ArrowUp]'.repeat(+fromMonthNum));
// selecting the next element directly breaks further arrowing for some reason, so ArrowRight
await user.keyboard('[ArrowRight]');
await user.keyboard('[ArrowDown]'.repeat(currentDate.getFullYear() + 1 - fromYearNum));
const toContainer = await findByLabelText(/^To/);
const toMonth = await tl.within(toContainer).findByRole('spinbutton',{name:'Month'});
const [toMonthNum, toYearNum] = p.toDate.split('/') || `12/${new Date().getFullYear()}`;
await user.click(toMonth);
await user.keyboard('[ArrowUp]'.repeat(+toMonthNum));
await user.keyboard('[ArrowRight]');
await user.keyboard('[ArrowDown]'.repeat(currentDate.getFullYear() + 1 - toYearNum));
if (p.description) await user.type(await findByLabelText(/^Role Description/), p.description);
}
})().catch(()=>{/* prevent monitoring spam */});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment