Job Application Auto-Filler - WorkDay

Auto-fill job application forms on WorkDay by first adding all sections, then filling them.

// ==UserScript==
// @name         Job Application Auto-Filler - WorkDay
// @namespace    https://gf.qytechs.cn/en/users/670188-hacker09?sort=daily_installs
// @version      3
// @description  Auto-fill job application forms on WorkDay by first adding all sections, then filling them.
// @author       hacker09
// @match        https://*.myworkday.com/*
// @icon         https://i.imgur.com/3R9QyLR.png
// @grant        none
// ==/UserScript==

(function() {
  'use strict';

  //Configuration - Your actual data.
  const formData = {
    jobs: [
      { //Newest job first
        title: "Senior Software Developer",
        company: "Tech Solutions Inc.",
        location: "San Francisco",
        startMonth: "01",
        startYear: "2022",
        currentlyWork: true, //Set to 'true' for a current job
        endMonth: "", //Leave empty if currently working here
        endYear: "", //Leave empty if currently working here
        summary: "• Led a team of 5 developers in creating a new e-commerce platform.\n\n• Wrote high-quality, scalable code using React and Node.js.\n\n• Optimized application performance, resulting in a 30% reduction in load times."
      },
      { //Older job
        title: "IT Support Intern",
        company: "Global Innovations LLC.",
        location: "New York",
        startMonth: "06",
        startYear: "2021",
        currentlyWork: false,
        endMonth: "12",
        endYear: "2021",
        summary: "• Provided technical assistance to over 200 employees.\n\n• Managed user accounts and system permissions.\n\n• Documented and resolved IT support tickets efficiently."
      }
    ],
    education: [
      { //Most recent education first
        schoolName: "State University",
        degree: "BS", //Use abbreviations like BS, MS, PhD, or GED
        fieldOfStudy: "Computer Science",
        startYear: "2018",
        endYear: "2022"
      },
      { //Older education
        schoolName: "Community College",
        degree: "GED",
        fieldOfStudy: "Information Technology",
        startYear: "2016",
        endYear: "2018"
      }
    ],
    skills: "JavaScript, React, Node.js, HTML, CSS, SQL, Python, Project Management, Team Leadership, Agile Methodologies, AWS, Docker", //Workday parses skills as individual keywords, strips formatting, splits combined terms, and alphabetizes them for standardized search and matching. Proper skill list organized into categories
    languages: [
      {
        name: "English",
        isNative: false,
        listeningproficiency: "3 - Intermediate",
        readingproficiency: "3 - Intermediate",
        speakingproficiency: "2 - Classroom Study",
        writingproficiency: "2 - Classroom Study"
      },
      {
        name: "Spanish",
        isNative: false,
        listeningproficiency: "3 - Intermediate",
        readingproficiency: "3 - Intermediate",
        speakingproficiency: "2 - Classroom Study",
        writingproficiency: "2 - Classroom Study"
      }
    ]
  };

  function delay(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
  }

  async function fillInputField(element, value) {
    if (!element) return;
    element.focus();
    await delay(100);
    element.value = value;
    element.dispatchEvent(new Event('input', {
      bubbles: true
    }));
    element.dispatchEvent(new Event('change', {
      bubbles: true
    }));
    element.blur();
    await delay(100);
  }

  async function fillDegreeField(element, value) {
    if (!element) return;
    element.focus();
    element.select();
    element.click(); //Click to open dropdown
    await delay(500);

    //Type value to filter options
    element.value = '';
    for (let char of value) {
      element.value += char;
      element.dispatchEvent(new KeyboardEvent('keydown', {
        key: char,
        bubbles: true
      }));
      element.dispatchEvent(new KeyboardEvent('keypress', {
        key: char,
        bubbles: true
      }));
      element.dispatchEvent(new Event('input', {
        bubbles: true
      }));
      element.dispatchEvent(new KeyboardEvent('keyup', {
        key: char,
        bubbles: true
      }));
      await delay(100);
    }

    //Press Enter to filter/search
    element.dispatchEvent(new KeyboardEvent('keydown', {
      key: 'Enter',
      code: 'Enter',
      bubbles: true
    }));
    element.dispatchEvent(new KeyboardEvent('keyup', {
      key: 'Enter',
      code: 'Enter',
      bubbles: true
    }));
    await delay(500); //was 1000

    //Find and click matching option - (Exclude already selected items)
    const dropdownOptions = document.querySelectorAll('[data-automation-id="promptOption"]');

    for (const option of dropdownOptions) {
      const optionText = (option.textContent || option.dataset.automationLabel || '').trim();

      //Skip if this is a selected item (pill)
      const parentContainer = option.closest('[data-automation-id="selectedItem"]');
      if (parentContainer) {
        continue;
      }

      if (optionText === value || optionText.toLowerCase() === value.toLowerCase()) {
        const clickTarget = option.closest('[role="option"]') || option.parentElement.closest('[role="option"]') || option;

        //Try clicking the radio button inside
        const radioBtn = clickTarget.querySelector('input[type="radio"]') || clickTarget.querySelector('[data-automation-id="radioBtn"]');
        if (radioBtn) {
          radioBtn.click();
          radioBtn.dispatchEvent(new Event('change', {
            bubbles: true
          }));
        } else {
          clickTarget.click();
        }
        await delay(500);
        return;
      }
    }
  }

  async function fillDateFields(monthElement, yearElement, month, year) {
    if (monthElement && month) {
      monthElement.click();
      monthElement.focus();
      await delay(100);
      monthElement.select();
      monthElement.value = '';
      for (let char of month) {
        monthElement.value += char;
        monthElement.dispatchEvent(new KeyboardEvent('keydown', {
          key: char,
          code: `Digit${char}`,
          bubbles: true
        }));
        monthElement.dispatchEvent(new KeyboardEvent('keypress', {
          key: char,
          code: `Digit${char}`,
          bubbles: true
        }));
        monthElement.dispatchEvent(new Event('input', {
          bubbles: true
        }));
        monthElement.dispatchEvent(new KeyboardEvent('keyup', {
          key: char,
          code: `Digit${char}`,
          bubbles: true
        }));
        await delay(100);
      }
      monthElement.dispatchEvent(new Event('change', {
        bubbles: true
      }));
      await delay(100);
    }

    if (yearElement && year) {
      yearElement.click();
      yearElement.focus();
      await delay(100);
      yearElement.select();
      yearElement.value = '';
      for (let char of year) {
        yearElement.value += char;
        yearElement.dispatchEvent(new KeyboardEvent('keydown', {
          key: char,
          code: `Digit${char}`,
          bubbles: true
        }));
        yearElement.dispatchEvent(new KeyboardEvent('keypress', {
          key: char,
          code: `Digit${char}`,
          bubbles: true
        }));
        yearElement.dispatchEvent(new Event('input', {
          bubbles: true
        }));
        yearElement.dispatchEvent(new KeyboardEvent('keyup', {
          key: char,
          code: `Digit${char}`,
          bubbles: true
        }));
        await delay(100);
      }
      yearElement.dispatchEvent(new Event('change', {
        bubbles: true
      }));
      yearElement.dispatchEvent(new KeyboardEvent('keydown', {
        key: 'Tab',
        code: 'Tab',
        bubbles: true
      }));
      await delay(100);
    }
  }

  async function selectDropdownOption(triggerElement, valueToSelect) {
    if (!triggerElement || !valueToSelect) return;
    triggerElement.click();
    await delay(500); //was 1000
    const options = document.querySelectorAll('[data-automation-id="promptOption"]');
    for (const option of options) {
      const optionText = (option.textContent || option.dataset.automationLabel || '').trim();
      if (optionText.toLowerCase() === valueToSelect.toLowerCase()) {
        option.closest('[role="option"]')?.click();
        await delay(500);
        return;
      }
    }
    document.body.click(); //Click away to close dropdown if no match found
    await delay(200);
  }

  //Click all "Add" buttons first
  async function addSections() {
    const addBtns = document.querySelectorAll('[data-automation-id="panelSetAddButton"]');
    if (addBtns.length === 0) return;

    //Add job sections (assumes 1 is already visible)
    for (let i = 1; i < formData.jobs.length; i++) {
      addBtns[0]?.click(); //Assumes first add button is for jobs
      await delay(500); //was 1500
    }

    //Add education sections (assumes 1 is already visible)
    for (let i = 1; i < formData.education.length; i++) {
      addBtns[1]?.click(); //Assumes second add button is for education
      await delay(500); //was 1500
    }

    //Add language sections (assumes it starts with 0)
    const lastAddBtn = addBtns[addBtns.length - 1];
    for (let i = 0; i < formData.languages.length; i++) {
      lastAddBtn?.click(); //Assumes last add button is for languages
      await delay(500); //was 1500
    }
  }

  async function fillJobSection() {
    for (let i = 0; i < formData.jobs.length; i++) {
      const job = formData.jobs[i];
      const titleInputs = document.querySelectorAll('input[id*="jobHistoryTitle"]');
      await fillInputField(titleInputs[i], job.title);
      const companyInputs = document.querySelectorAll('input[id*="jobHistoryCompany"]');
      await fillInputField(companyInputs[i], job.company);
      const locationInputs = document.querySelectorAll('input[id*="jobHistoryLocation"]');
      await fillInputField(locationInputs[i], job.location);
      const allMonthInputs = document.querySelectorAll('input[id*="dateSectionMonth"]');
      const allYearInputs = document.querySelectorAll('input[id*="dateSectionYear"]');
      await fillDateFields(allMonthInputs[i * 2], allYearInputs[i * 2], job.startMonth, job.startYear);
      const currentlyWorkInputs = document.querySelectorAll('input[id*="currentlyWorkHere"]');
      if (currentlyWorkInputs[i] && job.currentlyWork) {
        if (!currentlyWorkInputs[i].checked) currentlyWorkInputs[i].click();
      }
      if (!job.currentlyWork) {
        await fillDateFields(allMonthInputs[i * 2 + 1], allYearInputs[i * 2 + 1], job.endMonth, job.endYear);
      }
      const summaryTextareas = document.querySelectorAll('textarea[id*="jobSummary"]');
      await fillInputField(summaryTextareas[i], job.summary);
    }
  }

  async function fillEducationSection() {
    for (let i = 0; i < formData.education.length; i++) {
      const edu = formData.education[i];

      //Always query inputs fresh inside loop
      const schoolInputs = document.querySelectorAll('input[id*="schoolName"]');
      const degreeInputs = document.querySelectorAll('input[id*="schoolDegrees"]');
      const fieldInputs = document.querySelectorAll('input[id*="fieldOfStudy"]');
      const allYearInputs = document.querySelectorAll('input[id*="dateSectionYear"]');

      await fillInputField(schoolInputs[i], edu.schoolName);
      await fillDegreeField(degreeInputs[i], edu.degree);
      await delay(500);
      await fillInputField(fieldInputs[i], edu.fieldOfStudy);
      await delay(500);

      //Fill years immediately, using proper offsets after job entries
      const jobCount = formData.jobs.length;
      const eduStartIndex = jobCount * 2 + i * 2;
      const eduEndIndex = eduStartIndex + 1;

      await fillDateFields(null, allYearInputs[eduStartIndex], '', edu.startYear);
      await fillDateFields(null, allYearInputs[eduEndIndex], '', edu.endYear);
    }
  }

  async function fillSkillsSection() {
    const skillsTextarea = document.querySelector('textarea[id*="skills"]');
    await fillInputField(skillsTextarea, formData.skills);
  }

  async function fillLanguagesSection() {
    for (let i = 0; i < formData.languages.length; i++) {
      const lang = formData.languages[i];

      //Select language name
      const langSelectWidgets = document.querySelectorAll('[data-automation-id="selectWidget"][id*="languages"]');
      await selectDropdownOption(langSelectWidgets[i], lang.name);

      //Check "native language" if applicable
      const nativeCheckboxes = document.querySelectorAll('input[id*="myNativeLanguage"]');
      if (nativeCheckboxes[i] && lang.isNative && !nativeCheckboxes[i].checked) {
        nativeCheckboxes[i].click();
      }

      //Select proficiency levels
      const allProficiencyTriggers = document.querySelectorAll('[data-automation-id="selectWidget"][id*="langProficiencies"]');
      const proficiencyTriggersForCurrentLang = Array.from(allProficiencyTriggers).slice(i * 4, i * 4 + 4);

      if (proficiencyTriggersForCurrentLang.length >= 4) {
        await selectDropdownOption(proficiencyTriggersForCurrentLang[0], lang.listeningproficiency);
        await selectDropdownOption(proficiencyTriggersForCurrentLang[1], lang.readingproficiency);
        await selectDropdownOption(proficiencyTriggersForCurrentLang[2], lang.speakingproficiency);
        await selectDropdownOption(proficiencyTriggersForCurrentLang[3], lang.writingproficiency);
      }
    }
  }

  async function handleThreePostFillQuestions() {
    //Click Next to proceed to questions
    document.querySelector('[title="Next"]').click();

    //Wait for the "legal right to work" question to appear
    await new Promise(resolve => {
      const checkForQuestion = () => {
        if (document.body.textContent.includes('Do you have the legal')) {
          resolve();
        } else {
          setTimeout(checkForQuestion, 1500);
        }
      };
      checkForQuestion();
    });

    //Get all 3 dropdowns
    const dropdowns = [...document.querySelectorAll('li[role="presentation"]:nth-child(1) div[id*="dropDownSelectList"]')].filter(el => /-.*-/.test(el.id));

    //Question 1: Yes
    dropdowns[0].click();
    //await delay(500); //was enabled
    let options = document.querySelectorAll('[data-automation-activepopup="true"] [role="option"]');
    for(let option of options) {
      if(option.textContent.trim() === 'Yes') {
        option.click();
        break;
      }
      await delay(1500);
    }

    //Question 2: No
    dropdowns[1].click();
    //await delay(500); //was enabled
    options = document.querySelectorAll('[data-automation-activepopup="true"] [role="option"]');
    for(let option of options) {
      if(option.textContent.trim() === 'No') {
        option.click();
        break;
      }
      await delay(1500);
    }

    //Question 3: Legal - Yes
    dropdowns[2].click();
    //await delay(500); //was enabled
    options = document.querySelectorAll('[data-automation-activepopup="true"] [role="option"]');
    for(let option of options) {
      if(option.textContent.trim() === 'Yes') {
        option.click();
        break;
      }
      await delay(1500);
    }

    //Click Next again
    document.querySelector('[title="Next"]').click();
  }

  async function handleFinalHistoryQuestion() {
    //Wait for the criminal history question to appear
    await new Promise(resolve => {
      const checkForQuestion = () => {
        if (document.body.textContent.includes('guilty')) {
          resolve();
        } else {
          setTimeout(checkForQuestion, 1500);
        }
      };
      checkForQuestion();
    });

    //Find and click the dropdown
    document.querySelectorAll('div[id*="dropDownSelectList"]')[1].click();
    //Select "No"
    const options = document.querySelectorAll('[data-automation-activepopup="true"] [role="option"]');
    for(let option of options) {
      if(option.textContent.trim() === 'No') {
        option.click();
        break;
      }
      await delay(1500);
    }

    //Click Next
    document.querySelector('[title="Next"]').click();
  }

  //NEW main function to orchestrate adding and then filling
  async function runAutoFiller() {
    const triggerBtn = document.getElementById('workday-autofill-btn');
    if (!triggerBtn) return;

    console.log("Starting form auto-fill...");
    triggerBtn.disabled = true;
    triggerBtn.textContent = 'Filling...';

    console.log("Phase 1: Adding all necessary sections.");
    await addSections();
    console.log("Phase 2: Populating data into sections.");
    await fillJobSection();
    await fillEducationSection();
    await fillSkillsSection();
    await fillLanguagesSection();
    console.log("Phase3: Populating data for the last 3 questions.");
    await handleThreePostFillQuestions();
    console.log("Phase 4: Populating data for the last question.");
    await handleFinalHistoryQuestion();

    triggerBtn.disabled = false;
    triggerBtn.textContent = 'Auto-Fill Form';
    console.log("Form auto-fill completed!");
  }

  function handlePageChange() {
    if (location.pathname.includes('/apply/') && !document.getElementById('workday-autofill-btn')) {
      document.body.insertAdjacentHTML('beforeend',"<button id='workday-autofill-btn' style='position:fixed;top:10px;right:10px;z-index:9999;background-color:#007bff;color:white;border:none;padding:10px;border-radius:5px;cursor:pointer;'>Auto-Fill Form</button>");document.getElementById('workday-autofill-btn').addEventListener('click',runAutoFiller);
    } else if (!location.pathname.includes('/apply/') && document.getElementById('workday-autofill-btn')) {
      document.getElementById('workday-autofill-btn').remove();
    }
  }

  //Initial check when the script loads
  handlePageChange();

  //Use a MutationObserver to detect page changes in the SPA
  new MutationObserver(handlePageChange).observe(document.body, {
    childList: true,
    subtree: true
  });
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址