diff --git a/src/core/plugins/spec/reducers.js b/src/core/plugins/spec/reducers.js index 8a5644ded40..db25ba18bf3 100644 --- a/src/core/plugins/spec/reducers.js +++ b/src/core/plugins/spec/reducers.js @@ -37,7 +37,16 @@ export default { }, [UPDATE_URL]: (state, action) => { - return state.set("url", action.payload+"") + const nextUrl = action.payload + "" + if (state.get("url") === nextUrl) { + return state.set("url", nextUrl) + } + // Loading a new spec URL: clear any scheme state retained from the + // previously loaded spec so it cannot leak into the new spec when the + // new spec does not declare its own `schemes`. + return state + .set("url", nextUrl) + .delete("scheme") }, [UPDATE_JSON]: (state, action) => { diff --git a/test/e2e-cypress/e2e/bugs/5225.cy.js b/test/e2e-cypress/e2e/bugs/5225.cy.js new file mode 100644 index 00000000000..3912003f7b7 --- /dev/null +++ b/test/e2e-cypress/e2e/bugs/5225.cy.js @@ -0,0 +1,37 @@ +/** + * @prettier + */ +// http://github.com/swagger-api/swagger-ui/issues/5225 + +describe("#5225: loading new URL does not reset schemes value if schemes unset", () => { + it("should not retain the previous spec's scheme when the new spec omits schemes", () => { + cy.visit("?url=/documents/bugs/5225-with-schemes.yaml") + // Wait for the first spec (which declares `schemes: [https]`) to render + .get("#operations-default-getPing", { timeout: 10000 }) + .should("exist") + + // Switch to a spec that does NOT declare any `schemes`. Use the spec + // actions directly (mirrors how the TopBar swaps specs) so the test does + // not depend on TopBar markup details. + cy.window().then((win) => { + win.ui.specActions.updateUrl("/documents/bugs/5225-without-schemes.yaml") + win.ui.specActions.download("/documents/bugs/5225-without-schemes.yaml") + }) + + // Wait for the second spec to load + cy.get("#operations-default-getPong", { timeout: 10000 }) + .should("exist") + .click() + .get(".try-out__btn") + .click() + .get(".btn.execute") + .click() + + // The generated curl URL should be derived from the page URL scheme + // (http when served from http://localhost), NOT the leaked `https` + // default from the previously loaded spec. + cy.get(".curl-command", { timeout: 10000 }) + .should("contain", "http://example.com/pong") + .and("not.contain", "https://example.com/pong") + }) +}) diff --git a/test/e2e-cypress/static/documents/bugs/5225-with-schemes.yaml b/test/e2e-cypress/static/documents/bugs/5225-with-schemes.yaml new file mode 100644 index 00000000000..72fb05bf77b --- /dev/null +++ b/test/e2e-cypress/static/documents/bugs/5225-with-schemes.yaml @@ -0,0 +1,14 @@ +swagger: "2.0" +info: + title: with-schemes + version: 1.0.0 +host: example.com +schemes: + - https +paths: + /ping: + get: + operationId: getPing + responses: + "200": + description: OK diff --git a/test/e2e-cypress/static/documents/bugs/5225-without-schemes.yaml b/test/e2e-cypress/static/documents/bugs/5225-without-schemes.yaml new file mode 100644 index 00000000000..0de11ef3fca --- /dev/null +++ b/test/e2e-cypress/static/documents/bugs/5225-without-schemes.yaml @@ -0,0 +1,12 @@ +swagger: "2.0" +info: + title: without-schemes + version: 1.0.0 +host: example.com +paths: + /pong: + get: + operationId: getPong + responses: + "200": + description: OK diff --git a/test/unit/core/plugins/spec/reducer.js b/test/unit/core/plugins/spec/reducer.js index 2f274d30159..fddbc4d846e 100644 --- a/test/unit/core/plugins/spec/reducer.js +++ b/test/unit/core/plugins/spec/reducer.js @@ -199,6 +199,55 @@ describe("spec plugin - reducer", function(){ expect(List.isList(response)).toEqual(true) }) }) + describe("spec_update_url", function() { + it("should set the url payload as a string", () => { + const updateUrl = reducer["spec_update_url"] + + const state = fromJS({}) + const result = updateUrl(state, { payload: "https://example.com/spec.json" }) + + expect(result.get("url")).toEqual("https://example.com/spec.json") + }) + + it("should clear retained scheme state when loading a different url", () => { + const updateUrl = reducer["spec_update_url"] + + const state = fromJS({ + url: "https://old.example.com/spec.json", + scheme: { + _defaultScheme: "https", + "/pet": { + post: "https" + } + } + }) + + const result = updateUrl(state, { + payload: "http://new.example.com/spec.json" + }) + + expect(result.get("url")).toEqual("http://new.example.com/spec.json") + expect(result.has("scheme")).toEqual(false) + }) + + it("should keep scheme state when the url payload does not change", () => { + const updateUrl = reducer["spec_update_url"] + + const state = fromJS({ + url: "https://example.com/spec.json", + scheme: { + _defaultScheme: "https" + } + }) + + const result = updateUrl(state, { + payload: "https://example.com/spec.json" + }) + + expect(result.get("url")).toEqual("https://example.com/spec.json") + expect(result.getIn(["scheme", "_defaultScheme"])).toEqual("https") + }) + }) describe("SPEC_UPDATE_EMPTY_PARAM_INCLUSION", function() { it("should store parameter values by {in}.{name}", () => { const updateParam = reducer["spec_update_empty_param_inclusion"]