Polish README and point Network tab at GitHub
This commit is contained in:
@@ -1,42 +1,18 @@
|
||||
# portfolio.spec.ts
|
||||
|
||||
> A personal portfolio + resume styled as a **Playwright test runner**. Built by an SDET, for SDETs.
|
||||
>
|
||||
> Gitea remote: `ilia/sdetProfile` (local folder is usually cloned as `portfolio`).
|
||||
[](LICENSE)
|
||||
[](https://sdet.levkin.ca)
|
||||
|
||||
Click the green ▶ next to any test to "run" it — each passing test reveals a portfolio section. Filter by `@tag` chips like a real `--grep`. Includes a career-timeline trace viewer, a Source tab that renders the portfolio as actual-looking Playwright spec code, a **Network** tab that lists public [git.levkin.ca](https://git.levkin.ca/explore/repos) repos as Playwright-style `GET … 200` rows with expandable JSON (descriptions from the Gitea API or each repo's README), and a downloadable PDF resume.
|
||||
Career portfolio styled as a **Playwright test runner** — sidebar explorer, editor tabs, trace viewer, Network tab, keyboard shortcuts.
|
||||
|
||||
**Live preview:** deploy locally (see below) or open `index.html` directly.
|
||||
|
||||
---
|
||||
**Live:** [sdet.levkin.ca](https://sdet.levkin.ca) · **Public mirror:** [github.com/Gitilia/sdetProfile](https://github.com/Gitilia/sdetProfile)
|
||||
|
||||
## Why this exists
|
||||
|
||||
A traditional portfolio doesn't tell a hiring manager you live in test runners all day. This one does — every interaction is a love letter to the tooling SDETs use:
|
||||
|
||||
- Sidebar **Test Explorer** (400px) with collapsible suites, green run arrows, and tree-view ellipsis for long describe labels
|
||||
- **Editor tab strip** (28px, slim) above the report — switch between `portfolio.spec.ts`, `projects.spec.ts`, `skills.spec.ts`, `playground.spec.ts`; each rescopes the explorer, report, source view, and status counts (cookie-persisted)
|
||||
- **Status pill** merged into the editor bar with live pass/fail/skip counts per active spec
|
||||
- **Overflow menu** (⋯) on the right of the editor tabs with `--workers=` and `--headed` controls
|
||||
- **Skipped test** (`should meet performance budget`) with amber ⊘ icon — a 100% green suite looks fake; mixed outcomes feel authentic
|
||||
- Progress bars under each test that fill in real time
|
||||
- **Auto-run** first test on page load so the hero arrives with body content rendered (runner animation still plays)
|
||||
- **Summary stripe** above the results list: `Last run · 1.4s · 4 passed · 1 skipped`
|
||||
- **Pending preview** — the first idle test's body is shown expanded with a faded look + "click ▶ to run" overlay
|
||||
- **Tag bar** with 6 visible tags + "+N more" expand chip + a `× clear` button
|
||||
- **Count bubbles** right-aligned with consistent width, tabular numerals, outlined — reads as "metric" not "tag"
|
||||
- VS Code dark+ / Playwright trace viewer palette (Inter + JetBrains Mono)
|
||||
- A `Trace` tab that draws your career as a Gantt-style waterfall
|
||||
- A `Source` tab that renders the active spec as a real-looking `.spec.ts` file
|
||||
- A `Console` tab that logs test run events live
|
||||
- A `Network` tab modeled on Playwright's network panel — one row per public Gitea repo (`GET https://git.levkin.ca/api/v1/repos/…`), click to expand the faux JSON body
|
||||
- `--headed` toggle that slows animations down for demo mode
|
||||
- `--workers=` selector for parallel test execution (1, 2, or 4 workers)
|
||||
- Cookie-persisted theme cycle: dark → light → high-contrast (WCAG AAA)
|
||||
- Keyboard shortcuts (`?` to see all): `R` run all, `X` reset, `T` theme, `/` grep, `1–9` run Nth test
|
||||
- Mobile drawer for the test explorer, fully responsive
|
||||
|
||||
---
|
||||
Hiring managers who work with SDETs already live in test runners. This site uses
|
||||
that UI metaphor instead of a generic landing page: sidebar explorer, editor tabs,
|
||||
status pill, trace viewer, network panel, and keyboard shortcuts modeled on
|
||||
Playwright and VS Code.
|
||||
|
||||
## Stack
|
||||
|
||||
@@ -308,8 +284,4 @@ See [IDEAS.md](IDEAS.md) for the backlog. Quick wins on top, ambitious experimen
|
||||
|
||||
## License
|
||||
|
||||
MIT. Fork it, restyle it, replace the content with your own. If you ship a variation, a wave hello on LinkedIn would be appreciated but is not required.
|
||||
|
||||
---
|
||||
|
||||
**Built by** [Ilia Dobkin](https://www.linkedin.com/in/ilia-dobkin-8263343/) · Senior SDET · Remote (ET)
|
||||
MIT — see [LICENSE](LICENSE). Fork and replace the content as you like.
|
||||
|
||||
+2
-2
@@ -186,11 +186,11 @@
|
||||
<div class="source" id="source"></div>
|
||||
</div>
|
||||
|
||||
<!-- Network (Gitea repos as Playwright-style requests) -->
|
||||
<!-- Network (public GitHub repos as Playwright-style requests) -->
|
||||
<div class="pane" id="pane-network" role="tabpanel">
|
||||
<div class="network-pane">
|
||||
<div class="network-head">
|
||||
<div class="network-head__title">Network · git.levkin.ca</div>
|
||||
<div class="network-head__title">Network · github.com/Gitilia</div>
|
||||
<div class="network-head__meta"><span class="legend"><i class="sw sw--pass"></i> 200</span><span class="legend mono">application/json</span></div>
|
||||
</div>
|
||||
<div class="network-scroll" id="gitea-network" data-repo-list="1"></div>
|
||||
|
||||
@@ -677,7 +677,7 @@
|
||||
}
|
||||
const GF = window.PORTFOLIO;
|
||||
const repos = GF && Array.isArray(GF.giteaRepos) ? GF.giteaRepos : [];
|
||||
const apiRoot = 'https://git.levkin.ca/api/v1/repos/';
|
||||
const apiRoot = 'https://api.github.com/repos/';
|
||||
if (repos.length === 0) {
|
||||
wrap.innerHTML = `<div class="network-empty">No <span class="mono">giteaRepos</span> in <span class="mono">js/data.js</span>. Hard refresh the page (<span class="mono">Cmd+Shift+R</span>). If deploying, redeploy after adding the array.</div>`;
|
||||
return;
|
||||
|
||||
+18
-27
@@ -8,7 +8,8 @@ window.PORTFOLIO = {
|
||||
workAuth: "Canadian citizen",
|
||||
email: "idobkin@gmail.com",
|
||||
linkedin: "https://www.linkedin.com/in/ilia-dobkin-8263343/",
|
||||
gitea: "https://git.levkin.ca",
|
||||
gitea: "https://github.com/Gitilia",
|
||||
github: "https://github.com/Gitilia",
|
||||
site: "https://sdet.levkin.ca",
|
||||
blurb:
|
||||
"Senior SDET with 20+ years in audit/financial software and regulated web, including real-money iGaming. Deep across Playwright, Swagger/OpenAPI contract testing, and performance baselines integrated into CI/CD. I treat automation as a personal discipline as much as a job: scripts, shortcuts, and agents that streamline my day so engineering effort goes where it matters. Strong instinct for stabilizing flaky suites, tightening quality gates, and removing manual regression effort wherever it lives.",
|
||||
@@ -185,7 +186,7 @@ window.PORTFOLIO = {
|
||||
tags: ["@api","@perf"],
|
||||
duration: 0,
|
||||
steps: [
|
||||
{ kind: "ok", title: 'navigate to Gitea API endpoint', dur: 45 },
|
||||
{ kind: "ok", title: 'navigate to GitHub API endpoint', dur: 45 },
|
||||
{ kind: "ok", title: 'send GET /api/v1/repos/ilia/portfolio', dur: 62 },
|
||||
{ kind: "ok", title: 'assert status 200', dur: 8 },
|
||||
{ kind: "fail", title: 'expect(latency).toBeLessThan(200)', dur: 347 },
|
||||
@@ -377,30 +378,20 @@ window.PORTFOLIO = {
|
||||
],
|
||||
|
||||
/**
|
||||
* Public repos on git.levkin.ca — descriptions from Gitea API when set,
|
||||
* otherwise first paragraph of README (see scripts/fetch-gitea-repos.mjs).
|
||||
* API lists all 19 repos on one page (explore UI may paginate).
|
||||
* Public repos on github.com/Gitilia — mirrored from Gitea for recruiters.
|
||||
* Keep this list to public GitHub only (no private forge links).
|
||||
*/
|
||||
giteaRepos: [
|
||||
{ full_name: "ilia/ansible", name: "ansible", html_url: "https://git.levkin.ca/ilia/ansible", language: "Makefile", description: "Ansible automation for development machines, service hosts, and Proxmox-managed guests (LXC-first, with a path for KVM VMs)." },
|
||||
{ full_name: "ilia/AtAnyRate", name: "AtAnyRate", html_url: "https://git.levkin.ca/ilia/AtAnyRate", language: "Python", description: "Local Python application that identifies upcoming Toronto events likely to increase Airbnb demand, sends Telegram alerts, and optionally adjusts nightly prices via Playwright automation." },
|
||||
{ full_name: "ilia/atlas", name: "atlas", html_url: "https://git.levkin.ca/ilia/atlas", language: "Python", description: "Atlas is a local, privacy-focused home voice agent system — planning, architecture documentation, and kanban tickets for building the system." },
|
||||
{ full_name: "ilia/crkl", name: "crkl", html_url: "https://git.levkin.ca/ilia/crkl", language: "Kotlin", description: "Privacy-first Android AI assistant — circle or touch any element on-screen; on-device AI transcribes, summarizes, explains, or drafts responses." },
|
||||
{ full_name: "ilia/dotfiles", name: "dotfiles", html_url: "https://git.levkin.ca/ilia/dotfiles", language: "", description: "Dotfiles and shell configuration for dev machines." },
|
||||
{ full_name: "ilia/hilitehero", name: "hilitehero", html_url: "https://git.levkin.ca/ilia/hilitehero", language: "Python", description: "Python tool for extracting highlighted text from PDFs with precise ordering and hyphenation handling." },
|
||||
{ full_name: "ilia/invoice", name: "invoice", html_url: "https://git.levkin.ca/ilia/invoice", language: "JavaScript", description: "CLI for generating professional PDF invoices from JSON — interactive and non-interactive modes with preview-first workflow." },
|
||||
{ full_name: "ilia/Jobber", name: "Jobber", html_url: "https://git.levkin.ca/ilia/Jobber", language: "TypeScript", description: "Self-hosted job search orchestration — discover roles, score fit, draft resumes and cover letters, export PDFs, track email; you submit applications yourself." },
|
||||
{ full_name: "ilia/kanban", name: "kanban", html_url: "https://git.levkin.ca/ilia/kanban", language: "", description: "Kanban board project on self-hosted Gitea." },
|
||||
{ full_name: "ilia/linkedout", name: "linkedout", html_url: "https://git.levkin.ca/ilia/linkedout", language: "JavaScript", description: "Job market intelligence platform with integrated AI-powered insights — modular architecture for extensibility." },
|
||||
{ full_name: "ilia/llm_council", name: "llm_council", html_url: "https://git.levkin.ca/ilia/llm_council", language: "Python", description: "Local web UI like ChatGPT but sends each query to multiple LLMs — your \"LLM council\" votes with diverse models." },
|
||||
{ full_name: "ilia/mirror_match", name: "mirror_match", html_url: "https://git.levkin.ca/ilia/mirror_match", language: "TypeScript", description: "Photo guessing game — upload photos, others guess who is in the picture for points. Next.js, PostgreSQL, NextAuth." },
|
||||
{ full_name: "ilia/nanobot", name: "nanobot", html_url: "https://git.levkin.ca/ilia/nanobot", language: "Python", description: "Ultra-lightweight personal AI assistant (Python; published on PyPI as nanobot-ai)." },
|
||||
{ full_name: "ilia/onboarding", name: "onboarding", html_url: "https://git.levkin.ca/ilia/onboarding", language: "Shell", description: "Developer environment setup — automates 60+ apps and tools plus Git and SSH configuration." },
|
||||
{ full_name: "ilia/outreach", name: "outreach", html_url: "https://git.levkin.ca/ilia/outreach", language: "JavaScript", description: "Node.js email outreach for campaigns to law firms — templates, tracking, and tests." },
|
||||
{ full_name: "ilia/POTE", name: "POTE", html_url: "https://git.levkin.ca/ilia/POTE", language: "Python", description: "Research-oriented tool for tracking and analyzing public stock trades by government officials." },
|
||||
{ full_name: "ilia/profile", name: "profile", html_url: "https://git.levkin.ca/ilia/profile", language: "TypeScript", description: "Profile / personal site — TypeScript." },
|
||||
{ full_name: "ilia/punimtag", name: "punimtag", html_url: "https://git.levkin.ca/ilia/punimtag", language: "TypeScript", description: "Modern photo management and facial recognition system." },
|
||||
{ full_name: "ilia/resume", name: "resume", html_url: "https://git.levkin.ca/ilia/resume", language: "", description: "Résumé generator based on the best-resume-ever project." }
|
||||
{ full_name: "Gitilia/playkit", name: "playkit", html_url: "https://github.com/Gitilia/playkit", language: "TypeScript", description: "Shared Playwright + API test helpers — UI actions, API client, timings, metrics." },
|
||||
{ full_name: "Gitilia/context-extractor", name: "context-extractor", html_url: "https://github.com/Gitilia/context-extractor", language: "JavaScript", description: "Browser extension + Playwright package that dumps AI-ready page markdown, console, and network noise." },
|
||||
{ full_name: "Gitilia/PaperPod", name: "PaperPod", html_url: "https://github.com/Gitilia/PaperPod", language: "Python", description: "Offline overhead video → named PDFs for Paperless (CV + OCR)." },
|
||||
{ full_name: "Gitilia/SwipeAnything", name: "SwipeAnything", html_url: "https://github.com/Gitilia/SwipeAnything", language: "TypeScript", description: "Local-first swipe-to-triage UI with pluggable adapters." },
|
||||
{ full_name: "Gitilia/maCopy", name: "maCopy", html_url: "https://github.com/Gitilia/maCopy", language: "TypeScript", description: "Native macOS menu-bar clipboard manager (Tauri + React)." },
|
||||
{ full_name: "Gitilia/slack-sieve", name: "slack-sieve", html_url: "https://github.com/Gitilia/slack-sieve", language: "Python", description: "Sieve a Slack workspace export before Mattermost import." },
|
||||
{ full_name: "Gitilia/sdetProfile", name: "sdetProfile", html_url: "https://github.com/Gitilia/sdetProfile", language: "JavaScript", description: "Career portfolio styled as a Playwright test runner." },
|
||||
{ full_name: "Gitilia/portfolio", name: "portfolio", html_url: "https://github.com/Gitilia/portfolio", language: "JavaScript", description: "Personal hub for iliadobkin.com — projects, demos, consulting lanes." },
|
||||
{ full_name: "Gitilia/levkin.ca", name: "levkin.ca", html_url: "https://github.com/Gitilia/levkin.ca", language: "HTML", description: "Levkin Inc. public homepage — static stack site." },
|
||||
{ full_name: "Gitilia/mirror_match", name: "mirror_match", html_url: "https://github.com/Gitilia/mirror_match", language: "TypeScript", description: "Photo guessing game — upload photos, others guess who is in the picture for points." },
|
||||
]
|
||||
};
|
||||
|
||||
@@ -482,7 +473,7 @@ function renderProjects(){
|
||||
${_tags(p.tags)}
|
||||
</div>`).join('')
|
||||
}</div>
|
||||
<p class="projects-foot">Public code on <a href="https://git.levkin.ca/explore/repos" target="_blank" rel="noopener">git.levkin.ca</a> — open the <button type="button" class="tab-link" data-tab="network">Network</button> tab for an API-style request list.</p>
|
||||
<p class="projects-foot">Public code on <a href="https://github.com/Gitilia" target="_blank" rel="noopener">github.com/Gitilia</a> — open the <button type="button" class="tab-link" data-tab="network">Network</button> tab for an API-style request list.</p>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
@@ -537,7 +528,7 @@ function renderContact(){
|
||||
return `<div class="block"><div class="contact-grid">
|
||||
<div class="contact-cell"><label>email</label><a href="mailto:${p.email}">${p.email}</a></div>
|
||||
<div class="contact-cell"><label>linkedin</label><a href="${p.linkedin}" target="_blank" rel="noopener">in/idobkin</a></div>
|
||||
<div class="contact-cell"><label>gitea (self-hosted)</label><a href="${p.gitea}" target="_blank" rel="noopener">git.levkin.ca</a></div>
|
||||
<div class="contact-cell"><label>github</label><a href="${p.github || p.gitea}" target="_blank" rel="noopener">github.com/Gitilia</a></div>
|
||||
<div class="contact-cell"><label>site</label><a href="${p.site}" target="_blank" rel="noopener">sdet.levkin.ca</a></div>
|
||||
<div class="contact-cell"><label>location</label>${_esc(p.location)}${p.workAuth ? ` · ${_esc(p.workAuth)}` : ''}</div>
|
||||
</div></div>`;
|
||||
@@ -552,7 +543,7 @@ function renderPerfBudget(){
|
||||
|
||||
function renderResponseTime(){
|
||||
return `<div class="block">
|
||||
<p>The Gitea API endpoint <code>GET /api/v1/repos/ilia/portfolio</code> exceeded the <strong>200 ms</strong> latency budget three times in a row.</p>
|
||||
<p>The GitHub API endpoint <code>GET /repos/Gitilia/portfolio</code> exceeded the <strong>200 ms</strong> latency budget three times in a row.</p>
|
||||
<h4>What happened</h4>
|
||||
<ul>
|
||||
<li>Initial request returned in <strong>347 ms</strong> — likely a cold-start on the VPS.</li>
|
||||
|
||||
Reference in New Issue
Block a user