diff --git a/src/main/java/tech/jhipster/lite/generator/client/vue/core/domain/Vue.java b/src/main/java/tech/jhipster/lite/generator/client/vue/core/domain/Vue.java index fdd1e6bfbf9..7cffc00441f 100644 --- a/src/main/java/tech/jhipster/lite/generator/client/vue/core/domain/Vue.java +++ b/src/main/java/tech/jhipster/lite/generator/client/vue/core/domain/Vue.java @@ -53,7 +53,13 @@ public static List devDependencies() { "typescript", "vite", "vue-jest", - "vue-tsc" + "vue-tsc", + "@types/sinon", + "sinon" ); } + + public static List axiosDependency() { + return List.of("axios"); + } } diff --git a/src/main/java/tech/jhipster/lite/generator/client/vue/core/domain/VueDomainService.java b/src/main/java/tech/jhipster/lite/generator/client/vue/core/domain/VueDomainService.java index 41a6440ad21..a3285758417 100644 --- a/src/main/java/tech/jhipster/lite/generator/client/vue/core/domain/VueDomainService.java +++ b/src/main/java/tech/jhipster/lite/generator/client/vue/core/domain/VueDomainService.java @@ -65,6 +65,7 @@ private void addCommonVue(Project project) { addScripts(project); addJestSonar(project); addViteConfigFiles(project); + addAxios(project); addRootFiles(project); addAppFiles(project); addRouter(project); @@ -86,6 +87,28 @@ private void addPiniaMainConfiguration(Project project) { ); } + public void addAxios(Project project) { + addAxiosDependency(project); + addAxiosFile(project); + addAxiosTestFiles(project); + } + + private void addAxiosTestFiles(Project project) { + String destinationAxiosTest = "src/test/javascript/spec/http"; + String sourceAxiosTest = "test/spec/http"; + projectRepository.template(project, getPath(SOURCE, sourceAxiosTest), "AxiosHttp.spec.ts", destinationAxiosTest); + projectRepository.template(project, getPath(SOURCE, sourceAxiosTest), "AxiosHttpStub.ts", destinationAxiosTest); + projectRepository.template(project, getPath(SOURCE, sourceAxiosTest), "AxiosStub.ts", destinationAxiosTest); + } + + private void addAxiosDependency(Project project) { + Vue.axiosDependency().forEach(dependency -> addDependency(project, dependency)); + } + + private void addAxiosFile(Project project) { + projectRepository.template(project, getPath(SOURCE, "webapp/app/http"), "AxiosHttp.ts", "src/main/webapp/app/http"); + } + public void addDevDependencies(Project project) { Vue.devDependencies().forEach(devDependency -> addDevDependency(project, devDependency)); } diff --git a/src/main/resources/generator/client/vue/test/spec/http/AxiosHttp.spec.ts.mustache b/src/main/resources/generator/client/vue/test/spec/http/AxiosHttp.spec.ts.mustache new file mode 100644 index 00000000000..9d09b3d96c8 --- /dev/null +++ b/src/main/resources/generator/client/vue/test/spec/http/AxiosHttp.spec.ts.mustache @@ -0,0 +1,118 @@ +import { AxiosResponse } from 'axios'; +import { AxiosHttp } from '@/http/AxiosHttp'; +import { dataAxiosResponse, stubAxiosInstance } from './AxiosStub'; + +interface Payload { + payload: string; +} + +const fakePayload = (): Payload => ({ + payload: 'content', +}); + +interface Result { + result: string; +} + +const fakeResult = (): Result => ({ + result: 'content', +}); + +const responseResult = (): AxiosResponse => dataAxiosResponse(fakeResult()); + +const expectForQuerying = (uri: string, result: AxiosResponse) => { + expect(result.data).toEqual(fakeResult()); + expect(uri).toBe('/uri'); +}; + +const expectForSendingAndQuerying = (uri: string, payload: Payload, result: AxiosResponse) => { + expect(payload).toEqual(fakePayload()); + expectForQuerying(uri, result); +}; + +describe('axiosHttp', () => { + describe('GET', () => { + it('should get content', async () => { + const axiosInstance = stubAxiosInstance(); + axiosInstance.get.resolves(responseResult()); + const axiosHttp = new AxiosHttp(axiosInstance); + + const result = await axiosHttp.get('/uri'); + + const [uri] = axiosInstance.get.getCall(0).args; + expectForQuerying(uri, result); + }); + + it('should get content with params', async () => { + const axiosInstance = stubAxiosInstance(); + axiosInstance.get.resolves(responseResult()); + const axiosHttp = new AxiosHttp(axiosInstance); + + await axiosHttp.get('/uri', { params: { beer: 'chips' } }); + + const [, config] = axiosInstance.get.getCall(0).args; + expect(config.params.beer).toBe('chips'); + }); + }); + + describe('PUT', () => { + it('should only get content', async () => { + const axiosInstance = stubAxiosInstance(); + axiosInstance.put.resolves(responseResult()); + const axiosHttp = new AxiosHttp(axiosInstance); + + const result = await axiosHttp.put('/uri'); + + const [uri] = axiosInstance.put.getCall(0).args; + expectForQuerying(uri, result); + }); + + it('should send and get content', async () => { + const axiosInstance = stubAxiosInstance(); + axiosInstance.put.resolves(responseResult()); + const axiosHttp = new AxiosHttp(axiosInstance); + + const result = await axiosHttp.put('/uri', fakePayload()); + + const [uri, payload] = axiosInstance.put.getCall(0).args; + expectForSendingAndQuerying(uri, payload, result); + }); + }); + + describe('POST', () => { + it('should only get content', async () => { + const axiosInstance = stubAxiosInstance(); + axiosInstance.post.resolves(responseResult()); + const axiosHttp = new AxiosHttp(axiosInstance); + + const result = await axiosHttp.post('/uri'); + + const [uri] = axiosInstance.post.getCall(0).args; + expectForQuerying(uri, result); + }); + + it('should send and get content', async () => { + const axiosInstance = stubAxiosInstance(); + axiosInstance.post.resolves(responseResult()); + const axiosHttp = new AxiosHttp(axiosInstance); + + const result = await axiosHttp.post('/uri', fakePayload()); + + const [uri, payload] = axiosInstance.post.getCall(0).args; + expectForSendingAndQuerying(uri, payload, result); + }); + }); + + describe('DELETE', () => { + it('should get content', async () => { + const axiosInstance = stubAxiosInstance(); + axiosInstance.delete.resolves(responseResult()); + const axiosHttp = new AxiosHttp(axiosInstance); + + const result = await axiosHttp.delete('/uri'); + + const [uri] = axiosInstance.delete.getCall(0).args; + expectForQuerying(uri, result); + }); + }); +}); diff --git a/src/main/resources/generator/client/vue/test/spec/http/AxiosHttpStub.ts.mustache b/src/main/resources/generator/client/vue/test/spec/http/AxiosHttpStub.ts.mustache new file mode 100644 index 00000000000..2f378d11b50 --- /dev/null +++ b/src/main/resources/generator/client/vue/test/spec/http/AxiosHttpStub.ts.mustache @@ -0,0 +1,22 @@ +import sinon, { SinonStub } from 'sinon'; +import { AxiosHttp, AxiosHttpResponse } from '@/http/AxiosHttp'; + +export interface AxiosHttpStub extends AxiosHttp { + get: SinonStub; + post: SinonStub; + delete: SinonStub; + put: SinonStub; +} + +export const stubAxiosHttp = (): AxiosHttpStub => + ({ + get: sinon.stub(), + post: sinon.stub(), + delete: sinon.stub(), + put: sinon.stub(), + } as AxiosHttpStub); + +export const dataBackendResponse = (data: T): AxiosHttpResponse => + ({ + data, + } as AxiosHttpResponse); diff --git a/src/main/resources/generator/client/vue/test/spec/http/AxiosStub.ts.mustache b/src/main/resources/generator/client/vue/test/spec/http/AxiosStub.ts.mustache new file mode 100644 index 00000000000..9b9d2cc356c --- /dev/null +++ b/src/main/resources/generator/client/vue/test/spec/http/AxiosStub.ts.mustache @@ -0,0 +1,22 @@ +import { AxiosInstance, AxiosResponse } from 'axios'; +import sinon, { SinonStub } from 'sinon'; + +export interface AxiosStubInstance extends AxiosInstance { + get: SinonStub; + put: SinonStub; + post: SinonStub; + delete: SinonStub; +} + +export const stubAxiosInstance = (): AxiosStubInstance => + ({ + get: sinon.stub(), + put: sinon.stub(), + post: sinon.stub(), + delete: sinon.stub(), + } as AxiosStubInstance); + +export const dataAxiosResponse = (data: T): AxiosResponse => + ({ + data, + } as AxiosResponse); diff --git a/src/main/resources/generator/client/vue/webapp/app/http/AxiosHttp.ts.mustache b/src/main/resources/generator/client/vue/webapp/app/http/AxiosHttp.ts.mustache new file mode 100644 index 00000000000..2e4b569f5b7 --- /dev/null +++ b/src/main/resources/generator/client/vue/webapp/app/http/AxiosHttp.ts.mustache @@ -0,0 +1,23 @@ +import { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'; + +export type AxiosHttpResponse = AxiosResponse; + +export class AxiosHttp { + constructor(private axiosInstance: AxiosInstance) {} + + async get(uri: string, config: AxiosRequestConfig = {}): Promise> { + return this.axiosInstance.get(uri, config); + } + + async put(uri: string, data?: Payload): Promise> { + return this.axiosInstance.put(uri, data); + } + + async post(uri: string, data?: Payload, config?: AxiosRequestConfig): Promise> { + return this.axiosInstance.post(uri, data, config); + } + + async delete(uri: string): Promise> { + return this.axiosInstance.delete(uri); + } +} diff --git a/src/main/resources/generator/dependencies/vue/package.json b/src/main/resources/generator/dependencies/vue/package.json index a3d340381c8..38f49bdfeb7 100644 --- a/src/main/resources/generator/dependencies/vue/package.json +++ b/src/main/resources/generator/dependencies/vue/package.json @@ -4,6 +4,7 @@ "description": "JHipster Lite : used for Vite+Vue3 dependencies", "license": "Apache-2.0", "dependencies": { + "axios": "0.26.1", "pinia": "2.0.14", "pinia-plugin-persist": "1.0.0", "vue": "3.2.33", @@ -13,6 +14,7 @@ "@pinia/testing": "0.0.12", "@rushstack/eslint-patch": "1.1.3", "@types/jest": "27.5.0", + "@types/sinon": "10.0.11", "@typescript-eslint/parser": "5.22.0", "@vitejs/plugin-vue": "2.3.2", "@vue/eslint-config-typescript": "10.0.0", @@ -23,6 +25,7 @@ "jest": "26.6.3", "jest-sonar-reporter": "2.0.0", "jest-transform-stub": "2.0.0", + "sinon": "13.0.2", "ts-jest": "26.5.6", "typescript": "4.6.4", "vite": "2.9.8", diff --git a/src/test/java/tech/jhipster/lite/generator/client/vue/core/application/VueApplicationServiceIT.java b/src/test/java/tech/jhipster/lite/generator/client/vue/core/application/VueApplicationServiceIT.java index 13043e74c98..38ba481b6f8 100644 --- a/src/test/java/tech/jhipster/lite/generator/client/vue/core/application/VueApplicationServiceIT.java +++ b/src/test/java/tech/jhipster/lite/generator/client/vue/core/application/VueApplicationServiceIT.java @@ -27,6 +27,7 @@ void shouldAddVue() { VueAssert.assertAppFiles(project); VueAssert.assertRouterFiles(project); VueAssert.assertAppWithoutCss(project); + VueAssert.assertAxiosFile(project); VueAssert.assertJestSonar(project); } diff --git a/src/test/java/tech/jhipster/lite/generator/client/vue/core/application/VueAssert.java b/src/test/java/tech/jhipster/lite/generator/client/vue/core/application/VueAssert.java index cc644d29717..b502356ce23 100644 --- a/src/test/java/tech/jhipster/lite/generator/client/vue/core/application/VueAssert.java +++ b/src/test/java/tech/jhipster/lite/generator/client/vue/core/application/VueAssert.java @@ -20,6 +20,7 @@ public static void assertDependency(Project project) { Vue.dependencies().forEach(dependency -> assertFileContent(project, PACKAGE_JSON, DQ + dependency + DQ)); Vue.routerDependencies().forEach(dependency -> assertFileContent(project, PACKAGE_JSON, DQ + dependency + DQ)); Vue.devDependencies().forEach(devDependency -> assertFileContent(project, PACKAGE_JSON, DQ + devDependency + DQ)); + Vue.axiosDependency().forEach(dependency -> assertFileContent(project, PACKAGE_JSON, DQ + dependency + DQ)); } public static void assertPiniaDependency(Project project) { @@ -83,4 +84,11 @@ public static void assertJestSonar(Project project) { List.of("\"jestSonar\": {", "\"reportPath\": \"target/test-results/jest\",", "\"reportFile\": \"TESTS-results-sonar.xml\"", "}") ); } + + public static void assertAxiosFile(Project project) { + assertFileExist(project, "src/main/webapp/app/http/AxiosHttp.ts"); + assertFileExist(project, "src/test/javascript/spec/http/AxiosHttp.spec.ts"); + assertFileExist(project, "src/test/javascript/spec/http/AxiosHttpStub.ts"); + assertFileExist(project, "src/test/javascript/spec/http/AxiosStub.ts"); + } } diff --git a/src/test/java/tech/jhipster/lite/generator/client/vue/core/domain/VueDomainServiceTest.java b/src/test/java/tech/jhipster/lite/generator/client/vue/core/domain/VueDomainServiceTest.java index 156fceb15e0..cc3f1b47ea7 100644 --- a/src/test/java/tech/jhipster/lite/generator/client/vue/core/domain/VueDomainServiceTest.java +++ b/src/test/java/tech/jhipster/lite/generator/client/vue/core/domain/VueDomainServiceTest.java @@ -132,7 +132,7 @@ void shouldAddDevDependencies() { vueDomainService.addDevDependencies(project); - verify(npmService, times(17)).addDevDependency(any(Project.class), anyString(), anyString()); + verify(npmService, times(19)).addDevDependency(any(Project.class), anyString(), anyString()); } @Test @@ -221,4 +221,26 @@ void shouldUpdateMainConfigurationWhenAddingPinia() { verify(projectRepository, times(5)).replaceText(eq(project), anyString(), eq("main.ts"), anyString(), anyString()); } + + @Test + void shouldAddAxiosDependency() { + Project project = tmpProjectWithPackageJson(); + final String version = "0.0.0"; + when(npmService.getVersion(anyString(), anyString())).thenReturn(Optional.of(version)); + + vueDomainService.addAxios(project); + + verify(npmService).addDependency(project, "axios", version); + } + + @Test + void shouldAddAxiosFile() { + Project project = tmpProjectWithPackageJson(); + final String version = "0.0.0"; + when(npmService.getVersion(anyString(), anyString())).thenReturn(Optional.of(version)); + + vueDomainService.addAxios(project); + + verify(projectRepository, times(4)).template(any(Project.class), anyString(), anyString(), anyString()); + } } diff --git a/src/test/java/tech/jhipster/lite/generator/client/vue/core/infrastructure/primary/rest/VueResourceIT.java b/src/test/java/tech/jhipster/lite/generator/client/vue/core/infrastructure/primary/rest/VueResourceIT.java index d2afa122bce..23e7c50ee4a 100644 --- a/src/test/java/tech/jhipster/lite/generator/client/vue/core/infrastructure/primary/rest/VueResourceIT.java +++ b/src/test/java/tech/jhipster/lite/generator/client/vue/core/infrastructure/primary/rest/VueResourceIT.java @@ -44,6 +44,7 @@ void shouldAddVue() throws Exception { VueAssert.assertRootFiles(project); VueAssert.assertAppFiles(project); VueAssert.assertAppWithoutCss(project); + VueAssert.assertAxiosFile(project); VueAssert.assertJestSonar(project); }