mirror of
https://gitea.invidious.io/iv-org/shard-spectator.git
synced 2024-08-15 00:53:35 +00:00
Clean up expectation specs
Removed tests that are no longer valid or non-trivial to update.
This commit is contained in:
parent
251e3b8774
commit
a54e406fec
5 changed files with 34 additions and 458 deletions
|
@ -13,7 +13,7 @@ describe Spectator::ExpectationFailed do
|
|||
it "is the same as the expectation's #actual_message" do
|
||||
expectation = new_unsatisfied_expectation
|
||||
error = Spectator::ExpectationFailed.new(expectation)
|
||||
error.message.should eq(expectation.actual_message)
|
||||
error.message.should eq(expectation.failure_message)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,168 +0,0 @@
|
|||
require "../spec_helper"
|
||||
|
||||
describe Spectator::Expectations::BlockExpectationPartial do
|
||||
describe "#actual" do
|
||||
context "with a label" do
|
||||
it "contains the value passed to the constructor" do
|
||||
actual = ->{ 777 }
|
||||
partial = Spectator::Expectations::BlockExpectationPartial.new(actual, actual.to_s, __FILE__, __LINE__)
|
||||
partial.actual.should eq(actual.call)
|
||||
end
|
||||
end
|
||||
|
||||
context "without a label" do
|
||||
it "contains the value passed to the constructor" do
|
||||
actual = ->{ 777 }
|
||||
partial = Spectator::Expectations::BlockExpectationPartial.new(actual, __FILE__, __LINE__)
|
||||
partial.actual.should eq(actual.call)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#label" do
|
||||
context "when provided" do
|
||||
it "contains the value passed to the constructor" do
|
||||
actual = ->{ 777 }
|
||||
label = "lucky"
|
||||
partial = Spectator::Expectations::BlockExpectationPartial.new(actual, label, __FILE__, __LINE__)
|
||||
partial.label.should eq(label)
|
||||
end
|
||||
end
|
||||
|
||||
context "when omitted" do
|
||||
it "contains \"proc\"" do
|
||||
actual = ->{ 777 }
|
||||
partial = Spectator::Expectations::BlockExpectationPartial.new(actual, __FILE__, __LINE__)
|
||||
partial.label.should match(/proc/i)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#source_file" do
|
||||
it "is the expected value" do
|
||||
block = ->{ 42 }
|
||||
file = __FILE__
|
||||
partial = Spectator::Expectations::BlockExpectationPartial.new(block, file, __LINE__)
|
||||
partial.source_file.should eq(file)
|
||||
end
|
||||
end
|
||||
|
||||
describe "#source_line" do
|
||||
it "is the expected value" do
|
||||
block = ->{ 42 }
|
||||
line = __LINE__
|
||||
partial = Spectator::Expectations::BlockExpectationPartial.new(block, __FILE__, line)
|
||||
partial.source_line.should eq(line)
|
||||
end
|
||||
end
|
||||
|
||||
describe "#to" do
|
||||
it "reports an expectation" do
|
||||
spy = SpyExample.create do
|
||||
actual = ->{ 777 }
|
||||
expected = 777
|
||||
partial = Spectator::Expectations::BlockExpectationPartial.new(actual, __FILE__, __LINE__)
|
||||
matcher = Spectator::Matchers::EqualityMatcher.new(expected)
|
||||
partial.to(matcher)
|
||||
end
|
||||
Spectator::Internals::Harness.run(spy)
|
||||
spy.harness.expectations.size.should eq(1)
|
||||
end
|
||||
|
||||
it "reports multiple expectations" do
|
||||
spy = SpyExample.create do
|
||||
actual = ->{ 777 }
|
||||
expected = 777
|
||||
partial = Spectator::Expectations::BlockExpectationPartial.new(actual, __FILE__, __LINE__)
|
||||
matcher = Spectator::Matchers::EqualityMatcher.new(expected)
|
||||
5.times { partial.to(matcher) }
|
||||
end
|
||||
Spectator::Internals::Harness.run(spy)
|
||||
spy.harness.expectations.size.should eq(5)
|
||||
end
|
||||
|
||||
context "with a met condition" do
|
||||
it "reports a satisifed expectation" do
|
||||
spy = SpyExample.create do
|
||||
actual = ->{ 777 }
|
||||
expected = 777
|
||||
partial = Spectator::Expectations::BlockExpectationPartial.new(actual, __FILE__, __LINE__)
|
||||
matcher = Spectator::Matchers::EqualityMatcher.new(expected)
|
||||
partial.to(matcher)
|
||||
end
|
||||
Spectator::Internals::Harness.run(spy)
|
||||
spy.harness.expectations.first.satisfied?.should be_true
|
||||
end
|
||||
end
|
||||
|
||||
context "with an unmet condition" do
|
||||
it "reports an unsatisfied expectation" do
|
||||
spy = SpyExample.create do
|
||||
actual = ->{ 777 }
|
||||
expected = 42
|
||||
partial = Spectator::Expectations::BlockExpectationPartial.new(actual, __FILE__, __LINE__)
|
||||
matcher = Spectator::Matchers::EqualityMatcher.new(expected)
|
||||
partial.to(matcher)
|
||||
end
|
||||
Spectator::Internals::Harness.run(spy)
|
||||
spy.harness.expectations.first.satisfied?.should be_false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
{% for method in %i[to_not not_to] %}
|
||||
describe "#" + {{method.id.stringify}} do
|
||||
it "reports an expectation" do
|
||||
spy = SpyExample.create do
|
||||
actual = ->{ 777 }
|
||||
expected = 777
|
||||
partial = Spectator::Expectations::BlockExpectationPartial.new(actual, __FILE__, __LINE__)
|
||||
matcher = Spectator::Matchers::EqualityMatcher.new(expected)
|
||||
partial.{{method.id}}(matcher)
|
||||
end
|
||||
Spectator::Internals::Harness.run(spy)
|
||||
spy.harness.expectations.size.should eq(1)
|
||||
end
|
||||
|
||||
it "reports multiple expectations" do
|
||||
spy = SpyExample.create do
|
||||
actual = ->{ 777 }
|
||||
expected = 42
|
||||
partial = Spectator::Expectations::BlockExpectationPartial.new(actual, __FILE__, __LINE__)
|
||||
matcher = Spectator::Matchers::EqualityMatcher.new(expected)
|
||||
5.times { partial.{{method.id}}(matcher) }
|
||||
end
|
||||
Spectator::Internals::Harness.run(spy)
|
||||
spy.harness.expectations.size.should eq(5)
|
||||
end
|
||||
|
||||
context "with a met condition" do
|
||||
it "reports an unsatisifed expectation" do
|
||||
spy = SpyExample.create do
|
||||
actual = ->{ 777 }
|
||||
expected = 777
|
||||
partial = Spectator::Expectations::BlockExpectationPartial.new(actual, __FILE__, __LINE__)
|
||||
matcher = Spectator::Matchers::EqualityMatcher.new(expected)
|
||||
partial.{{method.id}}(matcher)
|
||||
end
|
||||
Spectator::Internals::Harness.run(spy)
|
||||
spy.harness.expectations.first.satisfied?.should be_false
|
||||
end
|
||||
end
|
||||
|
||||
context "with an unmet condition" do
|
||||
it "reports an satisfied expectation" do
|
||||
spy = SpyExample.create do
|
||||
actual = ->{ 777 }
|
||||
expected = 42
|
||||
partial = Spectator::Expectations::BlockExpectationPartial.new(actual, __FILE__, __LINE__)
|
||||
matcher = Spectator::Matchers::EqualityMatcher.new(expected)
|
||||
partial.{{method.id}}(matcher)
|
||||
end
|
||||
Spectator::Internals::Harness.run(spy)
|
||||
spy.harness.expectations.first.satisfied?.should be_true
|
||||
end
|
||||
end
|
||||
end
|
||||
{% end %}
|
||||
end
|
|
@ -5,18 +5,22 @@ describe Spectator::Expectations::Expectation do
|
|||
context "with a successful match" do
|
||||
it "is true" do
|
||||
value = 42
|
||||
match_data = new_matcher(value).match(new_partial(value))
|
||||
matcher = new_matcher(value)
|
||||
partial = new_partial(value)
|
||||
match_data = matcher.match(partial.actual)
|
||||
match_data.matched?.should be_true # Sanity check.
|
||||
expectation = Spectator::Expectations::Expectation.new(match_data, false)
|
||||
expectation = Spectator::Expectations::Expectation.new(match_data, partial.source)
|
||||
expectation.satisfied?.should be_true
|
||||
end
|
||||
|
||||
context "when negated" do
|
||||
it "is false" do
|
||||
value = 42
|
||||
match_data = new_matcher(value).match(new_partial(value))
|
||||
match_data.matched?.should be_true # Sanity check.
|
||||
expectation = Spectator::Expectations::Expectation.new(match_data, true)
|
||||
matcher = new_matcher(value)
|
||||
partial = new_partial(value)
|
||||
match_data = matcher.negated_match(partial.actual)
|
||||
match_data.matched?.should be_false # Sanity check.
|
||||
expectation = Spectator::Expectations::Expectation.new(match_data, partial.source)
|
||||
expectation.satisfied?.should be_false
|
||||
end
|
||||
end
|
||||
|
@ -24,126 +28,24 @@ describe Spectator::Expectations::Expectation do
|
|||
|
||||
context "with an unsuccessful match" do
|
||||
it "is false" do
|
||||
match_data = new_matcher(42).match(new_partial(777))
|
||||
matcher = new_matcher(42)
|
||||
partial = new_partial(777)
|
||||
match_data = matcher.match(partial.actual)
|
||||
match_data.matched?.should be_false # Sanity check.
|
||||
expectation = Spectator::Expectations::Expectation.new(match_data, false)
|
||||
expectation = Spectator::Expectations::Expectation.new(match_data, partial.source)
|
||||
expectation.satisfied?.should be_false
|
||||
end
|
||||
|
||||
context "when negated" do
|
||||
it "is true" do
|
||||
match_data = new_matcher(42).match(new_partial(777))
|
||||
match_data.matched?.should be_false # Sanity check.
|
||||
expectation = Spectator::Expectations::Expectation.new(match_data, true)
|
||||
matcher = new_matcher(42)
|
||||
partial = new_partial(777)
|
||||
match_data = matcher.negated_match(partial.actual)
|
||||
match_data.matched?.should be_true # Sanity check.
|
||||
expectation = Spectator::Expectations::Expectation.new(match_data, partial.source)
|
||||
expectation.satisfied?.should be_true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#values" do
|
||||
it "is the same as the match data values" do
|
||||
value = 42
|
||||
match_data = new_matcher(value).match(new_partial(value))
|
||||
expectation = Spectator::Expectations::Expectation.new(match_data, false)
|
||||
expectation_values = expectation.values
|
||||
match_data.values.zip(expectation_values).each do |m, e|
|
||||
m.label.should eq(e.label)
|
||||
m.value.value.should eq(e.value.value)
|
||||
end
|
||||
end
|
||||
|
||||
context "when negated" do
|
||||
it "negates all negatable values" do
|
||||
value = 42
|
||||
match_data = new_matcher(value).match(new_partial(value))
|
||||
expectation = Spectator::Expectations::Expectation.new(match_data, true)
|
||||
expectation.values.each do |labeled_value|
|
||||
label = labeled_value.label
|
||||
value = labeled_value.value
|
||||
value.to_s.should start_with(/not/i) if label == :expected
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#actual_message" do
|
||||
context "with a successful match" do
|
||||
it "equals the matcher's #message" do
|
||||
value = 42
|
||||
match_data = new_matcher(value).match(new_partial(value))
|
||||
match_data.matched?.should be_true # Sanity check.
|
||||
expectation = Spectator::Expectations::Expectation.new(match_data, false)
|
||||
expectation.actual_message.should eq(match_data.message)
|
||||
end
|
||||
|
||||
context "when negated" do
|
||||
it "equals the matcher's #message" do
|
||||
value = 42
|
||||
match_data = new_matcher(value).match(new_partial(value))
|
||||
match_data.matched?.should be_true # Sanity check.
|
||||
expectation = Spectator::Expectations::Expectation.new(match_data, true)
|
||||
expectation.actual_message.should eq(match_data.message)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "with an unsuccessful match" do
|
||||
it "equals the matcher's #negated_message" do
|
||||
match_data = new_matcher(42).match(new_partial(777))
|
||||
match_data.matched?.should be_false # Sanity check.
|
||||
expectation = Spectator::Expectations::Expectation.new(match_data, false)
|
||||
expectation.actual_message.should eq(match_data.negated_message)
|
||||
end
|
||||
|
||||
context "when negated" do
|
||||
it "equals the matcher's #negated_message" do
|
||||
match_data = new_matcher(42).match(new_partial(777))
|
||||
match_data.matched?.should be_false # Sanity check.
|
||||
expectation = Spectator::Expectations::Expectation.new(match_data, true)
|
||||
expectation.actual_message.should eq(match_data.negated_message)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#expected_message" do
|
||||
context "with a successful match" do
|
||||
it "equals the matcher's #message" do
|
||||
value = 42
|
||||
match_data = new_matcher(value).match(new_partial(value))
|
||||
match_data.matched?.should be_true # Sanity check.
|
||||
expectation = Spectator::Expectations::Expectation.new(match_data, false)
|
||||
expectation.expected_message.should eq(match_data.message)
|
||||
end
|
||||
|
||||
context "when negated" do
|
||||
it "equals the matcher's #negated_message" do
|
||||
value = 42
|
||||
match_data = new_matcher(value).match(new_partial(value))
|
||||
match_data.matched?.should be_true # Sanity check.
|
||||
expectation = Spectator::Expectations::Expectation.new(match_data, true)
|
||||
expectation.expected_message.should eq(match_data.negated_message)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "with an unsuccessful match" do
|
||||
it "equals the matcher's #message" do
|
||||
match_data = new_matcher(42).match(new_partial(777))
|
||||
match_data.matched?.should be_false # Sanity check.
|
||||
expectation = Spectator::Expectations::Expectation.new(match_data, false)
|
||||
expectation.expected_message.should eq(match_data.message)
|
||||
end
|
||||
|
||||
context "when negated" do
|
||||
it "equals the matcher's #negated_message" do
|
||||
match_data = new_matcher(42).match(new_partial(777))
|
||||
match_data.matched?.should be_false # Sanity check.
|
||||
expectation = Spectator::Expectations::Expectation.new(match_data, true)
|
||||
expectation.expected_message.should eq(match_data.negated_message)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,166 +0,0 @@
|
|||
require "../spec_helper"
|
||||
|
||||
describe Spectator::Expectations::ValueExpectationPartial do
|
||||
describe "#actual" do
|
||||
context "with a label" do
|
||||
it "contains the value passed to the constructor" do
|
||||
actual = 777
|
||||
partial = Spectator::Expectations::ValueExpectationPartial.new(actual, actual.to_s, __FILE__, __LINE__)
|
||||
partial.actual.should eq(actual)
|
||||
end
|
||||
end
|
||||
|
||||
context "without a label" do
|
||||
it "contains the value passed to the constructor" do
|
||||
actual = 777
|
||||
partial = Spectator::Expectations::ValueExpectationPartial.new(actual, __FILE__, __LINE__)
|
||||
partial.actual.should eq(actual)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#label" do
|
||||
context "when provided" do
|
||||
it "contains the value passed to the constructor" do
|
||||
actual = 777
|
||||
label = "lucky"
|
||||
partial = Spectator::Expectations::ValueExpectationPartial.new(actual, label, __FILE__, __LINE__)
|
||||
partial.label.should eq(label)
|
||||
end
|
||||
end
|
||||
|
||||
context "when omitted" do
|
||||
it "contains a stringified version of #actual" do
|
||||
actual = 777
|
||||
partial = Spectator::Expectations::ValueExpectationPartial.new(actual, __FILE__, __LINE__)
|
||||
partial.label.should eq(actual.to_s)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#source_file" do
|
||||
it "is the expected value" do
|
||||
file = __FILE__
|
||||
partial = Spectator::Expectations::ValueExpectationPartial.new(42, file, __LINE__)
|
||||
partial.source_file.should eq(file)
|
||||
end
|
||||
end
|
||||
|
||||
describe "#source_line" do
|
||||
it "is the expected value" do
|
||||
line = __LINE__
|
||||
partial = Spectator::Expectations::ValueExpectationPartial.new(42, __FILE__, line)
|
||||
partial.source_line.should eq(line)
|
||||
end
|
||||
end
|
||||
|
||||
describe "#to" do
|
||||
it "reports an expectation" do
|
||||
spy = SpyExample.create do
|
||||
actual = 777
|
||||
expected = 777
|
||||
partial = Spectator::Expectations::ValueExpectationPartial.new(actual, __FILE__, __LINE__)
|
||||
matcher = Spectator::Matchers::EqualityMatcher.new(expected)
|
||||
partial.to(matcher)
|
||||
end
|
||||
Spectator::Internals::Harness.run(spy)
|
||||
spy.harness.expectations.size.should eq(1)
|
||||
end
|
||||
|
||||
it "reports multiple expectations" do
|
||||
spy = SpyExample.create do
|
||||
actual = 777
|
||||
expected = 777
|
||||
partial = Spectator::Expectations::ValueExpectationPartial.new(actual, __FILE__, __LINE__)
|
||||
matcher = Spectator::Matchers::EqualityMatcher.new(expected)
|
||||
5.times { partial.to(matcher) }
|
||||
end
|
||||
Spectator::Internals::Harness.run(spy)
|
||||
spy.harness.expectations.size.should eq(5)
|
||||
end
|
||||
|
||||
context "with a met condition" do
|
||||
it "reports a satisifed expectation" do
|
||||
spy = SpyExample.create do
|
||||
actual = 777
|
||||
expected = 777
|
||||
partial = Spectator::Expectations::ValueExpectationPartial.new(actual, __FILE__, __LINE__)
|
||||
matcher = Spectator::Matchers::EqualityMatcher.new(expected)
|
||||
partial.to(matcher)
|
||||
end
|
||||
Spectator::Internals::Harness.run(spy)
|
||||
spy.harness.expectations.first.satisfied?.should be_true
|
||||
end
|
||||
end
|
||||
|
||||
context "with an unmet condition" do
|
||||
it "reports an unsatisfied expectation" do
|
||||
spy = SpyExample.create do
|
||||
actual = 777
|
||||
expected = 42
|
||||
partial = Spectator::Expectations::ValueExpectationPartial.new(actual, __FILE__, __LINE__)
|
||||
matcher = Spectator::Matchers::EqualityMatcher.new(expected)
|
||||
partial.to(matcher)
|
||||
end
|
||||
Spectator::Internals::Harness.run(spy)
|
||||
spy.harness.expectations.first.satisfied?.should be_false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
{% for method in %i[to_not not_to] %}
|
||||
describe "#" + {{method.id.stringify}} do
|
||||
it "reports an expectation" do
|
||||
spy = SpyExample.create do
|
||||
actual = 777
|
||||
expected = 777
|
||||
partial = Spectator::Expectations::ValueExpectationPartial.new(actual, __FILE__, __LINE__)
|
||||
matcher = Spectator::Matchers::EqualityMatcher.new(expected)
|
||||
partial.{{method.id}}(matcher)
|
||||
end
|
||||
Spectator::Internals::Harness.run(spy)
|
||||
spy.harness.expectations.size.should eq(1)
|
||||
end
|
||||
|
||||
it "reports multiple expectations" do
|
||||
spy = SpyExample.create do
|
||||
actual = 777
|
||||
expected = 42
|
||||
partial = Spectator::Expectations::ValueExpectationPartial.new(actual, __FILE__, __LINE__)
|
||||
matcher = Spectator::Matchers::EqualityMatcher.new(expected)
|
||||
5.times { partial.{{method.id}}(matcher) }
|
||||
end
|
||||
Spectator::Internals::Harness.run(spy)
|
||||
spy.harness.expectations.size.should eq(5)
|
||||
end
|
||||
|
||||
context "with a met condition" do
|
||||
it "reports an unsatisifed expectation" do
|
||||
spy = SpyExample.create do
|
||||
actual = 777
|
||||
expected = 777
|
||||
partial = Spectator::Expectations::ValueExpectationPartial.new(actual, __FILE__, __LINE__)
|
||||
matcher = Spectator::Matchers::EqualityMatcher.new(expected)
|
||||
partial.{{method.id}}(matcher)
|
||||
end
|
||||
Spectator::Internals::Harness.run(spy)
|
||||
spy.harness.expectations.first.satisfied?.should be_false
|
||||
end
|
||||
end
|
||||
|
||||
context "with an unmet condition" do
|
||||
it "reports an satisfied expectation" do
|
||||
spy = SpyExample.create do
|
||||
actual = 777
|
||||
expected = 42
|
||||
partial = Spectator::Expectations::ValueExpectationPartial.new(actual, __FILE__, __LINE__)
|
||||
matcher = Spectator::Matchers::EqualityMatcher.new(expected)
|
||||
partial.{{method.id}}(matcher)
|
||||
end
|
||||
Spectator::Internals::Harness.run(spy)
|
||||
spy.harness.expectations.first.satisfied?.should be_true
|
||||
end
|
||||
end
|
||||
end
|
||||
{% end %}
|
||||
end
|
|
@ -1,30 +1,38 @@
|
|||
# Utility methods for creating expectations, partials, and matchers.
|
||||
|
||||
def new_partial(actual : T, label : String) forall T
|
||||
Spectator::Expectations::ValueExpectationPartial.new(actual, label, __FILE__, __LINE__)
|
||||
test_value = Spectator::TestValue.new(actual, label)
|
||||
source = Spectator::Source.new(__FILE__, __LINE__)
|
||||
Spectator::Expectations::ExpectationPartial.new(test_value, source)
|
||||
end
|
||||
|
||||
def new_partial(actual : T = 123) forall T
|
||||
Spectator::Expectations::ValueExpectationPartial.new(actual, __FILE__, __LINE__)
|
||||
test_value = Spectator::TestValue.new(actual)
|
||||
source = Spectator::Source.new(__FILE__, __LINE__)
|
||||
Spectator::Expectations::ExpectationPartial.new(test_value, source)
|
||||
end
|
||||
|
||||
def new_block_partial(label = "BLOCK", &block)
|
||||
Spectator::Expectations::BlockExpectationPartial.new(block, label, __FILE__, __LINE__)
|
||||
test_block = Spectator::TestBlock.new(block, label)
|
||||
source = Spectator::Source.new(__FILE__, __LINE__)
|
||||
Spectator::Expectations::ExpectationPartial.new(test_block, source)
|
||||
end
|
||||
|
||||
def new_matcher(expected : T, label : String) forall T
|
||||
Spectator::Matchers::EqualityMatcher.new(expected, label)
|
||||
test_value = Spectator::TestValue.new(expected, label)
|
||||
Spectator::Matchers::EqualityMatcher.new(test_value)
|
||||
end
|
||||
|
||||
def new_matcher(expected : T = 123) forall T
|
||||
Spectator::Matchers::EqualityMatcher.new(expected)
|
||||
test_value = Spectator::TestValue.new(expected)
|
||||
Spectator::Matchers::EqualityMatcher.new(test_value)
|
||||
end
|
||||
|
||||
def new_expectation(expected : ExpectedType = 123, actual : ActualType = 123) forall ExpectedType, ActualType
|
||||
partial = new_partial(actual, "foo")
|
||||
matcher = new_matcher(expected, "bar")
|
||||
match_data = matcher.match(partial)
|
||||
Spectator::Expectations::Expectation.new(match_data, false)
|
||||
match_data = matcher.match(partial.actual)
|
||||
Spectator::Expectations::Expectation.new(match_data, partial.source)
|
||||
end
|
||||
|
||||
def new_satisfied_expectation(value : T = 123) forall T
|
||||
|
|
Loading…
Reference in a new issue