Skip to content

Commit e641778

Browse files
feat: add e2e and unit tests (#11)
* move existing tests under unit folder * don't fail unit tests when env is not setup * create helpers folder for test helpers * feat: add e2e test to verify project use case * add jest and tsconfig.jest files * feat: add unit tests using jest for all entities
1 parent a53a225 commit e641778

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

70 files changed

+2044
-3074
lines changed

examples/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ Sensitive information like API keys and authorization tokens are automatically r
4747
When adding new examples:
4848

4949
1. Create a new `.ts` file in this directory
50-
2. Import the SDK: `import { PlaneClient } from '../src'`
50+
2. Import the SDK: `import { PlaneClient } from '../../src'`
5151
3. Include proper error handling
5252
4. Add documentation comments
5353
5. Update this README with a description

examples/bootstrap-project.ts

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// examples/project-bootstrap.ts
2-
import { CreateWorkItemProperty, PlaneClient, WorkItemProperty } from "../src";
2+
import { CreateWorkItemProperty, PlaneClient, WorkItemProperty } from "../../src";
33

44
async function bootstrapProject(workspaceSlug: string) {
55
const client = new PlaneClient({
@@ -30,11 +30,7 @@ async function bootstrapProject(workspaceSlug: string) {
3030
];
3131

3232
for (const type of workItemTypes) {
33-
const workItemType = await client.workItemTypes.create(
34-
workspaceSlug,
35-
project.id,
36-
type
37-
);
33+
const workItemType = await client.workItemTypes.create(workspaceSlug, project.id, type);
3834
const workItemTypeId = workItemType.id;
3935
const customProperties: CreateWorkItemProperty[] = [
4036
{
@@ -47,12 +43,7 @@ async function bootstrapProject(workspaceSlug: string) {
4743
];
4844
// 3. Create custom properties
4945
for (const prop of customProperties) {
50-
await client.workItemProperties.create(
51-
workspaceSlug,
52-
project.id,
53-
workItemTypeId,
54-
prop
55-
);
46+
await client.workItemProperties.create(workspaceSlug, project.id, workItemTypeId, prop);
5647
}
5748
}
5849

jest.config.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
module.exports = {
2+
preset: 'ts-jest',
3+
testEnvironment: 'node',
4+
roots: ['<rootDir>/tests'],
5+
testMatch: [
6+
'**/__tests__/**/*.ts',
7+
'**/?(*.)+(spec|test).ts'
8+
],
9+
transform: {
10+
'^.+\\.ts$': ['ts-jest', {
11+
tsconfig: 'tsconfig.jest.json'
12+
}],
13+
},
14+
collectCoverageFrom: [
15+
'src/**/*.ts',
16+
'!src/**/*.d.ts',
17+
'!src/**/*.spec.ts'
18+
],
19+
coverageDirectory: 'coverage',
20+
coverageReporters: ['text', 'lcov', 'html'],
21+
testTimeout: 30000, // 30 seconds timeout for API tests
22+
verbose: true,
23+
// Allow tests to run in parallel but with some control
24+
maxWorkers: 1, // Run tests sequentially to avoid API rate limits
25+
};

package.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,12 @@
77
"scripts": {
88
"build": "tsc",
99
"dev": "tsc --watch",
10-
"test": "ts-node tests/run-all.test.ts",
10+
"test": "jest",
11+
"test:unit": "jest --testPathPattern=tests/unit",
12+
"test:e2e": "jest --testPathPattern=tests/e2e",
13+
"test:all": "pnpm test:unit && pnpm test:e2e",
14+
"test:watch": "jest --watch",
15+
"test:coverage": "jest --coverage",
1116
"lint": "eslint src/**/*.ts",
1217
"format": "prettier --write src/**/*.ts",
1318
"clean": "rm -rf dist"

src/api/BaseResource.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,8 @@ export abstract class BaseResource {
153153
* Centralized error handling
154154
*/
155155
protected handleError(error: any): never {
156+
console.error("❌ [ERROR]", error);
157+
156158
if (error instanceof HttpError) {
157159
throw error;
158160
}

src/api/Customers/Properties.ts

Lines changed: 12 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ import {
88
ListCustomerPropertiesParams,
99
CreateCustomerPropertyRequest,
1010
UpdateCustomerPropertyRequest,
11+
CustomPropertyValueResponse,
1112
} from "../../models/Customer";
13+
import { PaginatedResponse } from "../../models/common";
1214

1315
/**
1416
* Customer Properties API resource
@@ -27,11 +29,8 @@ export class Properties extends BaseResource {
2729
async listPropertyDefinitions(
2830
workspaceSlug: string,
2931
params?: ListCustomerPropertiesParams
30-
): Promise<CustomerProperty[]> {
31-
return this.get<CustomerProperty[]>(
32-
`/workspaces/${workspaceSlug}/customer-properties/`,
33-
params
34-
);
32+
): Promise<PaginatedResponse<CustomerProperty>> {
33+
return this.get<PaginatedResponse<CustomerProperty>>(`/workspaces/${workspaceSlug}/customer-properties/`, params);
3534
}
3635

3736
/**
@@ -41,22 +40,14 @@ export class Properties extends BaseResource {
4140
workspaceSlug: string,
4241
createProperty: CreateCustomerPropertyRequest
4342
): Promise<CustomerProperty> {
44-
return this.post<CustomerProperty>(
45-
`/workspaces/${workspaceSlug}/customer-properties/`,
46-
createProperty
47-
);
43+
return this.post<CustomerProperty>(`/workspaces/${workspaceSlug}/customer-properties/`, createProperty);
4844
}
4945

5046
/**
5147
* Retrieve customer property
5248
*/
53-
async retrievePropertyDefinition(
54-
workspaceSlug: string,
55-
propertyId: string
56-
): Promise<CustomerProperty> {
57-
return this.get<CustomerProperty>(
58-
`/workspaces/${workspaceSlug}/customer-properties/${propertyId}/`
59-
);
49+
async retrievePropertyDefinition(workspaceSlug: string, propertyId: string): Promise<CustomerProperty> {
50+
return this.get<CustomerProperty>(`/workspaces/${workspaceSlug}/customer-properties/${propertyId}/`);
6051
}
6152

6253
/**
@@ -76,13 +67,8 @@ export class Properties extends BaseResource {
7667
/**
7768
* Delete customer property
7869
*/
79-
async deletePropertyDefinition(
80-
workspaceSlug: string,
81-
propertyId: string
82-
): Promise<void> {
83-
return this.httpDelete(
84-
`/workspaces/${workspaceSlug}/customer-properties/${propertyId}/`
85-
);
70+
async deletePropertyDefinition(workspaceSlug: string, propertyId: string): Promise<void> {
71+
return this.httpDelete(`/workspaces/${workspaceSlug}/customer-properties/${propertyId}/`);
8672
}
8773

8874
// ===== CUSTOMER PROPERTY VALUES API METHODS =====
@@ -94,8 +80,8 @@ export class Properties extends BaseResource {
9480
workspaceSlug: string,
9581
customerId: string,
9682
params?: ListCustomerPropertyValuesParams
97-
): Promise<CustomerPropertyValue[]> {
98-
return this.get<CustomerPropertyValue[]>(
83+
): Promise<CustomPropertyValueResponse> {
84+
return this.get<CustomPropertyValueResponse>(
9985
`/workspaces/${workspaceSlug}/customers/${customerId}/property-values/`,
10086
params
10187
);
@@ -104,11 +90,7 @@ export class Properties extends BaseResource {
10490
/**
10591
* Get single property value
10692
*/
107-
async retrieveValue(
108-
workspaceSlug: string,
109-
customerId: string,
110-
propertyId: string
111-
): Promise<CustomerPropertyValue> {
93+
async retrieveValue(workspaceSlug: string, customerId: string, propertyId: string): Promise<CustomerPropertyValue> {
11294
return this.get<CustomerPropertyValue>(
11395
`/workspaces/${workspaceSlug}/customers/${customerId}/property-values/${propertyId}/`
11496
);

src/api/Customers/Requests.ts

Lines changed: 8 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
UpdateCustomerRequest,
77
ListCustomerRequestsParams,
88
} from "../../models/Customer";
9+
import { PaginatedResponse } from "../../models/common";
910

1011
/**
1112
* Customer Requests API resource
@@ -23,8 +24,8 @@ export class Requests extends BaseResource {
2324
workspaceSlug: string,
2425
customerId: string,
2526
params?: ListCustomerRequestsParams
26-
): Promise<CustomerRequest[]> {
27-
return this.get<CustomerRequest[]>(
27+
): Promise<PaginatedResponse<CustomerRequest>> {
28+
return this.get<PaginatedResponse<CustomerRequest>>(
2829
`/workspaces/${workspaceSlug}/customers/${customerId}/requests/`,
2930
params
3031
);
@@ -38,23 +39,14 @@ export class Requests extends BaseResource {
3839
customerId: string,
3940
createRequest: CreateCustomerRequest
4041
): Promise<CustomerRequest> {
41-
return this.post<CustomerRequest>(
42-
`/workspaces/${workspaceSlug}/customers/${customerId}/requests/`,
43-
createRequest
44-
);
42+
return this.post<CustomerRequest>(`/workspaces/${workspaceSlug}/customers/${customerId}/requests/`, createRequest);
4543
}
4644

4745
/**
4846
* Retrieve customer request
4947
*/
50-
async retrieve(
51-
workspaceSlug: string,
52-
customerId: string,
53-
requestId: string
54-
): Promise<CustomerRequest> {
55-
return this.get<CustomerRequest>(
56-
`/workspaces/${workspaceSlug}/customers/${customerId}/requests/${requestId}/`
57-
);
48+
async retrieve(workspaceSlug: string, customerId: string, requestId: string): Promise<CustomerRequest> {
49+
return this.get<CustomerRequest>(`/workspaces/${workspaceSlug}/customers/${customerId}/requests/${requestId}/`);
5850
}
5951

6052
/**
@@ -75,13 +67,7 @@ export class Requests extends BaseResource {
7567
/**
7668
* Delete customer request
7769
*/
78-
async delete(
79-
workspaceSlug: string,
80-
customerId: string,
81-
requestId: string
82-
): Promise<void> {
83-
return this.httpDelete(
84-
`/workspaces/${workspaceSlug}/customers/${customerId}/requests/${requestId}/`
85-
);
70+
async delete(workspaceSlug: string, customerId: string, requestId: string): Promise<void> {
71+
return this.httpDelete(`/workspaces/${workspaceSlug}/customers/${customerId}/requests/${requestId}/`);
8672
}
8773
}

src/api/Customers/index.ts

Lines changed: 18 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@ import {
55
CreateCustomer,
66
UpdateCustomer,
77
ListCustomersParams,
8+
LinkIssuesToCustomerResponse,
89
} from "../../models/Customer";
910
import { Properties } from "./Properties";
1011
import { Requests } from "./Requests";
12+
import { PaginatedResponse } from "../../models/common";
1113

1214
/**
1315
* Customers API resource
@@ -27,73 +29,45 @@ export class Customers extends BaseResource {
2729
/**
2830
* Create a new customer
2931
*/
30-
async create(
31-
workspaceSlug: string,
32-
createCustomer: CreateCustomer
33-
): Promise<Customer> {
34-
return this.post<Customer>(
35-
`/workspaces/${workspaceSlug}/customers/`,
36-
createCustomer
37-
);
32+
async create(workspaceSlug: string, createCustomer: CreateCustomer): Promise<Customer> {
33+
return this.post<Customer>(`/workspaces/${workspaceSlug}/customers/`, createCustomer);
3834
}
3935

4036
/**
4137
* Retrieve a customer by ID
4238
*/
4339
async retrieve(workspaceSlug: string, customerId: string): Promise<Customer> {
44-
return this.get<Customer>(
45-
`/workspaces/${workspaceSlug}/customers/${customerId}/`
46-
);
40+
return this.get<Customer>(`/workspaces/${workspaceSlug}/customers/${customerId}/`);
4741
}
4842

4943
/**
5044
* Update a customer
5145
*/
52-
async update(
53-
workspaceSlug: string,
54-
customerId: string,
55-
updateCustomer: UpdateCustomer
56-
): Promise<Customer> {
57-
return this.patch<Customer>(
58-
`/workspaces/${workspaceSlug}/customers/${customerId}/`,
59-
updateCustomer
60-
);
46+
async update(workspaceSlug: string, customerId: string, updateCustomer: UpdateCustomer): Promise<Customer> {
47+
return this.patch<Customer>(`/workspaces/${workspaceSlug}/customers/${customerId}/`, updateCustomer);
6148
}
6249

6350
/**
6451
* Delete a customer
6552
*/
6653
async delete(workspaceSlug: string, customerId: string): Promise<void> {
67-
return this.httpDelete(
68-
`/workspaces/${workspaceSlug}/customers/${customerId}/`
69-
);
54+
return this.httpDelete(`/workspaces/${workspaceSlug}/customers/${customerId}/`);
7055
}
7156

7257
/**
7358
* List customers with optional filtering
7459
*/
75-
async list(
76-
workspaceSlug: string,
77-
params?: ListCustomersParams
78-
): Promise<Customer[]> {
79-
return this.get<Customer[]>(
80-
`/workspaces/${workspaceSlug}/customers/`,
81-
params
82-
);
60+
async list(workspaceSlug: string, params?: ListCustomersParams): Promise<PaginatedResponse<Customer>> {
61+
return this.get<PaginatedResponse<Customer>>(`/workspaces/${workspaceSlug}/customers/`, params);
8362
}
8463

8564
// ===== CUSTOMER ISSUES API METHODS =====
8665

8766
/**
8867
* List customer issues
8968
*/
90-
async listCustomerIssues(
91-
workspaceSlug: string,
92-
customerId: string
93-
): Promise<any[]> {
94-
return this.get<any[]>(
95-
`/workspaces/${workspaceSlug}/customers/${customerId}/issues/`
96-
);
69+
async listCustomerIssues(workspaceSlug: string, customerId: string): Promise<any[]> {
70+
return this.get<any[]>(`/workspaces/${workspaceSlug}/customers/${customerId}/issues/`);
9771
}
9872

9973
/**
@@ -103,23 +77,16 @@ export class Customers extends BaseResource {
10377
workspaceSlug: string,
10478
customerId: string,
10579
issueIds: string[]
106-
): Promise<any> {
107-
return this.post<any>(
108-
`/workspaces/${workspaceSlug}/customers/${customerId}/issues/`,
109-
{ issue_ids: issueIds }
110-
);
80+
): Promise<LinkIssuesToCustomerResponse> {
81+
return this.post<LinkIssuesToCustomerResponse>(`/workspaces/${workspaceSlug}/customers/${customerId}/issues/`, {
82+
issue_ids: issueIds,
83+
});
11184
}
11285

11386
/**
11487
* Unlink issue from customer
11588
*/
116-
async unlinkIssueFromCustomer(
117-
workspaceSlug: string,
118-
customerId: string,
119-
issueId: string
120-
): Promise<void> {
121-
return this.httpDelete(
122-
`/workspaces/${workspaceSlug}/customers/${customerId}/issues/${issueId}/`
123-
);
89+
async unlinkIssueFromCustomer(workspaceSlug: string, customerId: string, issueId: string): Promise<void> {
90+
return this.httpDelete(`/workspaces/${workspaceSlug}/customers/${customerId}/issues/${issueId}/`);
12491
}
12592
}

src/api/Links.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { BaseResource } from "./BaseResource";
22
import { Configuration } from "../Configuration";
33
import { Link, CreateLink, UpdateLink, ListLinksParams } from "../models/Link";
4+
import { PaginatedResponse } from "../models/common";
45

56
/**
67
* Links API resource
@@ -54,7 +55,15 @@ export class Links extends BaseResource {
5455
/**
5556
* List links for a work item with optional filtering
5657
*/
57-
async list(workspaceSlug: string, projectId: string, issueId: string, params?: ListLinksParams): Promise<Link[]> {
58-
return this.get<Link[]>(`/workspaces/${workspaceSlug}/projects/${projectId}/work-items/${issueId}/links/`, params);
58+
async list(
59+
workspaceSlug: string,
60+
projectId: string,
61+
issueId: string,
62+
params?: ListLinksParams
63+
): Promise<PaginatedResponse<Link>> {
64+
return this.get<PaginatedResponse<Link>>(
65+
`/workspaces/${workspaceSlug}/projects/${projectId}/work-items/${issueId}/links/`,
66+
params
67+
);
5968
}
6069
}

0 commit comments

Comments
 (0)