9292 <div class =" test-output" ref =" testOutputSection" v-if =" testFiles?.length > 0 || isRunningTests" >
9393 <h2 >
9494 Test Results
95- <div v-if =" isRunningTests" class =" loading-spinner" >
96- <div class =" spinner" ></div >
97- <span >Running tests...</span >
95+ <button v-if =" !isRunningTests && harMessages.length > 0" @click =" downloadHar" class =" download-button" >
96+ Download HTTP Archive (HAR)
97+ </button >
98+ <div v-if =" isRunningTests" class =" loading-spinner" >
99+ <div class =" spinner" ></div >
100+ <span >Running tests...</span >
98101 </div >
99102 </h2 >
100103 <div v-if =" isRunningTests && testFiles.length === 0" class =" running-tests-message" >
110113 r.skipped() ? 'test:skip' : '',
111114 !(r.getLatest().type === 'test:fail' ||
112115 r.skipped() ||
113- r.messages.some(m => m.type === 'test:diagnostic' && m.data.message?.type === 'request ')) ? 'no-details' : ''
116+ r.messages.some(m => m.type === 'test:diagnostic' && m.data.message?._type === 'har ')) ? 'no-details' : ''
114117 ]" >
115118 <component :is =" r.getLatest().data.nesting == 0 ? 'h2' : 'h3'" >
116119 {{ r.getLatest().data.name }} <span >show more ↴</span >
125128
126129 <!-- Diagnostic Messages -->
127130 <div v-else-if =" r.messages.filter(m => ['test:diagnostic'].includes(m.type)).length > 0"
128- v-for =" msg in r?.messages.filter(m => ['test:diagnostic'].includes(m.type) && m.data.message.type == 'request ')"
131+ v-for =" msg in r?.messages.filter(m => ['test:diagnostic'].includes(m.type) && m.data.message._type == 'har ')"
129132 class =" diagnostic-tabs" >
130133 <div class =" tabs" >
131134 <div class =" tab" :class =" { active: getActiveDiagnosticTab(msg.id) === 'request' }"
140143
141144 <div v-if =" getActiveDiagnosticTab(msg.id) === 'request'" class =" tab-content" >
142145 <div class =" http-request" >
143- <div class =" request-line" ><strong >{{ msg.data.message.method }}</strong > {{ msg.data.message.url }}
146+ <div class =" request-line" ><strong >{{ msg.data.message.request. method }}</strong > {{ msg.data.message.request .url }}
144147 HTTP/1.1</div >
145148 <div class =" request-headers" >
146- <div v-for =" (value, key) in msg.data.message.headers" :key =" key" >
147- <strong >{{ key }}:</strong > {{ value }}
149+ <div v-for =" header in msg.data.message.request .headers" :key =" key" >
150+ <strong >{{ header.name }}:</strong > {{ header. value }}
148151 </div >
149152 </div >
150- <pre v-if =" msg.data.message.body " >{{ formatJSON(msg.data.message.body ) }}</pre >
153+ <pre v-if =" msg.data.message.request.postData " >{{ formatJSON(msg.data.message.request.postData.text ) }}</pre >
151154 </div >
152155 </div >
153156
154157 <div v-if =" getActiveDiagnosticTab(msg.id) === 'response'" class =" tab-content" >
155- <div v-for =" response in r?.messages.filter((m, index) => {
156- const msgIndex = r.messages.findIndex(item => item.id === msg.id);
157- return ['test:diagnostic'].includes(m.type) &&
158- m.data.message.type == 'response' &&
159- m.data.message.requestId === msg.data.message.id &&
160- index > msgIndex;
161- }).slice(0, 1)" >
162- <div >HTTP {{ response.data.message.status }} {{ response.data.message.statusText }}</div >
163- <div v-if =" response.data.message.headers" >
164- <span v-for =" (value, key) in response.data.message.headers" :key =" key" >
165- <strong >{{ key }}:</strong > {{ value }}<br />
166- </span >
158+ <div class =" http-response" >
159+ <div class =" status-line" >HTTP {{ msg.data.message.response.status }} {{ msg.data.message.response.statusText }}</div >
160+ <div class =" response-headers" v-if =" msg.data.message.response.headers" >
161+ <div v-for =" header in msg.data.message.response.headers" :key =" header.name" >
162+ <strong >{{ header.name }}:</strong > {{ header.value }}
163+ </div >
167164 </div >
168- <pre v-if =" response .data.message.body " >{{ formatJSON(response .data.message.body ) }}</pre >
165+ <pre v-if =" msg .data.message.response.content " >{{ formatJSON(msg .data.message.response.content.text ) }}</pre >
169166 </div >
170167 </div >
171168 </div >
@@ -187,6 +184,7 @@ import { foldGutter, foldKeymap } from '@codemirror/language';
187184import { keymap } from ' @codemirror/view' ;
188185import defaultConfig from ' ./config.yaml?raw' ;
189186
187+
190188class TestResult {
191189 constructor (file , line , column , name ) {
192190 this .file = file;
@@ -271,6 +269,7 @@ export default {
271269 turnstileWidgetId: null ,
272270
273271 isRunningTests: false , // Track if tests are currently running
272+ harMessages: [], // Add this to store HAR messages
274273 };
275274 },
276275 created () {
@@ -489,6 +488,7 @@ export default {
489488 this .result .clear ();
490489 this .testFiles = [];
491490 this .diagnosticTabs .clear ();
491+ this .harMessages = []; // Clear HAR messages
492492 this .output = ' Starting tests...\n ' ;
493493
494494 const serverUrl = import .meta.env.VITE_SCIM_TEST_SERVER_URL;
@@ -592,9 +592,14 @@ export default {
592592 let existing = testFile .findResult (
593593 json .data .line ,
594594 json .data .column ,
595- json .data .name ?? json .data .message ? .test_name
595+ json .data .name ?? json .data .message ? .test_name ?? json . data . message ? . _test_name
596596 );
597597
598+ // Check if this is a HAR message that we need to store
599+ if (json .data ? .message ? ._type === ' har' ) {
600+ this .harMessages .push (json);
601+ }
602+
598603 if (existing) {
599604 existing .messages .push (json);
600605 } else {
@@ -672,7 +677,7 @@ export default {
672677 },
673678
674679 formatJSON (json ) {
675- return JSON .stringify (json, null , 2 );
680+ return JSON .stringify (JSON . parse ( json) , null , 2 );
676681 },
677682 // Set active tab for a specific diagnostic message
678683 setDiagnosticTab (messageId , tabName ) {
@@ -684,6 +689,31 @@ export default {
684689 // Default to 'response' if not set
685690 return this .diagnosticTabs .get (messageId) || ' response' ;
686691 },
692+
693+ downloadHar () {
694+ const har = {
695+ log: {
696+ version: ' 1.2' ,
697+ creator: {
698+ name: ' verify.scim.dev' ,
699+ version: ' 1.0'
700+ },
701+ pages: [],
702+ entries: this .harMessages .map (msg => msg .data .message )
703+ }
704+ };
705+
706+ // Create blob and download
707+ const blob = new Blob ([JSON .stringify (har, null , 2 )], { type: ' application/json' });
708+ const url = URL .createObjectURL (blob);
709+ const a = document .createElement (' a' );
710+ a .href = url;
711+ a .download = ' scim-verify.har' ;
712+ document .body .appendChild (a);
713+ a .click ();
714+ document .body .removeChild (a);
715+ URL .revokeObjectURL (url);
716+ },
687717 },
688718};
689719< / script>
@@ -1124,7 +1154,7 @@ summary {
11241154
11251155/* Code styling */
11261156pre,
1127- .http - request {
1157+ .http - request, . http - response {
11281158 font- family: ' SF Mono' , SFMono- Regular, ui- monospace, Consolas, Menlo, monospace;
11291159 font- size: 13px ;
11301160}
@@ -1150,7 +1180,7 @@ pre {
11501180 }
11511181}
11521182
1153- .request - headers {
1183+ .response - headers, . request - headers {
11541184 margin- bottom: 12px ;
11551185 color: #64748b ;
11561186
@@ -1443,4 +1473,28 @@ select {
14431473 color: #1a73e8 ;
14441474 text- decoration: underline ! important;
14451475}
1476+
1477+
1478+ .download - button {
1479+ background- color: transparent;
1480+ color: #1a73e8 ;
1481+ padding: 6px 12px ;
1482+ border: 1px solid #1a73e8 ;
1483+ border- radius: 4px ;
1484+ cursor: pointer;
1485+ font- size: 12px ;
1486+ font- weight: 500 ;
1487+ margin: 0 ;
1488+ margin- left: 10px ;
1489+ transition: all 0 .15s ease;
1490+
1491+ & : hover {
1492+ background- color: rgba (26 , 115 , 232 , 0.04 );
1493+ text- decoration: none;
1494+ }
1495+
1496+ & : active {
1497+ background- color: rgba (26 , 115 , 232 , 0.08 );
1498+ }
1499+ }
14461500< / style>
0 commit comments