From 77f032da4c690f7224e91736482005d9318b2b16 Mon Sep 17 00:00:00 2001 From: Aman <83913651+Aman-shabbas@users.noreply.github.com> Date: Mon, 13 Oct 2025 19:22:44 +0530 Subject: [PATCH 01/21] [ #85 ] [ Aman ] Adds api key authentication feature --- src/components/RequestAuthForm/index.scss | 12 +++++ src/components/RequestAuthForm/index.tsx | 36 ++++++++++++-- src/components/RequestAuthForm/types.ts | 4 +- tests/components/RequestAuthForm.test.tsx | 56 ++++++++++++++++++---- tests/components/RequestAuthPanel.test.tsx | 13 ----- 5 files changed, 93 insertions(+), 28 deletions(-) diff --git a/src/components/RequestAuthForm/index.scss b/src/components/RequestAuthForm/index.scss index 76871c4..b235e71 100644 --- a/src/components/RequestAuthForm/index.scss +++ b/src/components/RequestAuthForm/index.scss @@ -22,3 +22,15 @@ border-radius: 5px; } } + +.horizontal { + flex-direction: row; + width: 500px; +} + +.keyValueSeparator { + height: 100%; + display: flex; + align-items: center; + justify-content: center; +} \ No newline at end of file diff --git a/src/components/RequestAuthForm/index.tsx b/src/components/RequestAuthForm/index.tsx index 1073224..b0f3aff 100644 --- a/src/components/RequestAuthForm/index.tsx +++ b/src/components/RequestAuthForm/index.tsx @@ -1,10 +1,10 @@ import './index.scss'; -import { AuthenticationFormProps as RequestAuthFormProps, BasicAuthFormProps } from './types'; +import { RequestAuthFormProps, AuthForms } from './types'; import { AuthType } from '../RequestAuthPanel/types'; import { useState } from 'react'; -const BasicAuthForm = (props: BasicAuthFormProps) => { +const BasicAuthForm = (props: AuthForms) => { const [username, setUsername] = useState(''); const [password, setPassword] = useState(''); @@ -35,6 +35,36 @@ const BasicAuthForm = (props: BasicAuthFormProps) => { ); }; +const ApiKeyAuthForm = (props: AuthForms) => { + const [authKey, setAuthKey] = useState(''); + const [authValue, setAuthValue] = useState(''); + + const onAuthKeyChange = (event: React.ChangeEvent) => { + setAuthKey(event.target.value); + props.onCredentialsChange({key: event.target.value, value: authValue}); + } + + const onAuthValueChange = (event: React.ChangeEvent) => { + setAuthValue(event.target.value); + props.onCredentialsChange({key: authKey, value: event.target.value}); + } + return
+ + : + +
; +} + const RequestAuthForm = (props: RequestAuthFormProps) => { if (props.authType == AuthType.NoAuth) { return
Select authentication type!
; @@ -42,7 +72,7 @@ const RequestAuthForm = (props: RequestAuthFormProps) => { if (props.authType == AuthType.BasicAuth) { return ; } - return
Selected authentication type is not supported yet!
; + return }; export default RequestAuthForm; diff --git a/src/components/RequestAuthForm/types.ts b/src/components/RequestAuthForm/types.ts index 3f724c5..ced4b7f 100644 --- a/src/components/RequestAuthForm/types.ts +++ b/src/components/RequestAuthForm/types.ts @@ -1,10 +1,10 @@ import { AuthType } from '../RequestAuthPanel/types'; -export type AuthenticationFormProps = { +export type RequestAuthFormProps = { authType: AuthType; onCredentialsChange: (credentials: object) => void; }; -export type BasicAuthFormProps = { +export type AuthForms = { onCredentialsChange: (credentials: object) => void; }; diff --git a/tests/components/RequestAuthForm.test.tsx b/tests/components/RequestAuthForm.test.tsx index ea04555..bebac6c 100644 --- a/tests/components/RequestAuthForm.test.tsx +++ b/tests/components/RequestAuthForm.test.tsx @@ -1,6 +1,5 @@ -import React from 'react'; -import { render, screen, fireEvent } from '@testing-library/react'; -import { describe, it, expect, vi } from 'vitest'; +import { fireEvent, render, screen } from '@testing-library/react'; +import { describe, expect, it, vi } from 'vitest'; import RequestAuthForm from '../../src/components/RequestAuthForm'; import { AuthType } from '../../src/components/RequestAuthPanel/types'; import '@testing-library/jest-dom'; @@ -18,13 +17,6 @@ describe('RequestAuthForm', () => { expect(screen.getByText('Select authentication type!')).toBeInTheDocument(); }); - it('should render unsupported message for unsupported auth types', () => { - render(); - expect( - screen.getByText('Selected authentication type is not supported yet!') - ).toBeInTheDocument(); - }); - describe('BasicAuth Form', () => { const basicAuthProps = { ...defaultProps, @@ -77,4 +69,48 @@ describe('RequestAuthForm', () => { }); }); }); + + describe('ApiKey Form', () => { + const apiKeyAuthProps = { + ...defaultProps, + authType: AuthType.ApiKey, + }; + + it('should render key and value input area when Api auth is selected', () => { + render(); + + expect(screen.getByPlaceholderText('Key')).toBeInTheDocument(); + expect(screen.getByPlaceholderText('Value')).toBeInTheDocument(); + }); + + it('should call onCredentialsChange when key is changed', () => { + render(); + + const keyInput = screen.getByPlaceholderText('Key'); + fireEvent.change(keyInput, { target: { value: 'apiKey' } }); + + expect(mockOnCredentialsChange).toHaveBeenCalledWith({ key: 'apiKey', value: '' }); + }); + + it('should call onCredentialsChange when value is changed', () => { + render(); + + const valueInput = screen.getByPlaceholderText('Value'); + fireEvent.change(valueInput, { target: { value: 'apiValue' } }); + + expect(mockOnCredentialsChange).toHaveBeenCalledWith({ key: '', value: 'apiValue' }); + }); + + it('should maintain both key and value state when either is changed', () => { + render(); + + const keyInput = screen.getByPlaceholderText('Key'); + const valueInput = screen.getByPlaceholderText('Value'); + + fireEvent.change(keyInput, { target: { value: 'apiKey' } }); + fireEvent.change(valueInput, { target: { value: 'apiValue' } }); + + expect(mockOnCredentialsChange).toHaveBeenCalledWith({ key: 'apiKey', value: 'apiValue' }); + }); + }); }); diff --git a/tests/components/RequestAuthPanel.test.tsx b/tests/components/RequestAuthPanel.test.tsx index ec07205..7354155 100644 --- a/tests/components/RequestAuthPanel.test.tsx +++ b/tests/components/RequestAuthPanel.test.tsx @@ -51,19 +51,6 @@ describe('RequestAuthPanel', () => { }); }); - it('should call onCredentialsChange with ApiKey', () => { - render(); - const dropdown = screen.getByText('NO AUTH'); - fireEvent.click(dropdown); - - const apiKeyOption = screen.getByText('API KEY'); - fireEvent.click(apiKeyOption); - - expect( - screen.getByText('Selected authentication type is not supported yet!') - ).toBeInTheDocument(); - }); - it('should reset to no auth when No Auth is selected', () => { render(); const dropdown = screen.getByText('NO AUTH'); From 301ea16037dd7552d85f0bd79530078ff39e4578 Mon Sep 17 00:00:00 2001 From: Aman <83913651+Aman-shabbas@users.noreply.github.com> Date: Mon, 13 Oct 2025 19:40:40 +0530 Subject: [PATCH 02/21] [ #85 ] [ Aman ] Renames AuthForm to AuthFormProps --- src/components/RequestAuthForm/index.tsx | 6 +++--- src/components/RequestAuthForm/types.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/RequestAuthForm/index.tsx b/src/components/RequestAuthForm/index.tsx index b0f3aff..b12311d 100644 --- a/src/components/RequestAuthForm/index.tsx +++ b/src/components/RequestAuthForm/index.tsx @@ -1,10 +1,10 @@ import './index.scss'; -import { RequestAuthFormProps, AuthForms } from './types'; +import { RequestAuthFormProps, AuthFormsProps } from './types'; import { AuthType } from '../RequestAuthPanel/types'; import { useState } from 'react'; -const BasicAuthForm = (props: AuthForms) => { +const BasicAuthForm = (props: AuthFormsProps) => { const [username, setUsername] = useState(''); const [password, setPassword] = useState(''); @@ -35,7 +35,7 @@ const BasicAuthForm = (props: AuthForms) => { ); }; -const ApiKeyAuthForm = (props: AuthForms) => { +const ApiKeyAuthForm = (props: AuthFormsProps) => { const [authKey, setAuthKey] = useState(''); const [authValue, setAuthValue] = useState(''); diff --git a/src/components/RequestAuthForm/types.ts b/src/components/RequestAuthForm/types.ts index ced4b7f..7043ffb 100644 --- a/src/components/RequestAuthForm/types.ts +++ b/src/components/RequestAuthForm/types.ts @@ -5,6 +5,6 @@ export type RequestAuthFormProps = { onCredentialsChange: (credentials: object) => void; }; -export type AuthForms = { +export type AuthFormsProps = { onCredentialsChange: (credentials: object) => void; }; From c72ae9bdbb9bd1ac49bd3a6c82911619d01f81ac Mon Sep 17 00:00:00 2001 From: hariramprasanth Date: Tue, 14 Oct 2025 20:27:58 +0530 Subject: [PATCH 03/21] Add. PR Build --- .github/workflows/pr.yml | 59 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 .github/workflows/pr.yml diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml new file mode 100644 index 0000000..ee58661 --- /dev/null +++ b/.github/workflows/pr.yml @@ -0,0 +1,59 @@ +name: PR Checks + +on: + pull_request: + types: [opened, synchronize, reopened] + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 22.12.0 + + - name: Install dependencies + run: yarn install --frozen-lockfile + + - name: Run linter + run: yarn run lint + + typecheck: + runs-on: ubuntu-latest + needs: lint + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 22.12.0 + + - name: Install dependencies + run: yarn install --frozen-lockfile + + - name: Run typecheck + run: yarn run typecheck + + test: + runs-on: ubuntu-latest + needs: [lint, typecheck] + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 22.12.0 + + - name: Install dependencies + run: yarn install --frozen-lockfile + + - name: Run tests + run: yarn run test:coverage From 94086294cfcb4f8bd94a45a4acc59a1c3a5dd9ff Mon Sep 17 00:00:00 2001 From: hariramprasanth Date: Tue, 14 Oct 2025 20:34:30 +0530 Subject: [PATCH 04/21] Remove. Circle CI PR Build --- .circleci/config.yml | 34 ---------------------------------- 1 file changed, 34 deletions(-) delete mode 100644 .circleci/config.yml diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 8609d86..0000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,34 +0,0 @@ -version: 2.1 - -jobs: - lint: - docker: - - image: cimg/node:22.12.0 - steps: - - checkout - - run: yarn install --dev - - run: yarn run lint - - typecheck: - docker: - - image: cimg/node:22.12.0 - steps: - - checkout - - run: yarn install --dev - - run: yarn run typecheck - - test: - docker: - - image: cimg/node:22.12.0 - steps: - - checkout - - run: yarn install --dev - - run: yarn run test:coverage - -workflows: - version: 2 - main: - jobs: - - lint - - typecheck - - test From 11ed08464826a382ed88c83547ff7a59a2240934 Mon Sep 17 00:00:00 2001 From: hariramprasanth Date: Wed, 15 Oct 2025 11:12:23 +0530 Subject: [PATCH 05/21] Add. CI Pipeline --- .github/workflows/ci.yml | 60 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..15215c9 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,60 @@ +name: CI + +on: + push: + branches: + - main + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 22.12.0 + + - name: Install dependencies + run: yarn install --frozen-lockfile + + - name: Run linter + run: yarn run lint + + typecheck: + runs-on: ubuntu-latest + needs: lint + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 22.12.0 + + - name: Install dependencies + run: yarn install --frozen-lockfile + + - name: Run typecheck + run: yarn run typecheck + + test: + runs-on: ubuntu-latest + needs: [lint, typecheck] + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 22.12.0 + + - name: Install dependencies + run: yarn install --frozen-lockfile + + - name: Run tests + run: yarn run test:coverage From c2f9695b1ae2417d889bee3b13ff6954297222d7 Mon Sep 17 00:00:00 2001 From: hariramprasanth Date: Wed, 15 Oct 2025 11:23:08 +0530 Subject: [PATCH 06/21] Add. CI Pipeline templates --- .github/workflows/ci.yml | 51 ++----------------- .github/workflows/pr.yml | 51 ++----------------- .github/workflows/templates/lint-job.yml | 22 ++++++++ .github/workflows/templates/test-job.yml | 22 ++++++++ .github/workflows/templates/typecheck-job.yml | 22 ++++++++ 5 files changed, 72 insertions(+), 96 deletions(-) create mode 100644 .github/workflows/templates/lint-job.yml create mode 100644 .github/workflows/templates/test-job.yml create mode 100644 .github/workflows/templates/typecheck-job.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 15215c9..d91f643 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,54 +7,9 @@ on: jobs: lint: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: 22.12.0 - - - name: Install dependencies - run: yarn install --frozen-lockfile - - - name: Run linter - run: yarn run lint - + uses: ./.github/workflows/templates/lint-job.yml typecheck: - runs-on: ubuntu-latest - needs: lint - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: 22.12.0 - - - name: Install dependencies - run: yarn install --frozen-lockfile - - - name: Run typecheck - run: yarn run typecheck - + uses: ./.github/workflows/templates/typecheck-job.yml test: - runs-on: ubuntu-latest needs: [lint, typecheck] - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: 22.12.0 - - - name: Install dependencies - run: yarn install --frozen-lockfile - - - name: Run tests - run: yarn run test:coverage + uses: ./.github/workflows/templates/test-job.yml diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index ee58661..360f803 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -6,54 +6,9 @@ on: jobs: lint: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: 22.12.0 - - - name: Install dependencies - run: yarn install --frozen-lockfile - - - name: Run linter - run: yarn run lint - + uses: ./.github/workflows/templates/lint-job.yml typecheck: - runs-on: ubuntu-latest - needs: lint - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: 22.12.0 - - - name: Install dependencies - run: yarn install --frozen-lockfile - - - name: Run typecheck - run: yarn run typecheck - + uses: ./.github/workflows/templates/typecheck-job.yml test: - runs-on: ubuntu-latest needs: [lint, typecheck] - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: 22.12.0 - - - name: Install dependencies - run: yarn install --frozen-lockfile - - - name: Run tests - run: yarn run test:coverage + uses: ./.github/workflows/templates/test-job.yml \ No newline at end of file diff --git a/.github/workflows/templates/lint-job.yml b/.github/workflows/templates/lint-job.yml new file mode 100644 index 0000000..32abdfa --- /dev/null +++ b/.github/workflows/templates/lint-job.yml @@ -0,0 +1,22 @@ +name: Lint Job + +on: + workflow_call: + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 22.12.0 + + - name: Install dependencies + run: yarn install --frozen-lockfile + + - name: Run linter + run: yarn run lint diff --git a/.github/workflows/templates/test-job.yml b/.github/workflows/templates/test-job.yml new file mode 100644 index 0000000..abb3b89 --- /dev/null +++ b/.github/workflows/templates/test-job.yml @@ -0,0 +1,22 @@ +name: Test Job + +on: + workflow_call: + +jobs: + test: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 22.12.0 + + - name: Install dependencies + run: yarn install --frozen-lockfile + + - name: Run tests + run: yarn run test:coverage diff --git a/.github/workflows/templates/typecheck-job.yml b/.github/workflows/templates/typecheck-job.yml new file mode 100644 index 0000000..e63cba9 --- /dev/null +++ b/.github/workflows/templates/typecheck-job.yml @@ -0,0 +1,22 @@ +name: Typecheck Job + +on: + workflow_call: + +jobs: + typecheck: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 22.12.0 + + - name: Install dependencies + run: yarn install --frozen-lockfile + + - name: Run typecheck + run: yarn run typecheck From bb4f09ee253bef43b86c864172061547df147f49 Mon Sep 17 00:00:00 2001 From: hariramprasanth Date: Wed, 15 Oct 2025 11:38:54 +0530 Subject: [PATCH 07/21] Add. CI Pipeline templates --- .github/workflows/ci.yml | 6 +++--- .github/workflows/{templates => }/lint-job.yml | 0 .github/workflows/pr.yml | 6 +++--- .github/workflows/{templates => }/test-job.yml | 0 .github/workflows/{templates => }/typecheck-job.yml | 0 5 files changed, 6 insertions(+), 6 deletions(-) rename .github/workflows/{templates => }/lint-job.yml (100%) rename .github/workflows/{templates => }/test-job.yml (100%) rename .github/workflows/{templates => }/typecheck-job.yml (100%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d91f643..40f4cd4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,9 +7,9 @@ on: jobs: lint: - uses: ./.github/workflows/templates/lint-job.yml + uses: ./.github/workflows/lint-job.yml typecheck: - uses: ./.github/workflows/templates/typecheck-job.yml + uses: ./.github/workflows/typecheck-job.yml test: needs: [lint, typecheck] - uses: ./.github/workflows/templates/test-job.yml + uses: ./.github/workflows/test-job.yml diff --git a/.github/workflows/templates/lint-job.yml b/.github/workflows/lint-job.yml similarity index 100% rename from .github/workflows/templates/lint-job.yml rename to .github/workflows/lint-job.yml diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 360f803..d72b397 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -6,9 +6,9 @@ on: jobs: lint: - uses: ./.github/workflows/templates/lint-job.yml + uses: ./.github/workflows/lint-job.yml typecheck: - uses: ./.github/workflows/templates/typecheck-job.yml + uses: ./.github/workflows/typecheck-job.yml test: needs: [lint, typecheck] - uses: ./.github/workflows/templates/test-job.yml \ No newline at end of file + uses: ./.github/workflows/test-job.yml diff --git a/.github/workflows/templates/test-job.yml b/.github/workflows/test-job.yml similarity index 100% rename from .github/workflows/templates/test-job.yml rename to .github/workflows/test-job.yml diff --git a/.github/workflows/templates/typecheck-job.yml b/.github/workflows/typecheck-job.yml similarity index 100% rename from .github/workflows/templates/typecheck-job.yml rename to .github/workflows/typecheck-job.yml From 4e6eafec8b566c1c6138218b7ded6f29fa4230b1 Mon Sep 17 00:00:00 2001 From: Dhanoj0810 <142652835+Dhanoj0810@users.noreply.github.com> Date: Tue, 14 Oct 2025 15:23:36 +0530 Subject: [PATCH 08/21] [#117] | Dhanoj | Enhance the styling of request completion time --- src/components/RequestAuthForm/index.scss | 2 +- src/components/RequestAuthForm/index.tsx | 34 +++++++++-------------- src/components/ResponsePanel/index.scss | 15 ++++++---- src/components/ResponsePanel/index.tsx | 12 ++++---- tests/components/ResponsePanel.test.tsx | 16 +++++++---- 5 files changed, 39 insertions(+), 40 deletions(-) diff --git a/src/components/RequestAuthForm/index.scss b/src/components/RequestAuthForm/index.scss index b235e71..3051a49 100644 --- a/src/components/RequestAuthForm/index.scss +++ b/src/components/RequestAuthForm/index.scss @@ -33,4 +33,4 @@ display: flex; align-items: center; justify-content: center; -} \ No newline at end of file +} diff --git a/src/components/RequestAuthForm/index.tsx b/src/components/RequestAuthForm/index.tsx index b12311d..a577ba8 100644 --- a/src/components/RequestAuthForm/index.tsx +++ b/src/components/RequestAuthForm/index.tsx @@ -41,29 +41,21 @@ const ApiKeyAuthForm = (props: AuthFormsProps) => { const onAuthKeyChange = (event: React.ChangeEvent) => { setAuthKey(event.target.value); - props.onCredentialsChange({key: event.target.value, value: authValue}); - } + props.onCredentialsChange({ key: event.target.value, value: authValue }); + }; const onAuthValueChange = (event: React.ChangeEvent) => { setAuthValue(event.target.value); - props.onCredentialsChange({key: authKey, value: event.target.value}); - } - return
- - : - -
; -} + props.onCredentialsChange({ key: authKey, value: event.target.value }); + }; + return ( +
+ + : + +
+ ); +}; const RequestAuthForm = (props: RequestAuthFormProps) => { if (props.authType == AuthType.NoAuth) { @@ -72,7 +64,7 @@ const RequestAuthForm = (props: RequestAuthFormProps) => { if (props.authType == AuthType.BasicAuth) { return ; } - return + return ; }; export default RequestAuthForm; diff --git a/src/components/ResponsePanel/index.scss b/src/components/ResponsePanel/index.scss index 6367fd3..96229f8 100644 --- a/src/components/ResponsePanel/index.scss +++ b/src/components/ResponsePanel/index.scss @@ -54,17 +54,21 @@ width: 70%; } - .status { + .status-container { display: flex; align-items: center; - font-size: 15px; + gap: 12px; white-space: nowrap; } + .status { + font-size: 15px; + } + .response-header-right { display: flex; align-items: center; - gap: 15px; + gap: 12px; padding-right: 2px; white-space: nowrap; } @@ -96,8 +100,7 @@ } .time-taken { - font-size: 14px; - margin-left: 10px; - color: #333; + font-size: 15px; + color: colors.$secondary-dark-color; } } diff --git a/src/components/ResponsePanel/index.tsx b/src/components/ResponsePanel/index.tsx index ed31fa3..ffde412 100644 --- a/src/components/ResponsePanel/index.tsx +++ b/src/components/ResponsePanel/index.tsx @@ -40,14 +40,14 @@ const RawResponseViewer = (props: RawResponseViewerProps) => { const Status = (props: StatusProps) => { const statusClassName = `status ${getStatusClassName(props.statusCode)}`; - let statusMessage; const timeInSeconds = (props.statusTime / 1000).toFixed(2); - if (props.statusCode === 0) statusMessage = `${props.statusText} ${timeInSeconds}s`; - else statusMessage = `${props.statusCode} ${props.statusText} ${timeInSeconds}s`; + const statusMessage = + props.statusCode === 0 ? props.statusText : `${props.statusCode} ${props.statusText}`; return ( -
- {statusMessage} +
+ {statusMessage} + {timeInSeconds}s
); }; @@ -123,7 +123,7 @@ const ResponsePanel = (props: ResponsePanelProps) => { statusText={props.statusText} statusTime={props.timeTaken} /> - + {formatSize(responseSize)} diff --git a/tests/components/ResponsePanel.test.tsx b/tests/components/ResponsePanel.test.tsx index cac9778..44781a8 100644 --- a/tests/components/ResponsePanel.test.tsx +++ b/tests/components/ResponsePanel.test.tsx @@ -235,7 +235,7 @@ describe(`ResponsePanel`, () => { const statusBar = screen.getByTestId('status-bar'); expect(navbar).toBeInTheDocument(); - expect(statusBar).toHaveTextContent(/^200 OK 0.12s$/); + expect(statusBar).toHaveTextContent(/^200 OK0.12s$/); }); it('should status text and time taken when status code is 0', () => { @@ -252,7 +252,7 @@ describe(`ResponsePanel`, () => { const statusBar = screen.getByTestId('status-bar'); expect(navbar).toBeInTheDocument(); - expect(statusBar).toHaveTextContent(/^Network Error 0.46s$/); + expect(statusBar).toHaveTextContent(/^Network Error0.46s$/); }); it('should have "success" in the class name when status code is between 200 and 300', () => { @@ -266,8 +266,9 @@ describe(`ResponsePanel`, () => { }; render(); const statusBar = screen.getByTestId('status-bar'); + const statusElement = statusBar.querySelector('.status'); - expect(statusBar).toHaveClass('success'); + expect(statusElement).toHaveClass('success'); }); it('should have "redirect" in the class name when status code is between 300 and 400', () => { @@ -281,8 +282,9 @@ describe(`ResponsePanel`, () => { }; render(); const statusBar = screen.getByTestId('status-bar'); + const statusElement = statusBar.querySelector('.status'); - expect(statusBar).toHaveClass('redirect'); + expect(statusElement).toHaveClass('redirect'); }); it('should have "client-error" in the class name when status code is between 400 and 500', () => { @@ -296,8 +298,9 @@ describe(`ResponsePanel`, () => { }; render(); const statusBar = screen.getByTestId('status-bar'); + const statusElement = statusBar.querySelector('.status'); - expect(statusBar).toHaveClass('client-error'); + expect(statusElement).toHaveClass('client-error'); }); it('should have "server-error" in the class name when status code is between 500 and 600', () => { @@ -311,8 +314,9 @@ describe(`ResponsePanel`, () => { }; render(); const statusBar = screen.getByTestId('status-bar'); + const statusElement = statusBar.querySelector('.status'); - expect(statusBar).toHaveClass('server-error'); + expect(statusElement).toHaveClass('server-error'); }); }); From d63a854b6ba33a37d2dec5723417dcd7cf2363c4 Mon Sep 17 00:00:00 2001 From: Dhanoj0810 <142652835+Dhanoj0810@users.noreply.github.com> Date: Tue, 21 Oct 2025 11:35:30 +0530 Subject: [PATCH 09/21] [#117] | Dhanoj | separate status and time assertions in ResponsePanel tests --- tests/components/ResponsePanel.test.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/components/ResponsePanel.test.tsx b/tests/components/ResponsePanel.test.tsx index 44781a8..5474deb 100644 --- a/tests/components/ResponsePanel.test.tsx +++ b/tests/components/ResponsePanel.test.tsx @@ -233,9 +233,12 @@ describe(`ResponsePanel`, () => { render(); const navbar = screen.queryByTestId('navbar'); const statusBar = screen.getByTestId('status-bar'); + const statusElement = statusBar.querySelector('.status'); + const timeElement = statusBar.querySelector('.time-taken'); expect(navbar).toBeInTheDocument(); - expect(statusBar).toHaveTextContent(/^200 OK0.12s$/); + expect(statusElement).toHaveTextContent('200 OK'); + expect(timeElement).toHaveTextContent('0.12s'); }); it('should status text and time taken when status code is 0', () => { @@ -250,9 +253,12 @@ describe(`ResponsePanel`, () => { render(); const navbar = screen.queryByTestId('navbar'); const statusBar = screen.getByTestId('status-bar'); + const statusElement = statusBar.querySelector('.status'); + const timeElement = statusBar.querySelector('.time-taken'); expect(navbar).toBeInTheDocument(); - expect(statusBar).toHaveTextContent(/^Network Error0.46s$/); + expect(statusElement).toHaveTextContent('Network Error'); + expect(timeElement).toHaveTextContent('0.46s'); }); it('should have "success" in the class name when status code is between 200 and 300', () => { From 993f9e15eeb9c1f57c16cdad737942036089b6e4 Mon Sep 17 00:00:00 2001 From: Dhanoj0810 <142652835+Dhanoj0810@users.noreply.github.com> Date: Tue, 21 Oct 2025 11:52:50 +0530 Subject: [PATCH 10/21] [#166] | Dhanoj | Remove unnecessary stdout messages during test runs and formated --- tests/components/Editor.test.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/components/Editor.test.tsx b/tests/components/Editor.test.tsx index 705a6b1..5d0681f 100644 --- a/tests/components/Editor.test.tsx +++ b/tests/components/Editor.test.tsx @@ -4,6 +4,8 @@ import { describe, it, expect, vi, beforeEach } from 'vitest'; import Editor from '../../src/components/Editor'; import '@testing-library/jest-dom'; +vi.spyOn(console, 'log').mockImplementation(() => {}); + vi.mock('react-ace', () => ({ default: ({ className, @@ -112,23 +114,21 @@ describe('Editor Component', () => { }); it('should render JsonViewer when readOnly is true and log correct message', () => { - const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {}); + vi.clearAllMocks(); render(); - expect(consoleSpy).toHaveBeenCalledWith('Read only Editor loaded!'); - consoleSpy.mockRestore(); + expect(console.log).toHaveBeenCalledWith('Read only Editor loaded!'); }); }); describe('JsonEditor (Editable mode)', () => { it('should render JsonEditor when readOnly is false and log correct message', () => { - const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {}); + vi.clearAllMocks(); render( ); - expect(consoleSpy).toHaveBeenCalledWith('Editor loaded!'); - consoleSpy.mockRestore(); + expect(console.log).toHaveBeenCalledWith('Editor loaded!'); }); it('should display the initial value in editable mode', () => { From 907e74afe9d7c648ec4c2cf4a7e53bfa21198158 Mon Sep 17 00:00:00 2001 From: Krishna Sreeraj Date: Wed, 22 Oct 2025 17:36:42 +0530 Subject: [PATCH 11/21] [#133] | Krishna | Add Trivy integration to Github Actions PR workflow --- .github/workflows/pr.yml | 3 +++ .github/workflows/trivy-scan-job.yml | 22 ++++++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 .github/workflows/trivy-scan-job.yml diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index d72b397..8c73450 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -12,3 +12,6 @@ jobs: test: needs: [lint, typecheck] uses: ./.github/workflows/test-job.yml + trivy-scan: + needs: [ lint, typecheck, test] + uses: ./.github/workflows/trivy-scan-job.yml diff --git a/.github/workflows/trivy-scan-job.yml b/.github/workflows/trivy-scan-job.yml new file mode 100644 index 0000000..8041234 --- /dev/null +++ b/.github/workflows/trivy-scan-job.yml @@ -0,0 +1,22 @@ +name: Trivy Scan Job + +on: + workflow_call: + +jobs: + trivy-scan: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install Trivy + run: | + curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sudo sh + echo "$(pwd)/bin" >> $GITHUB_PATH + + - name: Run Trivy vulnerability scan + run: trivy fs --exit-code 1 --severity HIGH,CRITICAL . + + - name: Generate Trivy Report + run: trivy fs --format json --output trivy-report.json . From 1f54ed84b98955a8feba48ac0821d75ba8ba9ea0 Mon Sep 17 00:00:00 2001 From: Krishna Sreeraj Date: Wed, 22 Oct 2025 00:40:45 +0530 Subject: [PATCH 12/21] [#133] | Krishna | Add Trivy integration to Github Actions workflow # Conflicts: # .github/workflows/trivy-scan-job.yml --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 40f4cd4..fa01c0b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,3 +13,6 @@ jobs: test: needs: [lint, typecheck] uses: ./.github/workflows/test-job.yml + trivy-scan: + needs: [lint, typecheck, test] + uses: ./.github/workflows/trivy-scan-job.yml From b5cd5dcf9ae357e37b56072ebe9c58b3f5f51194 Mon Sep 17 00:00:00 2001 From: Krishna Sreeraj Date: Wed, 22 Oct 2025 00:41:35 +0530 Subject: [PATCH 13/21] [#133] | Krishna | Add the .trivyignore file to add the vulnerabilities that need to be ignored during trivy scan --- .trivyignore | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 .trivyignore diff --git a/.trivyignore b/.trivyignore new file mode 100644 index 0000000..e69de29 From 5b204ad92ccb9e1e5f4c8250265359103755290e Mon Sep 17 00:00:00 2001 From: Krishna Sreeraj Date: Wed, 22 Oct 2025 00:43:00 +0530 Subject: [PATCH 14/21] [#133] | Krishna | Update the README file to add the details related to trivy vulnerability scanning --- README.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/README.md b/README.md index c9c938e..1633668 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,32 @@ To run the unit tests for the application, run the following command: yarn test ``` +### Trivy Vulnerability Scanning +This project uses [Trivy](https://github.com/aquasecurity/trivy) integrated into the Github Actions pipeline to ensure dependency and container security. + +#### CI Integration + +- The Github Actions pipeline automatically runs a Trivy vulnerability scan on each commit. +- The scan fails the CI build if **High** or **Critical** vulnerabilities are detected. +- Scan results are visible directly in the CI job logs and stored as artifacts (`trivy-report.json`). +- Trivy Ignore (.trivyignore) support: + - Certain vulnerabilities that are acknowledged but not relevant can be ignored using a .trivyignore file at the project root. + - Example: add the CVE IDs you want to ignore, one per line: + - CVE-2023-1234 + - CVE-2023-5678 + +#### Local Testing + +You can also run Trivy locally before committing: + +```bash +# Install Trivy +curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sudo sh + +# Run a local filesystem scan +trivy fs --severity HIGH,CRITICAL . +``` + ### Interested to contribute? Please see our [Contribution Guide](./CONTRIBUTING.md) on how you can get started. Thank you for your valuable time and interest to contribute to this project. From 23730ba160aed4e83fc316dfd100113190219071 Mon Sep 17 00:00:00 2001 From: Krishna Sreeraj Date: Wed, 22 Oct 2025 18:07:04 +0530 Subject: [PATCH 15/21] [#133] | Krishna | Combine the trivy scan and report generation step into a single step for efficiency --- .github/workflows/trivy-scan-job.yml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/trivy-scan-job.yml b/.github/workflows/trivy-scan-job.yml index 8041234..ac33ab4 100644 --- a/.github/workflows/trivy-scan-job.yml +++ b/.github/workflows/trivy-scan-job.yml @@ -15,8 +15,5 @@ jobs: curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sudo sh echo "$(pwd)/bin" >> $GITHUB_PATH - - name: Run Trivy vulnerability scan - run: trivy fs --exit-code 1 --severity HIGH,CRITICAL . - - - name: Generate Trivy Report - run: trivy fs --format json --output trivy-report.json . + - name: Run Trivy vulnerability scan and generate report + run: trivy fs --format json --output trivy-report.json --exit-code 1 --severity HIGH,CRITICAL . From febdb645daa0a3704903a22dc33901e9f17467d8 Mon Sep 17 00:00:00 2001 From: Krishna Sreeraj Date: Wed, 22 Oct 2025 18:31:09 +0530 Subject: [PATCH 16/21] [#133] | Krishna | Remove redundant logs during trivy vulnerability scan --- .github/workflows/trivy-scan-job.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/trivy-scan-job.yml b/.github/workflows/trivy-scan-job.yml index ac33ab4..d20073c 100644 --- a/.github/workflows/trivy-scan-job.yml +++ b/.github/workflows/trivy-scan-job.yml @@ -16,4 +16,4 @@ jobs: echo "$(pwd)/bin" >> $GITHUB_PATH - name: Run Trivy vulnerability scan and generate report - run: trivy fs --format json --output trivy-report.json --exit-code 1 --severity HIGH,CRITICAL . + run: trivy fs --exit-code 1 --severity HIGH,CRITICAL . From 841b7ab66c917f50f961e5a80be156ed1209d085 Mon Sep 17 00:00:00 2001 From: Binni Goel <41873024+droidbg@users.noreply.github.com> Date: Wed, 22 Oct 2025 13:31:20 +0530 Subject: [PATCH 17/21] [#126] | Binni | Add header count badge to response panel --- src/components/Navbar/index.scss | 14 ++++++++++ src/components/Navbar/index.tsx | 3 +++ src/components/Navbar/types.ts | 1 + src/components/ResponsePanel/index.tsx | 1 + tests/components/Navbar.test.tsx | 23 ++++++++++++++++ tests/components/ResponsePanel.test.tsx | 35 +++++++++++++++++++++++++ 6 files changed, 77 insertions(+) diff --git a/src/components/Navbar/index.scss b/src/components/Navbar/index.scss index 7cd70a6..b26694e 100644 --- a/src/components/Navbar/index.scss +++ b/src/components/Navbar/index.scss @@ -31,5 +31,19 @@ color: colors.$secondary-dark-color; border-bottom: colors.$accent-color 0.1rem solid; } + + .badge { + display: inline-block; + border: 1px solid #232323; + border-radius: 8px; + padding: 3px 3px; + font-size: 11px; + font-weight: normal; + margin-left: 6px; + margin-bottom: 15px; + min-width: 18px; + text-align: center; + line-height: 1.2; + } } } diff --git a/src/components/Navbar/index.tsx b/src/components/Navbar/index.tsx index b7d9333..c25d905 100644 --- a/src/components/Navbar/index.tsx +++ b/src/components/Navbar/index.tsx @@ -7,6 +7,9 @@ const Navbar = ({ items }: NavbarProps) => { return (
  • {itemConfig.label} + {itemConfig.badge !== undefined && itemConfig.badge > 0 && ( + {itemConfig.badge} + )}
  • ); }); diff --git a/src/components/Navbar/types.ts b/src/components/Navbar/types.ts index 6866b0c..cfc8b31 100644 --- a/src/components/Navbar/types.ts +++ b/src/components/Navbar/types.ts @@ -5,6 +5,7 @@ type NavBarItemConfig = { label: string; onClick: () => void; isActive: boolean; + badge?: number; }; type NavBarItemsConfig = NavBarItemConfig[]; diff --git a/src/components/ResponsePanel/index.tsx b/src/components/ResponsePanel/index.tsx index ffde412..3563529 100644 --- a/src/components/ResponsePanel/index.tsx +++ b/src/components/ResponsePanel/index.tsx @@ -75,6 +75,7 @@ const ResponsePanel = (props: ResponsePanelProps) => { ...item, isActive: item.name === activeItem, onClick: () => setActiveItem(item.name), + badge: item.name === 'headers' && props.headers.length > 0 ? props.headers.length : undefined, })); const navbarItemComponentMap: NavbarItemComponentMap = { diff --git a/tests/components/Navbar.test.tsx b/tests/components/Navbar.test.tsx index d9580fe..8cdc5fe 100644 --- a/tests/components/Navbar.test.tsx +++ b/tests/components/Navbar.test.tsx @@ -19,4 +19,27 @@ describe('Navbar', () => { expect(navItems[1]).toHaveTextContent('Headers'); expect(navItems[1]).toHaveClass('active'); }); + + it('should display badge when badge prop is provided and greater than 0', () => { + const itemsConfig = [ + { name: 'headers', label: 'Headers', isActive: true, onClick: () => ({}), badge: 3 }, + ]; + render(); + + const badge = screen.getByText('3'); + expect(badge).toBeInTheDocument(); + expect(badge).toHaveClass('badge'); + }); + + it('should not display badge when badge prop is 0 or undefined', () => { + const itemsConfig = [ + { name: 'headers', label: 'Headers', isActive: true, onClick: () => ({}), badge: 0 }, + { name: 'preview', label: 'Preview', isActive: false, onClick: () => ({}) }, + ]; + render(); + + expect(screen.queryByText('0')).not.toBeInTheDocument(); + expect(screen.queryByText('Preview')).toBeInTheDocument(); + expect(screen.queryByText('Preview')).not.toHaveClass('badge'); + }); }); diff --git a/tests/components/ResponsePanel.test.tsx b/tests/components/ResponsePanel.test.tsx index 5474deb..7e4f79c 100644 --- a/tests/components/ResponsePanel.test.tsx +++ b/tests/components/ResponsePanel.test.tsx @@ -108,6 +108,41 @@ describe(`ResponsePanel`, () => { ); }); + describe(`Testing badge functionality`, () => { + it('should pass header count as badge to Headers tab when headers are present', () => { + const props = { + isLoading: false, + isNoRequestTriggered: false, + response: 'Test response', + headers: [ + { key: 'Content-Type', value: 'application/json' }, + { key: 'Cache-Control', value: 'no-cache' }, + ], + }; + + render(); + + expect(mockedComponents.navbar.items).toBeDefined(); + const headersItem = mockedComponents.navbar.items.find(item => item.name === 'headers'); + expect(headersItem?.badge).toBe(2); + }); + + it('should not show badge when no headers are present', () => { + const props = { + isLoading: false, + isNoRequestTriggered: false, + response: 'Test response', + headers: [], + }; + + render(); + + expect(mockedComponents.navbar.items).toBeDefined(); + const headersItem = mockedComponents.navbar.items.find(item => item.name === 'headers'); + expect(headersItem?.badge).toBeUndefined(); + }); + }); + describe(`Testing special Cases`, () => { it(`should handle when response is an blob`, () => { const blob = new Blob(['This is a test blob'], { type: 'text/plain' }); From 31c0e50fc649a81446c2d1efd133621010491539 Mon Sep 17 00:00:00 2001 From: Binni Goel <41873024+droidbg@users.noreply.github.com> Date: Wed, 22 Oct 2025 13:56:38 +0530 Subject: [PATCH 18/21] [#126] | Binni | Use color variables for badge styling --- src/components/Navbar/index.scss | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/components/Navbar/index.scss b/src/components/Navbar/index.scss index b26694e..f57997d 100644 --- a/src/components/Navbar/index.scss +++ b/src/components/Navbar/index.scss @@ -34,14 +34,15 @@ .badge { display: inline-block; - border: 1px solid #232323; - border-radius: 8px; - padding: 3px 3px; - font-size: 11px; + border: 0.1rem solid colors.$divider-color; + color: colors.$accent-contrast-color; + border-radius: 0.5rem; + padding: 0.2rem 0.2rem; + font-size: 0.7rem; font-weight: normal; - margin-left: 6px; - margin-bottom: 15px; - min-width: 18px; + margin-left: 0.4rem; + margin-bottom: 1rem; + min-width: 1.2rem; text-align: center; line-height: 1.2; } From d66b4e509f8622fbafd96245a3364bbb0df7a9e2 Mon Sep 17 00:00:00 2001 From: Binni Goel <41873024+droidbg@users.noreply.github.com> Date: Wed, 22 Oct 2025 23:53:37 +0530 Subject: [PATCH 19/21] [#126] | Binni | Refactor badge logic to use null instead of undefined and simplify condition checks --- src/components/Navbar/index.tsx | 4 +--- src/components/Navbar/types.ts | 2 +- src/components/ResponsePanel/index.tsx | 2 +- tests/components/Navbar.test.tsx | 9 +++------ tests/components/ResponsePanel.test.tsx | 2 +- 5 files changed, 7 insertions(+), 12 deletions(-) diff --git a/src/components/Navbar/index.tsx b/src/components/Navbar/index.tsx index c25d905..bfd8599 100644 --- a/src/components/Navbar/index.tsx +++ b/src/components/Navbar/index.tsx @@ -7,9 +7,7 @@ const Navbar = ({ items }: NavbarProps) => { return (
  • {itemConfig.label} - {itemConfig.badge !== undefined && itemConfig.badge > 0 && ( - {itemConfig.badge} - )} + {itemConfig.badge && {itemConfig.badge}}
  • ); }); diff --git a/src/components/Navbar/types.ts b/src/components/Navbar/types.ts index cfc8b31..c63c916 100644 --- a/src/components/Navbar/types.ts +++ b/src/components/Navbar/types.ts @@ -5,7 +5,7 @@ type NavBarItemConfig = { label: string; onClick: () => void; isActive: boolean; - badge?: number; + badge?: number | null; }; type NavBarItemsConfig = NavBarItemConfig[]; diff --git a/src/components/ResponsePanel/index.tsx b/src/components/ResponsePanel/index.tsx index 3563529..088f359 100644 --- a/src/components/ResponsePanel/index.tsx +++ b/src/components/ResponsePanel/index.tsx @@ -75,7 +75,7 @@ const ResponsePanel = (props: ResponsePanelProps) => { ...item, isActive: item.name === activeItem, onClick: () => setActiveItem(item.name), - badge: item.name === 'headers' && props.headers.length > 0 ? props.headers.length : undefined, + badge: item.name === 'headers' && props.headers.length > 0 ? props.headers.length : null, })); const navbarItemComponentMap: NavbarItemComponentMap = { diff --git a/tests/components/Navbar.test.tsx b/tests/components/Navbar.test.tsx index 8cdc5fe..4cb9e14 100644 --- a/tests/components/Navbar.test.tsx +++ b/tests/components/Navbar.test.tsx @@ -31,15 +31,12 @@ describe('Navbar', () => { expect(badge).toHaveClass('badge'); }); - it('should not display badge when badge prop is 0 or undefined', () => { + it('should not display badge when badge prop is null', () => { const itemsConfig = [ - { name: 'headers', label: 'Headers', isActive: true, onClick: () => ({}), badge: 0 }, - { name: 'preview', label: 'Preview', isActive: false, onClick: () => ({}) }, + { name: 'headers', label: 'Headers', isActive: true, onClick: () => ({}), badge: null }, ]; render(); - expect(screen.queryByText('0')).not.toBeInTheDocument(); - expect(screen.queryByText('Preview')).toBeInTheDocument(); - expect(screen.queryByText('Preview')).not.toHaveClass('badge'); + expect(screen.queryByText('Headers')).not.toHaveClass('badge'); }); }); diff --git a/tests/components/ResponsePanel.test.tsx b/tests/components/ResponsePanel.test.tsx index 7e4f79c..6fc381c 100644 --- a/tests/components/ResponsePanel.test.tsx +++ b/tests/components/ResponsePanel.test.tsx @@ -139,7 +139,7 @@ describe(`ResponsePanel`, () => { expect(mockedComponents.navbar.items).toBeDefined(); const headersItem = mockedComponents.navbar.items.find(item => item.name === 'headers'); - expect(headersItem?.badge).toBeUndefined(); + expect(headersItem?.badge).toBeNull(); }); }); From bdfed7c7db12f9363a332008087b80f59cc2e521 Mon Sep 17 00:00:00 2001 From: Aman <83913651+Aman-shabbas@users.noreply.github.com> Date: Sun, 26 Oct 2025 12:56:25 +0530 Subject: [PATCH 20/21] [ #78 ] [ Aman ] Implements API key authentication --- src/components/AppBody/index.tsx | 4 ++ tests/components/AppBody.test.tsx | 82 +++++++++++++++++++++++++------ 2 files changed, 70 insertions(+), 16 deletions(-) diff --git a/src/components/AppBody/index.tsx b/src/components/AppBody/index.tsx index 6b2b7fd..c4e845b 100644 --- a/src/components/AppBody/index.tsx +++ b/src/components/AppBody/index.tsx @@ -86,6 +86,10 @@ const AppBody = () => { consolidatedHeaders.push({ key: 'Authorization', value: encodedHeaderValue }); } + if (credentials.authType == AuthType.ApiKey && credentials.key && credentials.value) { + consolidatedHeaders.push({ key: credentials.key, value: credentials.value }); + } + const startTime = performance.now(); if (method === Method.Get && url !== '') { diff --git a/tests/components/AppBody.test.tsx b/tests/components/AppBody.test.tsx index 4dea37a..bb719e8 100644 --- a/tests/components/AppBody.test.tsx +++ b/tests/components/AppBody.test.tsx @@ -157,7 +157,9 @@ describe(`AppBody`, () => { expect(mockedComponents.responsePanel?.statusCode).toBe(200); expect(mockedComponents.responsePanel?.statusText).toBe('OK'); - expect(mockedComponents.responsePanel?.response).toBe(JSON.stringify('patch response', null, 2)); + expect(mockedComponents.responsePanel?.response).toBe( + JSON.stringify('patch response', null, 2) + ); }); it('should update ResponsePanel props on DELETE request', async () => { @@ -176,7 +178,9 @@ describe(`AppBody`, () => { expect(mockedComponents.responsePanel?.statusCode).toBe(204); expect(mockedComponents.responsePanel?.statusText).toBe('No Content'); - expect(mockedComponents.responsePanel?.response).toBe(JSON.stringify('delete response', null, 2)); + expect(mockedComponents.responsePanel?.response).toBe( + JSON.stringify('delete response', null, 2) + ); }); it('should pass correct response headers to ResponsePanel when response contains headers', async () => { @@ -203,7 +207,9 @@ describe(`AppBody`, () => { }).then(() => { expect(mockedComponents.responsePanel?.statusCode).toBe(200); expect(mockedComponents.responsePanel?.statusText).toBe('OK'); - expect(mockedComponents.responsePanel?.response).toBe(JSON.stringify('response with headers', null, 2)); + expect(mockedComponents.responsePanel?.response).toBe( + JSON.stringify('response with headers', null, 2) + ); expect(mockedComponents.responsePanel?.headers).toEqual([ { key: 'content-type', value: 'application/json' }, { key: 'x-custom-header', value: 'custom-value' }, @@ -212,10 +218,12 @@ describe(`AppBody`, () => { }); }); - it('should handle missing statusText in successful response', async () => { const { get } = await import('../../src/api/rest.ts'); - (get as ReturnType).mockResolvedValueOnce({ status: 200, data: 'accepted response' }); + (get as ReturnType).mockResolvedValueOnce({ + status: 200, + data: 'accepted response', + }); act(() => { mockedComponents.urlPanel?.onMethodChange('GET'); mockedComponents.urlPanel?.onUrlChange('https://example.com/accepted'); @@ -228,7 +236,9 @@ describe(`AppBody`, () => { }); expect(mockedComponents.responsePanel?.statusCode).toBe(200); expect(mockedComponents.responsePanel?.statusText).toBe('OK'); - expect(mockedComponents.responsePanel?.response).toBe(JSON.stringify('accepted response', null, 2)); + expect(mockedComponents.responsePanel?.response).toBe( + JSON.stringify('accepted response', null, 2) + ); }); it(`should update ResponsePanel props on API error`, async () => { @@ -256,7 +266,9 @@ describe(`AppBody`, () => { expect(mockedComponents.responsePanel?.statusCode).toBe(404); expect(mockedComponents.responsePanel?.statusText).toBe('Not Found'); - expect(mockedComponents.responsePanel?.response).toBe(JSON.stringify({ message: 'Resource not found' }, null, 2)); + expect(mockedComponents.responsePanel?.response).toBe( + JSON.stringify({ message: 'Resource not found' }, null, 2) + ); }); it(`should handle missing statusText, body and statusCode in API error response`, async () => { @@ -351,7 +363,9 @@ describe(`AppBody`, () => { }).then(() => { expect(mockedComponents.responsePanel?.statusCode).toBe(200); expect(mockedComponents.responsePanel?.statusText).toBe('OK'); - expect(mockedComponents.responsePanel?.response).toBe(JSON.stringify('response with headers', null, 2)); + expect(mockedComponents.responsePanel?.response).toBe( + JSON.stringify('response with headers', null, 2) + ); expect(mockedComponents.responsePanel?.headers).toEqual([ { key: 'content-type', value: 'application/json' }, { key: 'x-custom-header', value: 'custom-value' }, @@ -360,7 +374,6 @@ describe(`AppBody`, () => { }); }); - it('should use updated data from RequestPanel callbacks in API calls', async () => { const { post } = await import('../../src/api/rest.ts'); @@ -374,9 +387,7 @@ describe(`AppBody`, () => { { key: 'X-Custom-Header', value: 'header-value' }, ]); - mockedComponents.requestPanel?.onParamsChange([ - { key: 'param1', value: 'value1' }, - ]); + mockedComponents.requestPanel?.onParamsChange([{ key: 'param1', value: 'value1' }]); mockedComponents.requestPanel?.onCredentialsChange({ authType: AuthType.BasicAuth, @@ -401,6 +412,44 @@ describe(`AppBody`, () => { }); }); + it('should use API key data from RequestPanel callbacks in API calls', async () => { + const { post } = await import('../../src/api/rest.ts'); + + act(() => { + mockedComponents.urlPanel?.onMethodChange('POST'); + mockedComponents.urlPanel?.onUrlChange('https://example.com'); + + mockedComponents.requestPanel?.onBodyChange('{"my_key":"my_value"}'); + + mockedComponents.requestPanel?.onHeadersChange([ + { key: 'X-Custom-Header', value: 'header-value' }, + ]); + + mockedComponents.requestPanel?.onParamsChange([{ key: 'param1', value: 'value1' }]); + + mockedComponents.requestPanel?.onCredentialsChange({ + authType: AuthType.ApiKey, + key: 'my_value', + value: 'api_key_value', + }); + }); + + act(() => { + mockedComponents.urlPanel?.onSend(); + }); + + await waitFor(() => { + const expectedUrl = 'https://example.com?param1=value1'; + const expectedBody = { my_key: 'my_value' }; + const expectedHeaders = expect.arrayContaining([ + { key: 'X-Custom-Header', value: 'header-value' }, + { key: 'my_value', value: 'api_key_value' }, + ]); + + expect(post).toHaveBeenCalledWith(expectedUrl, expectedBody, expectedHeaders); + }); + }); + it('should add new header and parameter via RequestPanel callbacks', async () => { const { get } = await import('../../src/api/rest.ts'); @@ -408,7 +457,10 @@ describe(`AppBody`, () => { mockedComponents.urlPanel?.onMethodChange('GET'); mockedComponents.urlPanel?.onUrlChange('https://example.com'); - mockedComponents.requestPanel?.onNewHeaderAddition({ key: 'X-New-Header', value: 'new-value' }); + mockedComponents.requestPanel?.onNewHeaderAddition({ + key: 'X-New-Header', + value: 'new-value', + }); mockedComponents.requestPanel?.onNewParamAddition({ key: 'newParam', value: 'newValue' }); }); @@ -419,9 +471,7 @@ describe(`AppBody`, () => { await waitFor(() => { const expectedUrl = 'https://example.com?newParam=newValue'; - const expectedHeaders = expect.arrayContaining([ - { key: 'X-New-Header', value: 'new-value' }, - ]); + const expectedHeaders = expect.arrayContaining([{ key: 'X-New-Header', value: 'new-value' }]); expect(get).toHaveBeenCalledWith(expectedUrl, expectedHeaders); }); From d8a00ff3ba4e4ec26d70e016e907d5689c626a2f Mon Sep 17 00:00:00 2001 From: Aman <83913651+Aman-shabbas@users.noreply.github.com> Date: Mon, 27 Oct 2025 15:49:18 +0530 Subject: [PATCH 21/21] [ #78 ] [ Aman ] wrapes condition around api auth form render --- src/components/RequestAuthForm/index.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/RequestAuthForm/index.tsx b/src/components/RequestAuthForm/index.tsx index a577ba8..6ed2327 100644 --- a/src/components/RequestAuthForm/index.tsx +++ b/src/components/RequestAuthForm/index.tsx @@ -64,7 +64,9 @@ const RequestAuthForm = (props: RequestAuthFormProps) => { if (props.authType == AuthType.BasicAuth) { return ; } - return ; + if (props.authType == AuthType.ApiKey) { + return ; + } }; export default RequestAuthForm;