Skip to content

Commit

Permalink
Enable aliases names to contain the dot character (#4248)
Browse files Browse the repository at this point in the history
* Cherry picked the changes from PR 3929

* Support for dot in alias for wait and route

* Remove unused regex

* Remove console log messages

* Ensure alias without dot is handled

* Refactor code to use the same conditions

Updated code to use the same conditions to determine whether to extract the potential index present or not.

* Cherry picked the changes from PR 3929

* Support for dot in alias for wait and route

* Remove unused regex

* Remove console log messages

* Ensure alias without dot is handled

* Refactor code to use the same conditions

Updated code to use the same conditions to determine whether to extract the potential index present or not.
  • Loading branch information
clarmso authored and jennifer-shehane committed May 21, 2019
1 parent 3eacfeb commit 031ae70
Show file tree
Hide file tree
Showing 8 changed files with 230 additions and 98 deletions.
19 changes: 15 additions & 4 deletions packages/driver/src/cy/commands/querying.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,16 @@ module.exports = (Commands, Cypress, cy, state, config) ->

options._log.set(obj)

## we always want to strip everything after the first '.'
## since we support alias propertys like '1' or 'all'
if aliasObj = cy.getAlias(selector.split(".")[0])
## We want to strip everything after the last '.'
## only when it is potentially a number or 'all'
if _.indexOf(selector, ".") == -1 ||
selector.slice(1) in _.keys(cy.state("aliases"))
toSelect = selector
else
allParts = _.split(selector, '.')
toSelect = _.join(_.dropRight(allParts, 1), '.')

if aliasObj = cy.getAlias(toSelect)
{subject, alias, command} = aliasObj

return do resolveAlias = ->
Expand Down Expand Up @@ -176,7 +183,11 @@ module.exports = (Commands, Cypress, cy, state, config) ->

## if this is a route command
when command.get("name") is "route"
alias = _.compact([alias, selector.split(".")[1]]).join(".")
if !(_.indexOf(selector, ".") == -1 ||
selector.slice(1) in _.keys(cy.state("aliases")))
allParts = _.split(selector, ".")
index = _.last(allParts)
alias = _.join([alias, index], ".")
requests = cy.getRequestsByAlias(alias) ? null
log(requests, "route")
return requests
Expand Down
12 changes: 9 additions & 3 deletions packages/driver/src/cy/commands/waiting.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,15 @@ module.exports = (Commands, Cypress, cy, state, config) ->
, options

waitForXhr = (str, options) ->
## we always want to strip everything after the first '.'
## we always want to strip everything after the last '.'
## since we support alias property 'request'
[str, str2] = str.split(".")
if _.indexOf(str, ".") == -1 ||
str.slice(1) in _.keys(cy.state("aliases"))
[str, str2] = [str, null]
else
# potentially request, response or index
allParts = _.split(str, '.')
[str, str2] = [_.join(_.dropRight(allParts, 1), '.'), _.last(allParts)]

if not aliasObj = cy.getAlias(str, "wait", log)
cy.aliasNotFoundFor(str, "wait", log)
Expand All @@ -79,7 +85,7 @@ module.exports = (Commands, Cypress, cy, state, config) ->
{alias, command} = aliasObj

str = _.compact([alias, str2]).join(".")

type = cy.getXhrTypeByAlias(str)

[ index, num ] = getNumRequests(state, alias)
Expand Down
13 changes: 11 additions & 2 deletions packages/driver/src/cy/xhrs.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,11 @@ xhrNotWaitedOnByIndex = (state, alias, index, prop) ->
create = (state) ->
return {
getIndexedXhrByAlias: (alias, index) ->
[str, prop] = alias.split(".")
if _.indexOf(alias, ".") == -1
[str, prop] = [alias, null]
else
allParts = _.split(alias, '.')
[str, prop] = [_.join(_.dropRight(allParts, 1), '.'), _.last(allParts)]

if prop
if prop is "request"
Expand All @@ -38,7 +42,12 @@ create = (state) ->
xhrNotWaitedOnByIndex(state, str, index, "responses")

getRequestsByAlias: (alias) ->
[alias, prop] = alias.split(".")
if _.indexOf(alias, ".") == -1 || alias in _.keys(cy.state("aliases"))
[alias, prop] = [alias, null]
else
# potentially valid prop
allParts = _.split(alias, '.')
[alias, prop] = [_.join(_.dropRight(allParts, 1), '.'), _.last(allParts)]

if prop and not validAliasApiRe.test(prop)
$utils.throwErrByPath "get.alias_invalid", {
Expand Down
138 changes: 97 additions & 41 deletions packages/driver/test/cypress/integration/commands/agents_spec.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -201,59 +201,115 @@ describe "src/cy/commands/agents", ->
expect(@consoleProps[" 1.2 matching arguments"]).to.eql(["foo", "baz"])

describe ".as", ->
beforeEach ->
@logs = []
cy.on "log:added", (attrs, log) =>
@logs.push(log)
context "without dots", ->
beforeEach ->
@logs = []
cy.on "log:added", (attrs, log) =>
@logs.push(log)

@stub = cy.stub().as("myStub")
@stub = cy.stub().as("myStub")

it "returns stub", ->
expect(@stub).to.have.property("callCount")
it "returns stub", ->
expect(@stub).to.have.property("callCount")

it "updates instrument log with alias", ->
expect(@logs[0].get("alias")).to.eq("myStub")
expect(@logs[0].get("aliasType")).to.eq("agent")
it "updates instrument log with alias", ->
expect(@logs[0].get("alias")).to.eq("myStub")
expect(@logs[0].get("aliasType")).to.eq("agent")

it "includes alias in invocation log", ->
@stub()
expect(@logs[1].get("alias")).to.eql(["myStub"])
expect(@logs[1].get("aliasType")).to.eq("agent")
it "includes alias in invocation log", ->
@stub()
expect(@logs[1].get("alias")).to.eql(["myStub"])
expect(@logs[1].get("aliasType")).to.eq("agent")

it "includes alias in console props", ->
@stub()
consoleProps = @logs[1].get("consoleProps")()
expect(consoleProps["Alias"]).to.eql("myStub")
it "includes alias in console props", ->
@stub()
consoleProps = @logs[1].get("consoleProps")()
expect(consoleProps["Alias"]).to.eql("myStub")

it "updates the displayName of the agent", ->
expect(@myStub.displayName).to.eq("myStub")

it "stores the lookup as an alias", ->
expect(cy.state("aliases").myStub).to.be.defined

it "stores the agent as the subject", ->
expect(cy.state("aliases").myStub.subject).to.eq(@stub)

it "assigns subject to runnable ctx", ->
expect(@myStub).to.eq(@stub)

it "retries until assertions pass", ->
cy.on "command:retry", _.after 2, =>
@myStub("foo")

cy.get("@myStub").should("be.calledWith", "foo")

describe "errors", ->
_.each [null, undefined, {}, [], 123], (value) =>
it "throws when passed: #{value}", ->
expect(=> cy.stub().as(value)).to.throw("cy.as() can only accept a string.")

it "throws on blank string", ->
expect(=> cy.stub().as("")).to.throw("cy.as() cannot be passed an empty string.")

_.each ["test", "runnable", "timeout", "slow", "skip", "inspect"], (blacklist) ->
it "throws on a blacklisted word: #{blacklist}", ->
expect(=> cy.stub().as(blacklist)).to.throw("cy.as() cannot be aliased as: '#{blacklist}'. This word is reserved.")

context "with dots", ->
beforeEach ->
@logs = []
cy.on "log:added", (attrs, log) =>
@logs.push(log)

@stub = cy.stub().as("my.stub")

it "returns stub", ->
expect(@stub).to.have.property("callCount")

it "updates instrument log with alias", ->
expect(@logs[0].get("alias")).to.eq("my.stub")
expect(@logs[0].get("aliasType")).to.eq("agent")

it "includes alias in invocation log", ->
@stub()
expect(@logs[1].get("alias")).to.eql(["my.stub"])
expect(@logs[1].get("aliasType")).to.eq("agent")

it "includes alias in console props", ->
@stub()
consoleProps = @logs[1].get("consoleProps")()
expect(consoleProps["Alias"]).to.eql("my.stub")

it "updates the displayName of the agent", ->
expect(@myStub.displayName).to.eq("myStub")
it "updates the displayName of the agent", ->
expect(@["my.stub"].displayName).to.eq("my.stub")

it "stores the lookup as an alias", ->
expect(cy.state("aliases").myStub).to.be.defined
it "stores the lookup as an alias", ->
expect(cy.state("aliases")["my.stub"]).to.be.defined

it "stores the agent as the subject", ->
expect(cy.state("aliases").myStub.subject).to.eq(@stub)
it "stores the agent as the subject", ->
expect(cy.state("aliases")["my.stub"].subject).to.eq(@stub)

it "assigns subject to runnable ctx", ->
expect(@myStub).to.eq(@stub)
it "assigns subject to runnable ctx", ->
expect(@["my.stub"]).to.eq(@stub)

it "retries until assertions pass", ->
cy.on "command:retry", _.after 2, =>
@myStub("foo")

cy.get("@myStub").should("be.calledWith", "foo")
it "retries until assertions pass", ->
cy.on "command:retry", _.after 2, =>
@["my.stub"]("foo")
cy.get("@my.stub").should("be.calledWith", "foo")

describe "errors", ->
_.each [null, undefined, {}, [], 123], (value) =>
it "throws when passed: #{value}", ->
expect(=> cy.stub().as(value)).to.throw("cy.as() can only accept a string.")
describe "errors", ->
_.each [null, undefined, {}, [], 123], (value) =>
it "throws when passed: #{value}", ->
expect(=> cy.stub().as(value)).to.throw("cy.as() can only accept a string.")

it "throws on blank string", ->
expect(=> cy.stub().as("")).to.throw("cy.as() cannot be passed an empty string.")
it "throws on blank string", ->
expect(=> cy.stub().as("")).to.throw("cy.as() cannot be passed an empty string.")

_.each ["test", "runnable", "timeout", "slow", "skip", "inspect"], (blacklist) ->
it "throws on a blacklisted word: #{blacklist}", ->
expect(=> cy.stub().as(blacklist)).to.throw("cy.as() cannot be aliased as: '#{blacklist}'. This word is reserved.")
_.each ["test", "runnable", "timeout", "slow", "skip", "inspect"], (blacklist) ->
it "throws on a blacklisted word: #{blacklist}", ->
expect(=> cy.stub().as(blacklist)).to.throw("cy.as() cannot be aliased as: '#{blacklist}'. This word is reserved.")

describe "logging", ->
beforeEach ->
Expand Down Expand Up @@ -468,4 +524,4 @@ describe "src/cy/commands/agents", ->
expect(@agents.spy).to.be.a("function")
expect(@agents.spy().callCount).to.be.a("number")
expect(@agents.stub).to.be.a("function")
expect(@agents.stub().returns).to.be.a("function")
expect(@agents.stub().returns).to.be.a("function")
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,20 @@ describe "src/cy/commands/aliasing", ->

cy.get("@obj").should("deep.eq", { foo: "bar" })

it "allows dot in alias names", ->
cy.get("body").as("body.foo").then ->
expect(cy.get('@body.foo')).to.be.defined
expect(cy.state("aliases")['body.foo']).to.be.defined

it "recognizes dot and non dot with same alias names", ->
cy.get("body").as("body").then ->
expect(cy.get('@body')).to.be.defined
expect(cy.state("aliases")['body']).to.be.defined
cy.contains("foo").as("body.foo").then ->
expect(cy.get('@body.foo')).to.be.defined
expect(cy.get('@body.foo')).to.not.eq(cy.get('@body'))
expect(cy.state("aliases")['body.foo']).to.be.defined

context "DOM subjects", ->
it "assigns the remote jquery instance", ->
obj = {}
Expand All @@ -75,6 +89,10 @@ describe "src/cy/commands/aliasing", ->
.noop({}).as("baz").then (obj) ->
expect(@baz).to.eq obj

it "assigns subject with dot to runnable ctx", ->
cy.noop({}).as("bar.baz").then (obj) ->
expect(@["bar.baz"]).to.eq obj

describe "nested hooks", ->
afterEach ->
if not @bar
Expand Down Expand Up @@ -130,6 +148,13 @@ describe "src/cy/commands/aliasing", ->

cy.get("div:first").as("@myAlias")

it "throws on alias starting with @ char and dots", (done) ->
cy.on "fail", (err) ->
expect(err.message).to.eq "'@my.alias' cannot be named starting with the '@' symbol. Try renaming the alias to 'my.alias', or something else that does not start with the '@' symbol."
done()

cy.get("div:first").as("@my.alias")

it "does not throw on alias with @ char in non-starting position", () ->
cy.get("div:first").as("my@Alias")
cy.get("@my@Alias")
Expand Down Expand Up @@ -157,7 +182,6 @@ describe "src/cy/commands/aliasing", ->
lastLog = @lastLog

expect(lastLog.get("aliasType")).to.eq "primitive"

it "sets aliasType to 'dom'", ->
cy.get("body").find("button:first").click().as("button").then ->
lastLog = @lastLog
Expand Down Expand Up @@ -198,11 +222,6 @@ describe "src/cy/commands/aliasing", ->
expect(@logs[1].get("name")).to.eq("route")
expect(@logs[1].get("alias")).to.eq("getFoo")

# it "does not alias previous logs when no matching chainerId", ->
# cy
# .get("div:first")
# .noop({}).as("foo").then ->

context "#replayCommandsFrom", ->
describe "subject in document", ->
it "returns if subject is still in the document", ->
Expand Down Expand Up @@ -349,7 +368,7 @@ describe "src/cy/commands/aliasing", ->
.get("body").as("b")
.get("input:first").as("firstInput")
.get("@lastDiv")

it "throws when alias is missing '@' but matches an available alias", (done) ->
cy.on "fail", (err) ->
expect(err.message).to.eq "Invalid alias: 'getAny'.\nYou forgot the '@'. It should be written as: '@getAny'."
Expand All @@ -358,4 +377,4 @@ describe "src/cy/commands/aliasing", ->
cy
.server()
.route("*", {}).as("getAny")
.wait("getAny").then ->
.wait("getAny").then ->
Loading

0 comments on commit 031ae70

Please sign in to comment.