Skip to content

Commit

Permalink
feat(extensions): Add Bitbucket Pipelines extension
Browse files Browse the repository at this point in the history
  • Loading branch information
sergitorrres authored and alvarolorentedev committed Aug 24, 2024
1 parent 3a3c786 commit 12267a0
Show file tree
Hide file tree
Showing 20 changed files with 469 additions and 23 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,6 @@ typings/

# Electron-Forge
out/

# Intellij IDEA
.idea/
4 changes: 3 additions & 1 deletion src/assets/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,7 @@
"Owner": "Owner",
"Repository": "Repository",
"Workflow ID": "Workflow ID",
"Drop Link Here": "Drop Link Here"
"Drop Link Here": "Drop Link Here",
"Branch": "Branch",
"Workspace": "Workspace"
}
4 changes: 3 additions & 1 deletion src/assets/translations/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,7 @@
"Owner": "Dueño",
"Repository": "Repositorio",
"Workflow ID": "ID del Workflow",
"Drop Link Here": "Soltar Enlace Aqui"
"Drop Link Here": "Soltar Enlace Aqui",
"Branch": "Rama",
"Workspace": "Espacio de Trabajo"
}
58 changes: 58 additions & 0 deletions src/extensions/bitbucket/component/index.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/**
* @vitest-environment jsdom
*/
import React from 'react';
import { render, fireEvent, screen } from '@testing-library/react';
import '@testing-library/jest-dom';
import { Bitbucket } from './';
import { faker } from '@faker-js/faker';
import { expect, describe, it, vi, beforeEach } from 'vitest';

vi.mock('@mui/material/TextField', () => ({
__esModule: true,
default: (props: any) => <input data-testid={`textField-${props.label}`} {...props} />,
}));

describe('Bitbucket', () => {
const expectedObservable = {
owner: faker.lorem.word(),
repo: faker.lorem.word(),
workflowId: faker.lorem.word(),
authToken: faker.lorem.word(),
};
const expectedIndex = faker.number.int();
const updateFieldMock = vi.fn();
const translateMock = (val: string): string => val;
beforeEach(() => {
updateFieldMock.mockClear();
render(
<Bitbucket
observable={expectedObservable}
index={expectedIndex}
updateFieldWithValue={updateFieldMock}
translate={translateMock}
/>
);
});
describe.each([
['Workspace', 'workspace', undefined],
['Repository', 'repo', undefined],
['Branch', 'branch', undefined],
['Authorization Token', 'authToken', 'password'],
])('%s', (label: string, value: string, type: string) => {
it('should have correct textfield attributes', () => {
const textfield = screen.getByTestId(`textField-${label}`);
expect(textfield).toHaveAttribute('label', label);
expect(textfield).toHaveAttribute('variant', 'standard');
if (type) expect(textfield).toHaveAttribute('type', type);
expect(textfield).toHaveAttribute('value', (expectedObservable as any)[value]);
});

it('should call update field on change event', () => {
const expectedValue = faker.lorem.word();
const textfield = screen.getByTestId(`textField-${label}`);
fireEvent.change(textfield, { target: { value: expectedValue } });
expect(updateFieldMock).toBeCalledWith(value, expectedIndex, expectedValue);
});
});
});
38 changes: 38 additions & 0 deletions src/extensions/bitbucket/component/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React from 'react';
import TextField from '@mui/material/TextField';

export const Bitbucket = ({ observable, index, updateFieldWithValue, translate }: any) => (
<>
<TextField
label={translate('Workspace')}
variant="standard"
value={observable.workspace}
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
updateFieldWithValue('workspace', index, event.target.value)
}
/>
<TextField
label={translate('Repository')}
variant="standard"
value={observable.repo}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => updateFieldWithValue('repo', index, event.target.value)}
/>
<TextField
label={translate('Branch')}
variant="standard"
value={observable.branch}
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
updateFieldWithValue('branch', index, event.target.value)
}
/>
<TextField
label={translate('Authorization Token')}
variant="standard"
type="password"
value={observable.authToken}
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
updateFieldWithValue('authToken', index, event.target.value)
}
/>
</>
);
245 changes: 245 additions & 0 deletions src/extensions/bitbucket/observer/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
import { faker } from '@faker-js/faker';
import { Status } from '../../../types/Status';
import { expect, describe, it, vi, beforeEach } from 'vitest';
import { BitbucketConfiguration } from '../../../types/BitbucketConfiguration';
import { Bitbucket } from './';

const fetchtMock = vi.fn();
vi.mock('electron-fetch', () => {
return {
__esModule: true,
default: (...all: any) => fetchtMock(...all),
};
});

describe('Bitbucket', () => {
describe('getState', () => {
let config: BitbucketConfiguration;
let observer: Bitbucket;

let expectedUrl: string;

beforeEach(() => {
fetchtMock.mockClear();
config = {
type: 'bitbucket',
workspace: faker.lorem.word(),
repo: faker.lorem.word(),
branch: faker.lorem.word(),
authToken: faker.lorem.word(),
alias: faker.lorem.word(),
};
expectedUrl = `https://api.bitbucket.org/2.0/repositories/${config.workspace}/${config.repo}/pipelines?target.ref_name=${config.branch}&sort=-created_on`;
observer = new Bitbucket(config);
});

it('shoulds return NA status if request return different value than 200', async () => {
fetchtMock.mockResolvedValue({
json: () => Promise.resolve('kaboom'),
ok: false,
});
const result = await observer.getState();
expect(fetchtMock).toBeCalledWith(expectedUrl, {
method: 'GET',
headers: {
Authorization: `Bearer ${config.authToken}`,
},
});
expect(result).toEqual({
name: config.alias,
status: Status.NA,
link: `https://bitbucket.org/${config.workspace}/${config.repo}/pipelines/results/branch/${config.branch}/page/1`,
});
});

it('shoulds return NA status if request return no pipeline values', async () => {
fetchtMock.mockResolvedValue({
json: () => Promise.resolve({ page: 1, values: [] }),
ok: true,
});
const result = await observer.getState();
expect(fetchtMock).toBeCalledWith(expectedUrl, {
method: 'GET',
headers: {
Authorization: `Bearer ${config.authToken}`,
},
});
expect(result).toEqual({
name: config.alias,
status: Status.NA,
link: `https://bitbucket.org/${config.workspace}/${config.repo}/pipelines/results/branch/${config.branch}/page/1`,
});
});

it('shoulds return SUCCESS status if state is COMPLETED and result SUCCESSFUL', async () => {
const buildNumber = faker.number.int();
fetchtMock.mockResolvedValue({
json: () =>
Promise.resolve({
page: 1,
values: [
{
uuid: faker.lorem.word(),
build_number: buildNumber,
state: {
name: 'COMPLETED',
type: 'pipeline_state_completed',
result: {
name: 'SUCCESSFUL',
type: 'pipeline_state_completed_successful',
},
},
},
],
}),
ok: true,
});
const result = await observer.getState();
expect(fetchtMock).toBeCalledWith(expectedUrl, {
method: 'GET',
headers: {
Authorization: `Bearer ${config.authToken}`,
},
});
expect(result).toEqual({
name: config.alias,
status: Status.SUCCESS,
link: `https://bitbucket.org/${config.workspace}/${config.repo}/pipelines/results/${buildNumber}`,
});
});

it('shoulds return FAILURE status if state is COMPLETED and result FAILED', async () => {
const buildNumber = faker.number.int();
fetchtMock.mockResolvedValue({
json: () =>
Promise.resolve({
page: 1,
values: [
{
uuid: faker.lorem.word(),
build_number: buildNumber,
state: {
name: 'COMPLETED',
type: 'pipeline_state_completed',
result: {
name: 'FAILED',
type: 'pipeline_state_completed_failed',
},
},
},
],
}),
ok: true,
});
const result = await observer.getState();
expect(fetchtMock).toBeCalledWith(expectedUrl, {
method: 'GET',
headers: {
Authorization: `Bearer ${config.authToken}`,
},
});
expect(result).toEqual({
name: config.alias,
status: Status.FAILURE,
link: `https://bitbucket.org/${config.workspace}/${config.repo}/pipelines/results/${buildNumber}`,
});
});

it('shoulds return FAILURE status if state is COMPLETED and result ERROR', async () => {
const buildNumber = faker.number.int();
fetchtMock.mockResolvedValue({
json: () =>
Promise.resolve({
page: 1,
values: [
{
uuid: faker.lorem.word(),
build_number: buildNumber,
state: {
name: 'COMPLETED',
type: 'pipeline_state_completed',
result: {
name: 'ERROR',
type: 'pipeline_state_completed_error',
},
},
},
],
}),
ok: true,
});
const result = await observer.getState();
expect(fetchtMock).toBeCalledWith(expectedUrl, {
method: 'GET',
headers: {
Authorization: `Bearer ${config.authToken}`,
},
});
expect(result).toEqual({
name: config.alias,
status: Status.FAILURE,
link: `https://bitbucket.org/${config.workspace}/${config.repo}/pipelines/results/${buildNumber}`,
});
});

it('shoulds return CHECKING status if state is not COMPLETED', async () => {
const buildNumber = faker.number.int();
fetchtMock.mockResolvedValue({
json: () =>
Promise.resolve({
page: 1,
values: [
{
uuid: faker.lorem.word(),
build_number: buildNumber,
state: {
name: 'IN_PROGRESS',
type: 'pipeline_state_in_progress_running',
stage: {
name: 'RUNNING',
type: 'pipeline_state_in_progress_running',
},
},
},
],
}),
ok: true,
});
const result = await observer.getState();
expect(fetchtMock).toBeCalledWith(expectedUrl, {
method: 'GET',
headers: {
Authorization: `Bearer ${config.authToken}`,
},
});
expect(result).toEqual({
name: config.alias,
status: Status.CHECKING,
link: `https://bitbucket.org/${config.workspace}/${config.repo}/pipelines/results/${buildNumber}`,
});
});

it('shoulds return repo/branch if no alias', async () => {
fetchtMock.mockResolvedValue({
json: () =>
Promise.resolve({
page: 1,
values: [],
}),
ok: true,
});

config = {
type: 'bitbucket',
workspace: faker.lorem.word(),
repo: faker.lorem.word(),
branch: faker.lorem.word(),
authToken: faker.lorem.word(),
alias: undefined,
};
observer = new Bitbucket(config);
const result = await observer.getState();
expect(result.name).toEqual(`Bitbucket: ${config.repo}/${config.branch}`);
});
});
});
Loading

0 comments on commit 12267a0

Please sign in to comment.