@reactionary/examples-node 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,149 @@
1
+ import 'dotenv/config';
2
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
3
+ import { createClient, PrimaryProvider } from '../utils.js';
4
+
5
+ const testData = {
6
+ searchTerm: 'manhattan',
7
+ };
8
+
9
+ describe.each([PrimaryProvider.ALGOLIA, PrimaryProvider.COMMERCETOOLS])(
10
+ 'Product Search Capability - %s',
11
+ (provider) => {
12
+ let client: ReturnType<typeof createClient>;
13
+
14
+ beforeEach(() => {
15
+ client = createClient(provider);
16
+ });
17
+
18
+ it('should be able to get a result by term', async () => {
19
+ const result = await client.productSearch.queryByTerm({
20
+ search: {
21
+ term: testData.searchTerm,
22
+ facets: [],
23
+ paginationOptions: {
24
+ pageNumber: 1,
25
+ pageSize: 10,
26
+ },
27
+ filters: [],
28
+ },
29
+ });
30
+
31
+ expect(result.items.length).toBeGreaterThan(0);
32
+ });
33
+
34
+ it('should be able to get a result by term, paged', async () => {
35
+ const result = await client.productSearch.queryByTerm({
36
+ search: {
37
+ term: testData.searchTerm,
38
+ facets: [],
39
+ paginationOptions: {
40
+ pageNumber: 1,
41
+ pageSize: 1,
42
+ },
43
+ filters: [],
44
+ },
45
+ });
46
+
47
+ expect(result.items.length).toBeGreaterThan(0);
48
+ expect(result.totalPages).toBeGreaterThan(1);
49
+
50
+ const result2 = await client.productSearch.queryByTerm({
51
+ search: {
52
+ term: testData.searchTerm,
53
+ facets: [],
54
+ paginationOptions: {
55
+ pageNumber: 2,
56
+ pageSize: 1,
57
+ },
58
+ filters: [],
59
+ },
60
+ });
61
+
62
+ expect(result2.items.length).toBeGreaterThan(0);
63
+ expect(result2.totalPages).toBeGreaterThan(2);
64
+ expect(result2.items[0].identifier.key).not.toBe(
65
+ result.items[0].identifier.key
66
+ );
67
+ });
68
+
69
+ it('should be able to change page size', async () => {
70
+ const smallPage = await client.productSearch.queryByTerm({
71
+ search: {
72
+ term: testData.searchTerm,
73
+ paginationOptions: {
74
+ pageNumber: 1,
75
+ pageSize: 2,
76
+ },
77
+ facets: [],
78
+ filters: [],
79
+ },
80
+ });
81
+ const largePage = await client.productSearch.queryByTerm({
82
+ search: {
83
+ term: testData.searchTerm,
84
+
85
+ paginationOptions: {
86
+ pageNumber: 1,
87
+ pageSize: 30,
88
+ },
89
+ facets: [],
90
+ filters: [],
91
+ },
92
+ });
93
+
94
+ expect(smallPage.items.length).toBe(2);
95
+ expect(smallPage.pageSize).toBe(2);
96
+ expect(largePage.items.length).toBe(30);
97
+ expect(largePage.pageSize).toBe(30);
98
+ });
99
+
100
+ it('should be able to apply facets', async () => {
101
+
102
+ const initial = await client.productSearch.queryByTerm({
103
+ search: {
104
+ term: testData.searchTerm,
105
+ paginationOptions: {
106
+ pageNumber: 1,
107
+ pageSize: 2,
108
+ },
109
+ facets: [],
110
+ filters: [],
111
+ },
112
+ });
113
+
114
+ expect(initial.facets.length).toBeGreaterThan(0);
115
+
116
+ const filtered = await client.productSearch.queryByTerm({
117
+ search: {
118
+ term: testData.searchTerm,
119
+ paginationOptions: {
120
+ pageNumber: 1,
121
+ pageSize: 2,
122
+ },
123
+ facets: [initial.facets[0].values[0].identifier],
124
+ filters: [],
125
+ },
126
+ });
127
+
128
+ expect(initial.totalPages).toBeGreaterThanOrEqual(filtered.totalPages);
129
+ });
130
+
131
+ it('should not return facets with no values', async () => {
132
+ const result = await client.productSearch.queryByTerm({
133
+ search: {
134
+ term: testData.searchTerm,
135
+ paginationOptions: {
136
+ pageNumber: 1,
137
+ pageSize: 10,
138
+ },
139
+ facets: [],
140
+ filters: [],
141
+ },
142
+ });
143
+
144
+ for (const facet of result.facets) {
145
+ expect(facet.values.length).toBeGreaterThan(0);
146
+ }
147
+ });
148
+ }
149
+ );
@@ -0,0 +1,108 @@
1
+ import 'dotenv/config';
2
+ import { describe, expect, it, beforeEach } from 'vitest';
3
+ import { createClient, PrimaryProvider } from '../utils.js';
4
+
5
+ const testData = {
6
+ product: {
7
+ id: 'product_10959528',
8
+ name: 'Manhattan 170703 cable accessory Cable kit',
9
+ image: 'https://images.icecat.biz/img/norm/high/10959528-2837.jpg',
10
+ sku: '0766623170703',
11
+ slug: 'manhattan-170703-cable-accessory-cable-kit-10959528',
12
+ },
13
+ productWithMultiVariants: {
14
+ slug: 'hp-gk859aa-mouse-office-bluetooth-laser-1600-dpi-1377612',
15
+ }
16
+ };
17
+
18
+ describe.each([PrimaryProvider.COMMERCETOOLS])('Product Capability - %s', (provider) => {
19
+ let client: ReturnType<typeof createClient>;
20
+
21
+ beforeEach(() => {
22
+ client = createClient(provider);
23
+ });
24
+
25
+ it('should be able to get a product by id', async () => {
26
+ const result = await client.product.getById({ identifier: { key: testData.product.id } });
27
+
28
+ expect(result).toBeTruthy();
29
+ expect(result.identifier.key).toBe(testData.product.id);
30
+ expect(result.meta.placeholder).toBe(false);
31
+ expect(result.name).toBe(testData.product.name);
32
+ expect(result.mainVariant.images[0].sourceUrl).toBe(testData.product.image);
33
+ expect(result.mainVariant.name).toBeTruthy();
34
+ });
35
+
36
+ it('should be able to get a product by slug', async () => {
37
+ const result = await client.product.getBySlug({ slug: testData.product.slug });
38
+
39
+ expect(result).toBeTruthy();
40
+
41
+ if (result) {
42
+ expect(result.meta.placeholder).toBe(false);
43
+ expect(result.identifier.key).toBe(testData.product.id);
44
+ expect(result.name).toBe(testData.product.name);
45
+ expect(result.mainVariant.images[0].sourceUrl).toBe(
46
+ testData.product.image
47
+ );
48
+ }
49
+ });
50
+
51
+ it('should be able to get a multivariant product by slug', async () => {
52
+ const result = await client.product.getBySlug({ slug: testData.productWithMultiVariants.slug });
53
+ expect(result).toBeTruthy();
54
+ if (result) {
55
+ expect(result.meta.placeholder).toBe(false);
56
+ expect(result.identifier.key).toBeTruthy();
57
+ expect(result.slug).toBe(testData.productWithMultiVariants.slug);
58
+ expect(result.mainVariant).toBeDefined();
59
+ expect(result.variants.length).toBeGreaterThan(0);
60
+ expect(result.variants[0].identifier.sku).toBeTruthy();
61
+ expect(result.variants[0].identifier.sku).not.toBe(result.mainVariant.identifier.sku);
62
+ expect(result!.sharedAttributes.length).toBeGreaterThan(1);
63
+ expect(result!.sharedAttributes[1].values.length).toBeGreaterThan(0);
64
+ expect(result!.sharedAttributes[1].values[0].value).toBeTruthy();
65
+ }
66
+ });
67
+
68
+
69
+ it('should be able to get a product by sku', async () => {
70
+ const result = await client.product.getBySKU({
71
+ variant: { sku: testData.product.sku },
72
+ });
73
+
74
+ expect(result).toBeTruthy();
75
+ if (result) {
76
+ expect(result.meta.placeholder).toBe(false);
77
+ expect(result.identifier.key).toBe(testData.product.id);
78
+ expect(result.name).toBe(testData.product.name);
79
+ expect(result.mainVariant.images[0].sourceUrl).toBe(
80
+ testData.product.image
81
+ );
82
+ }
83
+ });
84
+
85
+ it('should contain both product level and variant level attributes', async () => {
86
+ const result = await client.product.getBySKU({
87
+ variant: { sku: testData.product.sku },
88
+ });
89
+
90
+ expect(result).toBeTruthy();
91
+ expect(result.sharedAttributes.length).toBeGreaterThan(1);
92
+ expect(result.sharedAttributes[1].values.length).toBeGreaterThan(0);
93
+ expect(result.sharedAttributes[1].values[0].value).toBeTruthy();
94
+ })
95
+
96
+ it('should return null for unknown slug', async () => {
97
+ const result = await client.product.getBySlug({ slug: 'unknown-slug' });
98
+
99
+ expect(result).toBeNull();
100
+ });
101
+
102
+ it('should return a placeholder product for unknown id', async () => {
103
+ const result = await client.product.getById({ identifier: { key: 'unknown-id' } });
104
+
105
+ expect(result).toBeTruthy();
106
+ expect(result.meta.placeholder).toBe(true);
107
+ });
108
+ });
@@ -0,0 +1,25 @@
1
+ import 'dotenv/config';
2
+ import { describe, expect, it, beforeEach } from 'vitest';
3
+ import { createClient, PrimaryProvider } from '../utils.js';
4
+
5
+ describe.each([PrimaryProvider.COMMERCETOOLS])('Profile Capability - %s', (provider) => {
6
+ let client: ReturnType<typeof createClient>;
7
+
8
+ beforeEach(() => {
9
+ client = createClient(provider);
10
+ });
11
+
12
+ it('should be able to fetch the profile for the current user', async () => {
13
+ const time = new Date().getTime();
14
+
15
+ await client.identity.register({
16
+ username: `martin.rogne+test-${time}@solteq.com`,
17
+ password: 'love2test',
18
+ });
19
+
20
+ const profile = await client.profile.getSelf({});
21
+
22
+ expect(profile).toBeDefined();
23
+ expect(profile.email).toContain('martin.rogne');
24
+ });
25
+ });
@@ -0,0 +1,22 @@
1
+ import 'dotenv/config';
2
+ import { describe, expect, it, beforeEach } from 'vitest';
3
+ import { createClient, PrimaryProvider } from '../utils.js';
4
+
5
+ describe.each([PrimaryProvider.COMMERCETOOLS])('Store Capability - %s', (provider) => {
6
+ let client: ReturnType<typeof createClient>;
7
+
8
+ beforeEach(() => {
9
+ client = createClient(provider);
10
+ });
11
+
12
+ it.skip('should be able to query stores by longitude and latitude', async () => {
13
+ const stores = await client.store.queryByProximity({
14
+ distance: 1000,
15
+ latitude: 15,
16
+ longitude: 15,
17
+ limit: 10,
18
+ });
19
+
20
+ expect(stores.length).toBe(2);
21
+ });
22
+ });
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ // FIXME: Convert from a library to an application. This is required due to it being a package...
2
+ export const FOO = 42;
package/src/utils.ts ADDED
@@ -0,0 +1,102 @@
1
+ import {
2
+ createInitialRequestContext,
3
+ ClientBuilder,
4
+ NoOpCache,
5
+ type PaymentMethod,
6
+ type PaymentMethodIdentifier,
7
+ } from '@reactionary/core';
8
+ import type { CommercetoolsConfiguration } from '@reactionary/provider-commercetools';
9
+ import { withCommercetoolsCapabilities } from '@reactionary/provider-commercetools';
10
+ import { withAlgoliaCapabilities } from '@reactionary/provider-algolia';
11
+ import { withMedusaCapabilities } from '@reactionary/provider-medusa';
12
+
13
+ export function getAlgoliaTestConfiguration() {
14
+ return {
15
+ apiKey: process.env['ALGOLIA_API_KEY'] || '',
16
+ appId: process.env['ALGOLIA_APP_ID'] || '',
17
+ indexName: process.env['ALGOLIA_INDEX'] || '',
18
+ };
19
+ }
20
+
21
+ export function getMedusaTestConfiguration() {
22
+ return {
23
+ publishable_key: process.env['MEDUSA_PUBLISHABLE_KEY'] || '',
24
+ adminApiKey: process.env['MEDUSA_ADMIN_KEY'] || '',
25
+ apiUrl: process.env['MEDUSA_API_URL'] || '',
26
+ defaultCurrency: process.env['MEDUSA_DEFAULT_CURRENCY'] || '',
27
+ allCurrencies: []
28
+ };
29
+ }
30
+
31
+
32
+ export function getCommercetoolsTestConfiguration() {
33
+ return {
34
+ apiUrl: process.env['CTP_API_URL'] || '',
35
+ authUrl: process.env['CTP_AUTH_URL'] || '',
36
+ clientId: process.env['CTP_CLIENT_ID'] || '',
37
+ clientSecret: process.env['CTP_CLIENT_SECRET'] || '',
38
+ projectKey: process.env['CTP_PROJECT_KEY'] || '',
39
+ scopes: (process.env['CTP_SCOPES'] || '')
40
+ .split(',')
41
+ .map((x) => x.trim())
42
+ .filter((x) => x && x.length > 0),
43
+
44
+ paymentMethods: [
45
+ {
46
+ identifier: {
47
+ method: 'stripe',
48
+ name: 'Stripe',
49
+ paymentProcessor: 'stripe'
50
+ },
51
+ isPunchOut: false,
52
+ meta: {
53
+ cache: {
54
+ hit: false,
55
+ key: ''
56
+ },
57
+ placeholder: false
58
+ },
59
+ description: 'Stripe payment gateway',
60
+
61
+ },
62
+ ],
63
+ facetFieldsForSearch: (process.env['CTP_FACET_FIELDS_FOR_SEARCH'] || '').split(',') || ['category.id' ]
64
+ } satisfies CommercetoolsConfiguration;
65
+ }
66
+
67
+ export enum PrimaryProvider {
68
+ ALGOLIA = 'Algolia',
69
+ COMMERCETOOLS = 'Commercetools',
70
+ MEDUSA = 'Medusa',
71
+ }
72
+
73
+ export function createClient(provider: PrimaryProvider) {
74
+ const context = createInitialRequestContext();
75
+ let builder = new ClientBuilder(context)
76
+ .withCache(new NoOpCache())
77
+ .withCapability(
78
+ withCommercetoolsCapabilities(getCommercetoolsTestConfiguration(), {
79
+ cart: true,
80
+ product: true,
81
+ category: true,
82
+ checkout: true,
83
+ identity: true,
84
+ inventory: true,
85
+ order: true,
86
+ price: true,
87
+ productSearch: true,
88
+ store: true,
89
+ profile: true,
90
+ })
91
+ );
92
+
93
+ if (provider === PrimaryProvider.ALGOLIA) {
94
+ builder = builder.withCapability(
95
+ withAlgoliaCapabilities(getAlgoliaTestConfiguration(), {
96
+ productSearch: true,
97
+ })
98
+ );
99
+ }
100
+
101
+ return builder.build();
102
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "module": "NodeNext",
5
+ "moduleResolution": "nodenext",
6
+ "forceConsistentCasingInFileNames": true,
7
+ "strict": true,
8
+ "importHelpers": true,
9
+ "noImplicitOverride": true,
10
+ "noImplicitReturns": true,
11
+ "noFallthroughCasesInSwitch": true,
12
+ "noPropertyAccessFromIndexSignature": true
13
+ },
14
+ "files": [],
15
+ "include": [],
16
+ "references": [
17
+ {
18
+ "path": "./tsconfig.lib.json"
19
+ }
20
+ ]
21
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "../../dist/out-tsc",
5
+ "declaration": true,
6
+ "types": ["node"]
7
+ },
8
+ "include": ["src/**/*.ts"]
9
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "extends": "./tsconfig.lib.json",
3
+ "exclude": []
4
+ }
@@ -0,0 +1,14 @@
1
+ import { defineConfig, defineProject } from 'vitest/config';
2
+ import tsconfigPaths from 'vite-tsconfig-paths';
3
+ import { resolve } from 'path';
4
+ import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
5
+
6
+ export default defineProject({
7
+ plugins: [nxViteTsPaths()],
8
+ test: {
9
+ root: resolve(__dirname),
10
+ globals: true,
11
+ environment: 'node',
12
+ include: ['src/**/*.spec.ts', 'src/**/*.test.ts']
13
+ },
14
+ });