Loading client/src/components/Tags/tagService.js +11 −10 Original line number Diff line number Diff line Loading @@ -57,10 +57,11 @@ export class TagService { if (!tag.valid) { throw new Error("Invalid tag"); } const url = `${getAppRoot()}tag/add_tag_async?item_id=${id}&item_class=${itemClass}&context=${context}&new_tag=${ tag.text }`; const response = await axios.get(url); const url = `${getAppRoot()}tag/add_tag_async`; const config = { params: { item_id: id, item_class: itemClass, context: context, new_tag: tag.text }, }; const response = await axios.get(url, config); if (response.status !== 200) { throw new Error(`Unable to save tag: ${tag}`); } Loading @@ -75,10 +76,9 @@ export class TagService { async delete(rawTag) { const { id, itemClass, context } = this; const tag = createTag(rawTag); const url = `${getAppRoot()}tag/remove_tag_async?item_id=${id}&item_class=${itemClass}&context=${context}&tag_name=${ tag.text }`; const response = await axios.get(url); const url = `${getAppRoot()}tag/remove_tag_async`; const config = { params: { item_id: id, item_class: itemClass, context: context, tag_name: tag.text } }; const response = await axios.get(url, config); if (response.status !== 200) { throw new Error(`Unable to delete tag: ${tag}`); } Loading @@ -92,8 +92,9 @@ export class TagService { */ async autocomplete(searchText) { const { id, itemClass } = this; const url = `${getAppRoot()}tag/tag_autocomplete_data?item_id=${id}&item_class=${itemClass}&q=${searchText}`; const response = await axios.get(url); const url = `${getAppRoot()}tag/tag_autocomplete_data`; const config = { params: { item_id: id, item_class: itemClass, q: searchText } }; const response = await axios.get(url, config); if (response.status !== 200) { throw new Error(`Unable to retrieve autocomplete tags for search string: ${searchText}`); } Loading client/src/components/Tags/tagService.test.js +17 −47 Original line number Diff line number Diff line Loading @@ -2,9 +2,6 @@ import axios from "axios"; import MockAdapter from "axios-mock-adapter"; import { TagService } from "./tagService"; import { createTag } from "./model"; //import { interval } from "rxjs"; //import { take, takeUntil } from "rxjs/operators"; // test response import autocompleteResponse from "./testData/autocompleteResponse.txt"; describe("Tags/tagService.js", () => { Loading @@ -15,6 +12,12 @@ describe("Tags/tagService.js", () => { context: "", debounceInterval: 50, // shorter value than default for unit tests }; const testLabel = "fo0bar123"; const expectedParams = { item_id: 123, item_class: "fooClass", context: "", }; const svc = new TagService(svcParams); beforeEach(() => { Loading @@ -26,43 +29,43 @@ describe("Tags/tagService.js", () => { }); describe("save", () => { const testLabel = "fo0bar123"; const testTag = createTag(testLabel); const { id, itemClass, context } = svcParams; const expectedSaveUrl = `/tag/add_tag_async?item_id=${id}&item_class=${itemClass}&context=${context}&new_tag=${testLabel}`; const expectedSaveUrl = `/tag/add_tag_async`; it("should save a string tag", async () => { const savedTag = await svc.save(testLabel); expect(savedTag.text).toBe(testLabel); expect(axiosMock.history.get[0].url).toBe(expectedSaveUrl); expect(axiosMock.history.get[0].params).toEqual({ ...expectedParams, new_tag: testLabel }); }); it("should save an object tag", async () => { const savedTag = await svc.save(testTag); expect(savedTag.text).toBe(testLabel); expect(axiosMock.history.get[0].url).toBe(expectedSaveUrl); expect(axiosMock.history.get[0].params).toEqual({ ...expectedParams, new_tag: testLabel }); }); // TODO: test error conditions }); describe("delete", () => { const testLabel = "fo0bar123"; const testTag = createTag(testLabel); const { id, itemClass, context } = svcParams; const expectedDeleteUrl = `/tag/remove_tag_async?item_id=${id}&item_class=${itemClass}&context=${context}&tag_name=${testLabel}`; const expectedDeleteUrl = `/tag/remove_tag_async`; it("should delete a text tag", async () => { const result = await svc.delete(testTag); expect(result).toBeTruthy(); expect(axiosMock.history.get[0].url).toBe(expectedDeleteUrl); expect(axiosMock.history.get[0].params).toEqual({ ...expectedParams, tag_name: testLabel }); }); it("should delete an object tag", async () => { const result = await svc.delete(testTag); expect(result).toBeTruthy(); expect(axiosMock.history.get[0].url).toBe(expectedDeleteUrl); expect(axiosMock.history.get[0].params).toEqual({ ...expectedParams, tag_name: testLabel }); }); // TODO: test error conditions Loading @@ -70,8 +73,7 @@ describe("Tags/tagService.js", () => { describe("autocomplete", () => { const searchString = "foo"; const { id, itemClass } = svcParams; const expectedSearchUrl = `/tag/tag_autocomplete_data?item_id=${id}&item_class=${itemClass}&q=${searchString}`; const expectedSearchUrl = `/tag/tag_autocomplete_data`; const checkAutocompleteResult = (result) => { expect(result).toBeTruthy(); Loading @@ -83,46 +85,14 @@ describe("Tags/tagService.js", () => { // straight ajax request, unused in practice, but it's easier to // test this call if we just expose it const { ...searchParams } = expectedParams; delete searchParams.context; it("ajax call should return tag objects", async () => { const result = await svc.autocomplete(searchString); expect(axiosMock.history.get[0].url).toBe(expectedSearchUrl); expect(axiosMock.history.get[0].params).toEqual({ ...searchParams, q: searchString }); checkAutocompleteResult(result); }); /* // hit the search input with multiple entries, only one ajax call // should result because of debouncing it("should debounce autocomplete search inputs", (done) => { // stub ajax request to return the success response if the // searchString is the expected input // take first emission, debouncing should guarantee the first // emission is the last thing we sent, should be searchString const timer$ = interval(100).pipe(take(1)); const option$ = svc.autocompleteOptions.pipe(takeUntil(timer$)); const nextHandler = jest.fn(); option$.subscribe({ next: nextHandler, error: (err) => console.warn("error", err), complete: () => { expect(nextHandler).toHaveBeenCalledTimes(1); expect(axiosMock.history.get.length).toBe(1); done(); }, }); // spam a bunch of key inputs followed by the correct input const spamCount = 2 + Math.floor(Math.random() * 10); for (let i = 0; i < spamCount; i++) { const spamVal = new String(Math.random()); svc.autocompleteSearchText = spamVal; } svc.autocompleteSearchText = searchString; }); */ }); }); Loading
client/src/components/Tags/tagService.js +11 −10 Original line number Diff line number Diff line Loading @@ -57,10 +57,11 @@ export class TagService { if (!tag.valid) { throw new Error("Invalid tag"); } const url = `${getAppRoot()}tag/add_tag_async?item_id=${id}&item_class=${itemClass}&context=${context}&new_tag=${ tag.text }`; const response = await axios.get(url); const url = `${getAppRoot()}tag/add_tag_async`; const config = { params: { item_id: id, item_class: itemClass, context: context, new_tag: tag.text }, }; const response = await axios.get(url, config); if (response.status !== 200) { throw new Error(`Unable to save tag: ${tag}`); } Loading @@ -75,10 +76,9 @@ export class TagService { async delete(rawTag) { const { id, itemClass, context } = this; const tag = createTag(rawTag); const url = `${getAppRoot()}tag/remove_tag_async?item_id=${id}&item_class=${itemClass}&context=${context}&tag_name=${ tag.text }`; const response = await axios.get(url); const url = `${getAppRoot()}tag/remove_tag_async`; const config = { params: { item_id: id, item_class: itemClass, context: context, tag_name: tag.text } }; const response = await axios.get(url, config); if (response.status !== 200) { throw new Error(`Unable to delete tag: ${tag}`); } Loading @@ -92,8 +92,9 @@ export class TagService { */ async autocomplete(searchText) { const { id, itemClass } = this; const url = `${getAppRoot()}tag/tag_autocomplete_data?item_id=${id}&item_class=${itemClass}&q=${searchText}`; const response = await axios.get(url); const url = `${getAppRoot()}tag/tag_autocomplete_data`; const config = { params: { item_id: id, item_class: itemClass, q: searchText } }; const response = await axios.get(url, config); if (response.status !== 200) { throw new Error(`Unable to retrieve autocomplete tags for search string: ${searchText}`); } Loading
client/src/components/Tags/tagService.test.js +17 −47 Original line number Diff line number Diff line Loading @@ -2,9 +2,6 @@ import axios from "axios"; import MockAdapter from "axios-mock-adapter"; import { TagService } from "./tagService"; import { createTag } from "./model"; //import { interval } from "rxjs"; //import { take, takeUntil } from "rxjs/operators"; // test response import autocompleteResponse from "./testData/autocompleteResponse.txt"; describe("Tags/tagService.js", () => { Loading @@ -15,6 +12,12 @@ describe("Tags/tagService.js", () => { context: "", debounceInterval: 50, // shorter value than default for unit tests }; const testLabel = "fo0bar123"; const expectedParams = { item_id: 123, item_class: "fooClass", context: "", }; const svc = new TagService(svcParams); beforeEach(() => { Loading @@ -26,43 +29,43 @@ describe("Tags/tagService.js", () => { }); describe("save", () => { const testLabel = "fo0bar123"; const testTag = createTag(testLabel); const { id, itemClass, context } = svcParams; const expectedSaveUrl = `/tag/add_tag_async?item_id=${id}&item_class=${itemClass}&context=${context}&new_tag=${testLabel}`; const expectedSaveUrl = `/tag/add_tag_async`; it("should save a string tag", async () => { const savedTag = await svc.save(testLabel); expect(savedTag.text).toBe(testLabel); expect(axiosMock.history.get[0].url).toBe(expectedSaveUrl); expect(axiosMock.history.get[0].params).toEqual({ ...expectedParams, new_tag: testLabel }); }); it("should save an object tag", async () => { const savedTag = await svc.save(testTag); expect(savedTag.text).toBe(testLabel); expect(axiosMock.history.get[0].url).toBe(expectedSaveUrl); expect(axiosMock.history.get[0].params).toEqual({ ...expectedParams, new_tag: testLabel }); }); // TODO: test error conditions }); describe("delete", () => { const testLabel = "fo0bar123"; const testTag = createTag(testLabel); const { id, itemClass, context } = svcParams; const expectedDeleteUrl = `/tag/remove_tag_async?item_id=${id}&item_class=${itemClass}&context=${context}&tag_name=${testLabel}`; const expectedDeleteUrl = `/tag/remove_tag_async`; it("should delete a text tag", async () => { const result = await svc.delete(testTag); expect(result).toBeTruthy(); expect(axiosMock.history.get[0].url).toBe(expectedDeleteUrl); expect(axiosMock.history.get[0].params).toEqual({ ...expectedParams, tag_name: testLabel }); }); it("should delete an object tag", async () => { const result = await svc.delete(testTag); expect(result).toBeTruthy(); expect(axiosMock.history.get[0].url).toBe(expectedDeleteUrl); expect(axiosMock.history.get[0].params).toEqual({ ...expectedParams, tag_name: testLabel }); }); // TODO: test error conditions Loading @@ -70,8 +73,7 @@ describe("Tags/tagService.js", () => { describe("autocomplete", () => { const searchString = "foo"; const { id, itemClass } = svcParams; const expectedSearchUrl = `/tag/tag_autocomplete_data?item_id=${id}&item_class=${itemClass}&q=${searchString}`; const expectedSearchUrl = `/tag/tag_autocomplete_data`; const checkAutocompleteResult = (result) => { expect(result).toBeTruthy(); Loading @@ -83,46 +85,14 @@ describe("Tags/tagService.js", () => { // straight ajax request, unused in practice, but it's easier to // test this call if we just expose it const { ...searchParams } = expectedParams; delete searchParams.context; it("ajax call should return tag objects", async () => { const result = await svc.autocomplete(searchString); expect(axiosMock.history.get[0].url).toBe(expectedSearchUrl); expect(axiosMock.history.get[0].params).toEqual({ ...searchParams, q: searchString }); checkAutocompleteResult(result); }); /* // hit the search input with multiple entries, only one ajax call // should result because of debouncing it("should debounce autocomplete search inputs", (done) => { // stub ajax request to return the success response if the // searchString is the expected input // take first emission, debouncing should guarantee the first // emission is the last thing we sent, should be searchString const timer$ = interval(100).pipe(take(1)); const option$ = svc.autocompleteOptions.pipe(takeUntil(timer$)); const nextHandler = jest.fn(); option$.subscribe({ next: nextHandler, error: (err) => console.warn("error", err), complete: () => { expect(nextHandler).toHaveBeenCalledTimes(1); expect(axiosMock.history.get.length).toBe(1); done(); }, }); // spam a bunch of key inputs followed by the correct input const spamCount = 2 + Math.floor(Math.random() * 10); for (let i = 0; i < spamCount; i++) { const spamVal = new String(Math.random()); svc.autocompleteSearchText = spamVal; } svc.autocompleteSearchText = searchString; }); */ }); });