Skip to content

Commit ec67288

Browse files
committed
Add test for filters
1 parent 5483333 commit ec67288

File tree

9 files changed

+257
-20
lines changed

9 files changed

+257
-20
lines changed

.github/workflows/test-and-release.yml

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
# .github/workflows/test-and-release.yml
21
name: Test and Release
32

43
on:
@@ -62,13 +61,53 @@ jobs:
6261
- name: Create extension archive
6362
run: |
6463
node chromePackage.js
65-
64+
65+
# Chrome Web Store deployment
6666
- name: Submit to Chrome Web Store
67-
if: ${{ !contains(github.event.head_commit.message, '[skip-release]') }}
67+
if: ${{ !contains(github.event.head_commit.message, '[skip-release]') && !contains(github.event.head_commit.message, '[skip-release-chrome]') }}
6868
run: |
6969
npx chrome-webstore-upload-cli@2 upload --source extension.zip --auto-publish
7070
env:
7171
EXTENSION_ID: ${{ secrets.EXTENSION_ID }}
7272
CLIENT_ID: ${{ secrets.CLIENT_ID }}
7373
CLIENT_SECRET: ${{ secrets.CLIENT_SECRET }}
74-
REFRESH_TOKEN: ${{ secrets.REFRESH_TOKEN }}
74+
REFRESH_TOKEN: ${{ secrets.REFRESH_TOKEN }}
75+
76+
# Microsoft Edge Add-on deployment
77+
- name: Submit to Microsoft Edge Add-ons
78+
if: ${{ !contains(github.event.head_commit.message, '[skip-release]') && !contains(github.event.head_commit.message, '[skip-release-edge]') }}
79+
run: |
80+
# Upload package
81+
UPLOAD_RESPONSE=$(curl -X POST \
82+
-H "Authorization: ApiKey ${{ secrets.EDGE_API_KEY }}" \
83+
-H "X-ClientID: ${{ secrets.EDGE_CLIENT_ID }}" \
84+
-H "Content-Type: application/zip" \
85+
-T extension.zip \
86+
https://api.addons.microsoftedge.microsoft.com/v1/products/${{ secrets.EDGE_PRODUCT_ID }}/submissions/draft/package)
87+
88+
OPERATION_ID=$(echo $UPLOAD_RESPONSE | jq -r '.operationId')
89+
90+
# Wait for upload to complete
91+
while true; do
92+
STATUS=$(curl -X GET \
93+
-H "Authorization: ApiKey ${{ secrets.EDGE_API_KEY }}" \
94+
-H "X-ClientID: ${{ secrets.EDGE_CLIENT_ID }}" \
95+
https://api.addons.microsoftedge.microsoft.com/v1/products/${{ secrets.EDGE_PRODUCT_ID }}/submissions/draft/package/operations/$OPERATION_ID \
96+
| jq -r '.status')
97+
98+
if [ "$STATUS" = "Succeeded" ]; then
99+
break
100+
elif [ "$STATUS" = "Failed" ]; then
101+
echo "Package upload failed"
102+
exit 1
103+
fi
104+
105+
sleep 10
106+
done
107+
108+
# Publish submission
109+
PUBLISH_RESPONSE=$(curl -X POST \
110+
-H "Authorization: ApiKey ${{ secrets.EDGE_API_KEY }}" \
111+
-H "X-ClientID: ${{ secrets.EDGE_CLIENT_ID }}" \
112+
-d '{"notes":"Automated deployment"}' \
113+
https://api.addons.microsoftedge.microsoft.com/v1/products/${{ secrets.EDGE_PRODUCT_ID }}/submissions)

coverage/coverage-summary.json

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
1-
{"total": {"lines":{"total":3753,"covered":1742,"skipped":0,"pct":46.41},"statements":{"total":3753,"covered":1742,"skipped":0,"pct":46.41},"functions":{"total":54,"covered":38,"skipped":0,"pct":70.37},"branches":{"total":392,"covered":278,"skipped":0,"pct":70.91},"branchesTrue":{"total":0,"covered":0,"skipped":0,"pct":100}}
2-
,"C:\\Users\\yuta\\Documents\\har\\HARlytics\\vite.config.js": {"lines":{"total":37,"covered":0,"skipped":0,"pct":0},"functions":{"total":1,"covered":0,"skipped":0,"pct":0},"statements":{"total":37,"covered":0,"skipped":0,"pct":0},"branches":{"total":1,"covered":0,"skipped":0,"pct":0}}
1+
{"total": {"lines":{"total":3716,"covered":3413,"skipped":0,"pct":91.84},"statements":{"total":3716,"covered":3413,"skipped":0,"pct":91.84},"functions":{"total":83,"covered":44,"skipped":0,"pct":53.01},"branches":{"total":467,"covered":304,"skipped":0,"pct":65.09},"branchesTrue":{"total":0,"covered":0,"skipped":0,"pct":100}}
32
,"C:\\Users\\yuta\\Documents\\har\\HARlytics\\src\\lib\\CookieTable.svelte": {"lines":{"total":68,"covered":68,"skipped":0,"pct":100},"functions":{"total":2,"covered":2,"skipped":0,"pct":100},"statements":{"total":68,"covered":68,"skipped":0,"pct":100},"branches":{"total":11,"covered":6,"skipped":0,"pct":54.54}}
43
,"C:\\Users\\yuta\\Documents\\har\\HARlytics\\src\\lib\\FileUpload.svelte": {"lines":{"total":8,"covered":8,"skipped":0,"pct":100},"functions":{"total":0,"covered":0,"skipped":0,"pct":100},"statements":{"total":8,"covered":8,"skipped":0,"pct":100},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}}
54
,"C:\\Users\\yuta\\Documents\\har\\HARlytics\\src\\lib\\chartUtils.js": {"lines":{"total":64,"covered":64,"skipped":0,"pct":100},"functions":{"total":2,"covered":2,"skipped":0,"pct":100},"statements":{"total":64,"covered":64,"skipped":0,"pct":100},"branches":{"total":19,"covered":19,"skipped":0,"pct":100}}
65
,"C:\\Users\\yuta\\Documents\\har\\HARlytics\\src\\lib\\constants.js": {"lines":{"total":51,"covered":51,"skipped":0,"pct":100},"functions":{"total":0,"covered":0,"skipped":0,"pct":100},"statements":{"total":51,"covered":51,"skipped":0,"pct":100},"branches":{"total":0,"covered":0,"skipped":0,"pct":100}}
76
,"C:\\Users\\yuta\\Documents\\har\\HARlytics\\src\\lib\\sequenceDiagramGenerator.js": {"lines":{"total":564,"covered":433,"skipped":0,"pct":76.77},"functions":{"total":18,"covered":16,"skipped":0,"pct":88.88},"statements":{"total":564,"covered":433,"skipped":0,"pct":76.77},"branches":{"total":105,"covered":79,"skipped":0,"pct":75.23}}
87
,"C:\\Users\\yuta\\Documents\\har\\HARlytics\\src\\lib\\utils.js": {"lines":{"total":517,"covered":385,"skipped":0,"pct":74.46},"functions":{"total":19,"covered":15,"skipped":0,"pct":78.94},"statements":{"total":517,"covered":385,"skipped":0,"pct":74.46},"branches":{"total":157,"covered":139,"skipped":0,"pct":88.53}}
9-
,"C:\\Users\\yuta\\Documents\\har\\HARlytics\\src\\lib\\components\\EntryDetailTable.svelte": {"lines":{"total":280,"covered":0,"skipped":0,"pct":0},"functions":{"total":1,"covered":0,"skipped":0,"pct":0},"statements":{"total":280,"covered":0,"skipped":0,"pct":0},"branches":{"total":1,"covered":0,"skipped":0,"pct":0}}
8+
,"C:\\Users\\yuta\\Documents\\har\\HARlytics\\src\\lib\\components\\EntryDetailTable.svelte": {"lines":{"total":280,"covered":280,"skipped":0,"pct":100},"functions":{"total":9,"covered":0,"skipped":0,"pct":0},"statements":{"total":280,"covered":280,"skipped":0,"pct":100},"branches":{"total":4,"covered":2,"skipped":0,"pct":50}}
109
,"C:\\Users\\yuta\\Documents\\har\\HARlytics\\src\\lib\\components\\EntryRowGeneral.svelte": {"lines":{"total":463,"covered":461,"skipped":0,"pct":99.56},"functions":{"total":7,"covered":3,"skipped":0,"pct":42.85},"statements":{"total":463,"covered":461,"skipped":0,"pct":99.56},"branches":{"total":66,"covered":14,"skipped":0,"pct":21.21}}
1110
,"C:\\Users\\yuta\\Documents\\har\\HARlytics\\src\\lib\\components\\FilterInput.svelte": {"lines":{"total":22,"covered":0,"skipped":0,"pct":0},"functions":{"total":1,"covered":0,"skipped":0,"pct":0},"statements":{"total":22,"covered":0,"skipped":0,"pct":0},"branches":{"total":1,"covered":0,"skipped":0,"pct":0}}
12-
,"C:\\Users\\yuta\\Documents\\har\\HARlytics\\src\\lib\\components\\PieChart.svelte": {"lines":{"total":83,"covered":0,"skipped":0,"pct":0},"functions":{"total":1,"covered":0,"skipped":0,"pct":0},"statements":{"total":83,"covered":0,"skipped":0,"pct":0},"branches":{"total":1,"covered":0,"skipped":0,"pct":0}}
11+
,"C:\\Users\\yuta\\Documents\\har\\HARlytics\\src\\lib\\components\\PieChart.svelte": {"lines":{"total":83,"covered":83,"skipped":0,"pct":100},"functions":{"total":0,"covered":0,"skipped":0,"pct":100},"statements":{"total":83,"covered":83,"skipped":0,"pct":100},"branches":{"total":2,"covered":2,"skipped":0,"pct":100}}
1312
,"C:\\Users\\yuta\\Documents\\har\\HARlytics\\src\\lib\\components\\SequenceExport.svelte": {"lines":{"total":148,"covered":147,"skipped":0,"pct":99.32},"functions":{"total":0,"covered":0,"skipped":0,"pct":100},"statements":{"total":148,"covered":147,"skipped":0,"pct":99.32},"branches":{"total":4,"covered":1,"skipped":0,"pct":25}}
1413
,"C:\\Users\\yuta\\Documents\\har\\HARlytics\\src\\lib\\components\\WaterfallBar.svelte": {"lines":{"total":125,"covered":125,"skipped":0,"pct":100},"functions":{"total":0,"covered":0,"skipped":0,"pct":100},"statements":{"total":125,"covered":125,"skipped":0,"pct":100},"branches":{"total":24,"covered":20,"skipped":0,"pct":83.33}}
15-
,"C:\\Users\\yuta\\Documents\\har\\HARlytics\\src\\routes\\+layout.svelte": {"lines":{"total":81,"covered":0,"skipped":0,"pct":0},"functions":{"total":1,"covered":0,"skipped":0,"pct":0},"statements":{"total":81,"covered":0,"skipped":0,"pct":0},"branches":{"total":1,"covered":0,"skipped":0,"pct":0}}
16-
,"C:\\Users\\yuta\\Documents\\har\\HARlytics\\src\\routes\\+page.svelte": {"lines":{"total":1242,"covered":0,"skipped":0,"pct":0},"functions":{"total":1,"covered":0,"skipped":0,"pct":0},"statements":{"total":1242,"covered":0,"skipped":0,"pct":0},"branches":{"total":1,"covered":0,"skipped":0,"pct":0}}
14+
,"C:\\Users\\yuta\\Documents\\har\\HARlytics\\src\\routes\\+layout.svelte": {"lines":{"total":81,"covered":79,"skipped":0,"pct":97.53},"functions":{"total":1,"covered":0,"skipped":0,"pct":0},"statements":{"total":81,"covered":79,"skipped":0,"pct":97.53},"branches":{"total":5,"covered":0,"skipped":0,"pct":0}}
15+
,"C:\\Users\\yuta\\Documents\\har\\HARlytics\\src\\routes\\+page.svelte": {"lines":{"total":1242,"covered":1229,"skipped":0,"pct":98.95},"functions":{"total":24,"covered":6,"skipped":0,"pct":25},"statements":{"total":1242,"covered":1229,"skipped":0,"pct":98.95},"branches":{"total":69,"covered":22,"skipped":0,"pct":31.88}}
1716
}

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
{
22
"name": "harlytics",
3-
"version": "0.1.6",
3+
"version": "0.2.0",
44
"private": true,
55
"scripts": {
66
"preinstall": "npx only-allow pnpm",
77
"dev": "vite dev",
88
"build": "node ./build.js && vite build",
99
"preview": "vite preview",
1010
"test": "vitest",
11-
"test:coverage": "vitest run --coverage",
11+
"test:coverage": "vitest run --coverage --exclude 'vite.config.js'",
1212
"test:e2e": "playwright test"
1313
},
1414
"devDependencies": {

src/routes/+layout.svelte

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,9 @@
8686
class="md:ml-auto flex flex-wrap items-center space-x-2 text-base justify-center"
8787
>
8888
<!-- <DarkMode {btnClass} /> -->
89-
<div id="buildTimestamp" class="text-xs" title="Build: 2025-02-05 09:56:35 UTC">v0.1.6</div>
89+
<div id="buildTimestamp" class="text-xs" title="Build: 2025-02-05 16:48:31 UTC">v0.2.0</div>
9090
{#if isLive}
91-
<Badge large color="indigo" class="ml-4">Cloud Edition</Badge>
91+
<Badge large color="indigo" class="ml-4" data-testid="cloud-edition-badge">Cloud Edition</Badge>
9292
{/if}
9393
<Button
9494
color="light"

src/routes/+page.svelte

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1332,7 +1332,7 @@ function handleMouseLeave(type) {
13321332
13331333
<!-- svelte-ignore a11y-no-static-element-interactions -->
13341334
<div
1335-
class="relative inline-block col-span-2 ml-2"
1335+
class="relative inline-block col-span-2 ml-2" data-testid="method-filter"
13361336
on:mouseenter={() => handleMouseEnter("method")}
13371337
on:mouseleave={() => handleMouseLeave("method")}
13381338
>
@@ -1383,7 +1383,7 @@ function handleMouseLeave(type) {
13831383
13841384
<!-- svelte-ignore a11y-no-static-element-interactions -->
13851385
<div
1386-
class="relative inline-block col-span-2 ml-2"
1386+
class="relative inline-block col-span-2 ml-2" data-testid="status-filter"
13871387
on:mouseenter={() => handleMouseEnter("status")}
13881388
on:mouseleave={() => handleMouseLeave("status")}
13891389
>
@@ -1440,7 +1440,7 @@ function handleMouseLeave(type) {
14401440
14411441
<!-- svelte-ignore a11y-no-static-element-interactions -->
14421442
<div
1443-
class="relative inline-block col-span-2 ml-2"
1443+
class="relative inline-block col-span-2 ml-2" data-testid="type-filter"
14441444
on:mouseenter={() => handleMouseEnter("type")}
14451445
on:mouseleave={() => handleMouseLeave("type")}
14461446
>
@@ -1491,7 +1491,7 @@ function handleMouseLeave(type) {
14911491
14921492
<!-- svelte-ignore a11y-no-static-element-interactions -->
14931493
<div
1494-
class="relative inline-block col-span-2 ml-2"
1494+
class="relative inline-block col-span-2 ml-2" data-testid="message-filter"
14951495
on:mouseenter={() => handleMouseEnter("messageElement")}
14961496
on:mouseleave={() => handleMouseLeave("messageElement")}
14971497
>

static/manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"manifest_version": 3,
33
"name": "HARlytics",
4-
"version": "0.1.6",
4+
"version": "0.2.0",
55
"description": "HARlytics is a powerful HAR file analyzer that transforms complex HTTP Archive files into actionable insights.",
66
"icons": {
77
"16": "favicon.png",
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
import { describe, it, expect, beforeEach } from 'vitest';
2+
import { render, fireEvent } from '@testing-library/svelte';
3+
import Page from '../../../src/routes/+page.svelte';
4+
5+
describe('Filter Tests', () => {
6+
let component;
7+
8+
beforeEach(() => {
9+
component = render(Page);
10+
});
11+
12+
describe('Method Filter', () => {
13+
it('should show method filter button with correct initial state', () => {
14+
const methodFilter = component.getByTestId('method-filter');
15+
const button = methodFilter.querySelector('button');
16+
expect(button.textContent).toContain('Method Filter');
17+
expect(button.classList).toContain('bg-white');
18+
});
19+
20+
it('should show filter icon when methods are filtered', async () => {
21+
const methodFilter = component.getByTestId('method-filter');
22+
await fireEvent.mouseEnter(methodFilter);
23+
24+
const checkbox = methodFilter.querySelector('input[type="checkbox"]');
25+
await fireEvent.click(checkbox);
26+
27+
const filterIcon = methodFilter.querySelector('svg.w-3.h-3.mr-1');
28+
expect(filterIcon).toBeTruthy();
29+
});
30+
31+
it('should update filter state when toggling methods', async () => {
32+
const methodFilter = component.getByTestId('method-filter');
33+
await fireEvent.mouseEnter(methodFilter);
34+
35+
const allToggle = methodFilter.querySelector('input[type="checkbox"]');
36+
await fireEvent.click(allToggle);
37+
38+
const button = methodFilter.querySelector('button');
39+
expect(button.classList).toContain('bg-primary-700');
40+
});
41+
42+
it('should display correct method counts', async () => {
43+
const methodFilter = component.getByTestId('method-filter');
44+
await fireEvent.mouseEnter(methodFilter);
45+
46+
const methodLabels = methodFilter.querySelectorAll('.rounded label:not(:first-child)');
47+
methodLabels.forEach(label => {
48+
expect(label.textContent).toMatch(/\(\d+\/\d+\)/);
49+
});
50+
});
51+
});
52+
53+
describe('Status Filter', () => {
54+
it('should show status filter button with correct initial state', () => {
55+
const statusFilter = component.getByTestId('status-filter');
56+
const button = statusFilter.querySelector('button');
57+
expect(button.textContent).toContain('Status Filter');
58+
expect(button.classList).toContain('bg-white');
59+
});
60+
61+
it('should show filter icon when status is filtered', async () => {
62+
const statusFilter = component.getByTestId('status-filter');
63+
await fireEvent.mouseEnter(statusFilter);
64+
65+
const checkbox = statusFilter.querySelector('input[type="checkbox"]');
66+
await fireEvent.click(checkbox);
67+
68+
const filterIcon = statusFilter.querySelector('svg.w-3.h-3.mr-1');
69+
expect(filterIcon).toBeTruthy();
70+
});
71+
72+
it('should update filter state when changing status selections', async () => {
73+
const statusFilter = component.getByTestId('status-filter');
74+
await fireEvent.mouseEnter(statusFilter);
75+
76+
const statusCheckboxes = statusFilter.querySelectorAll('.rounded input[type="checkbox"]');
77+
await fireEvent.click(statusCheckboxes[1]);
78+
79+
const button = statusFilter.querySelector('button');
80+
expect(button.classList).toContain('bg-primary-700');
81+
});
82+
});
83+
84+
describe('MimeType Filter', () => {
85+
it('should show mime type filter button with correct initial state', () => {
86+
const typeFilter = component.getByTestId('type-filter');
87+
const button = typeFilter.querySelector('button');
88+
expect(button.textContent).toContain('mimeType Filter');
89+
expect(button.classList).toContain('bg-white');
90+
});
91+
92+
it('should show filter icon when type is filtered', async () => {
93+
const typeFilter = component.getByTestId('type-filter');
94+
await fireEvent.mouseEnter(typeFilter);
95+
96+
const checkbox = typeFilter.querySelector('input[type="checkbox"]');
97+
await fireEvent.click(checkbox);
98+
99+
const filterIcon = typeFilter.querySelector('svg.w-3.h-3.mr-1');
100+
expect(filterIcon).toBeTruthy();
101+
});
102+
103+
it('should update filter state when changing type selections', async () => {
104+
const typeFilter = component.getByTestId('type-filter');
105+
await fireEvent.mouseEnter(typeFilter);
106+
107+
const typeCheckboxes = typeFilter.querySelectorAll('.rounded input[type="checkbox"]');
108+
await fireEvent.click(typeCheckboxes[1]);
109+
110+
const button = typeFilter.querySelector('button');
111+
expect(button.classList).toContain('bg-primary-700');
112+
});
113+
});
114+
115+
describe('Message Filter', () => {
116+
it('should show message filter button with correct initial state', () => {
117+
const messageFilter = component.getByTestId('message-filter');
118+
const button = messageFilter.querySelector('button');
119+
expect(button.textContent).toContain('Message Filter');
120+
expect(button.classList).toContain('bg-white');
121+
});
122+
123+
it('should show filter icon when messages are filtered', async () => {
124+
const messageFilter = component.getByTestId('message-filter');
125+
await fireEvent.mouseEnter(messageFilter);
126+
127+
const checkbox = messageFilter.querySelector('input[type="checkbox"]');
128+
await fireEvent.click(checkbox);
129+
130+
const filterIcon = messageFilter.querySelector('svg.w-3.h-3.mr-1');
131+
expect(filterIcon).toBeTruthy();
132+
});
133+
134+
it('should update filter state when toggling messages', async () => {
135+
const messageFilter = component.getByTestId('message-filter');
136+
await fireEvent.mouseEnter(messageFilter);
137+
138+
const allToggle = messageFilter.querySelector('input[type="checkbox"]');
139+
await fireEvent.click(allToggle);
140+
141+
const button = messageFilter.querySelector('button');
142+
expect(button.classList).toContain('bg-primary-700');
143+
});
144+
145+
it('should display all message types with counts', async () => {
146+
const messageFilter = component.getByTestId('message-filter');
147+
await fireEvent.mouseEnter(messageFilter);
148+
149+
const messageTypes = ['Authorization', 'QueryParameter', 'PostData', 'Set-Cookie', 'Plain'];
150+
151+
// 必要なデータが読み込まれるのを待つ
152+
await new Promise(resolve => setTimeout(resolve, 100));
153+
154+
const labels = Array.from(messageFilter.querySelectorAll('.rounded input[type="checkbox"]'));
155+
156+
messageTypes.forEach(type => {
157+
const typeExists = labels.some(label =>
158+
label.parentElement.textContent.includes(type) &&
159+
label.parentElement.textContent.match(/\(\d+\/\d+\)/)
160+
);
161+
expect(typeExists).toBeTruthy();
162+
});
163+
});
164+
});
165+
});

0 commit comments

Comments
 (0)