I don’t like silly tasks.
My legal employer has me fill in my hours every week, despite me being full-time with the subcontracted company. My hours don’t change. I am basically salaried, indirectly. It’s a long story. (Working remote from Hawai’i has its perks and its quirks.)
If I’m honest, it’s not that bad. The timesheet has an “autofill” feature so I usually just click a couple buttons and then I’m done.
A couple months ago, I forgot to click those buttons.
My only job (other than my job) is to tell my job that I did my job.
The money didn’t land. We were in the middle of moving money around for a family event and the money not showing up became a little stressful. It was my fault, but I was still irritated.
I decided to setup 2 reminders instead of 1 reminder to fill in my hours every week.
This worked well for a couple months. But as a software engineer I knew I could do better.
I went searching for some simple cron hosting sites. I was hoping that replit.com had free cron triggers but no luck. I didn’t care what I used, as long as it was easy to setup and free.
After a quick search, it turns out the easiest and cheapest thing to do what I wanted was to use Playwright and Github Actions. I also had email notifications as a requirement for success/failures and Github Actions has that built in!
I setup a new Playwright project, add a couple lines of code for an “end-to-end” test (despite it not being a test, but a cron task) and then created the Github Action and voilĂ — timesheets automatically filled out every week. Github even tells me via email if the task completed or not. Perfect!
Here’s how I set it up. You can use this template for anything, really. The Playwright and Github Actions combo is awesome!
First, I created a playwright project (docs):
npm init playwright@latest
I followed the prompts to create a TypeScript project and had it setup Github Actions stuff for me.
Next, I installed dotenv
to keep my secrets out of source code:
npm i dotenv@latest
I also deleted the test examples. Don’t need those.
Then I wrote my test. I found it easier to debug it by using the visual test runner that playwright provides. You can start it with:
npx playwright test --ui
Here’s our test:
import { test } from "@playwright/test"; test("fill timecard", async ({ page }) => { await page.goto(process.env.LOGIN_URL); await page.click("button:has-text('Accept Cookies')"); await page.click("text=Contingent Workers"); await page.click(".login-input-container input"); await page.waitForTimeout(1000); await page.keyboard.type(process.env.USERNAME, { delay: 100 }); await page.keyboard.press("Tab"); await page.keyboard.type(process.env.PASSWORD, { delay: 100 }); await page.keyboard.press("Tab"); await page.click("[type=submit]"); await page.waitForLoadState(); await page.click("td a img"); await page.click("text=Auto-Fill Timecard"); page.on("dialog", (dialog) => dialog.accept()); await page.click("text=Fill Timecard"); await page.click("input:has-text('Submit To Manager')"); await page.waitForSelector("text=Submitted", { state: "visible" }); });
You can see it’s pretty simple. It requires 3 environment variables to run properly, LOGIN_URL
, USERNAME
, and PASSWORD
.
Next thing is to create a repository on Github.com. I did so and then uploaded everything.
Lastly, we told Github Actions we’d like to run this every week. I used the pre-generated .github/workflows/playwright.yml
as a template and modified it to look as follows:
# cron.yml
name: Fill timecard
on:
schedule:
- cron: 0 16 * * 1
workflow_dispatch:
jobs:
fill-timecard:
timeout-minutes: 15
runs-on: ubuntu-latest
env:
LOGIN_URL: ${{ secrets.LOGIN_URL }}
USERNAME: ${{ secrets.USERNAME }}
PASSWORD: ${{ secrets.PASSWORD }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: lts/*
- name: Install dependencies
run: npm ci
- name: Install Playwright Browsers
run: npx playwright install --with-deps
- name: Run Playwright tests
run: npx playwright test
There’s a couple thing to note here.
First is the cron schedule string at the top. We told Github Actions to bootstrap a runner to execute those jobs every Monday at 16:00 UTC (if you’re as confused about cron format, head over to crontab.guru).
Second is this weird workflow_dispatch:
line. We told Github Actions to allow “dispatching” from a cute green button, so that we can run this action whenever we feel like without having to wait until Monday or pull down the repository.
Next lines are boilerplate. We told actions we wanna create a job and then gave it a timeout and specified the runner that we wanted. (Note: linux always cheaper, MacOS is ridiculously expensive. I am cheap.)
In order to login to our site, we needed to setup those environment variables.
I added some secrets LOGIN_URL
, USERNAME
and PASSWORD
to the Github repo. (Settings > Secrets and Variables > Actions > Repository Secrets > New Repository Secret
) and created all three of those with the proper values.
And there it is!
Now my timecard is automatically filled every Monday, I get back 2 minutes of my day, and I have some peace of mind with email notifications when things fail.
Subscribe for more nerdy content.