Files
ubicloud/spec/routes/web/project/billing_spec.rb
Enes Cakir e2da9099b1 Show only the company name on the invoice if it's provided
In the past, we didn't have a separate field for the company name, so
customers used the billing name as the company name.

Now that we've added a company name field, it appears on the invoice
twice. In addition, some customers have requested that only the company
name be shown on the invoice, not the person's name.

Therefore, if the company name is provided, there's no need to include
the person's name.

We didn't do this before because we were generating invoice PDFs on the
fly, which would have altered already generated invoices. However, we
have started to persist the invoices in blob storage and display them
from there, so these changes won't affect old invoices.
2025-04-30 21:38:33 +03:00

523 lines
25 KiB
Ruby

# frozen_string_literal: true
require_relative "../spec_helper"
require "aws-sdk-s3"
require "pdf-reader"
require "stripe"
RSpec.describe Clover, "billing" do
let(:user) { create_account }
let(:project) { user.create_project_with_default_policy("project-1") }
let(:project_wo_permissions) { user.create_project_with_default_policy("project-2", default_policy: nil) }
let(:billing_info) do
bi = BillingInfo.create_with_id(stripe_id: "cs_1234567890")
project.update(billing_info_id: bi.id)
bi
end
let(:payment_method) { PaymentMethod.create_with_id(billing_info_id: billing_info.id, stripe_id: "pm_1234567890") }
before do
login(user.email)
end
it "disabled when Stripe secret key not provided" do
allow(Config).to receive(:stripe_secret_key).and_return(nil)
visit project.path
within "#desktop-menu" do
expect { click_link "Billing" }.to raise_error Capybara::ElementNotFound
end
expect(page.title).to eq("Ubicloud - #{project.name}")
visit "#{project.path}/billing"
expect(page.status_code).to eq(501)
expect(page.body).to eq "Billing is not enabled. Set STRIPE_SECRET_KEY to enable billing."
end
it "tag payment method fraud after account suspension" do
expect(payment_method.reload.fraud).to be(false)
user.suspend
expect(payment_method.reload.fraud).to be(true)
end
context "when Stripe enabled" do
before do
allow(Config).to receive(:stripe_secret_key).and_return("secret_key")
end
it "raises forbidden when does not have permissions" do
project_wo_permissions
visit "#{project_wo_permissions.path}/billing"
expect(page.title).to eq("Ubicloud - Forbidden")
expect(page.status_code).to eq(403)
expect(page).to have_content "Forbidden"
end
it "can create billing info" do
# rubocop:disable RSpec/VerifiedDoubles
expect(Stripe::Checkout::Session).to receive(:create).and_return(double(Stripe::Checkout::Session, url: "#{project.path}/billing/success?session_id=session_123"))
expect(Stripe::PaymentIntent).to receive(:create).and_return(double(Stripe::PaymentIntent, status: "requires_capture", id: "pi_1234567890"))
# rubocop:enable RSpec/VerifiedDoubles
expect(Stripe::Checkout::Session).to receive(:retrieve).with("session_123").and_return({"setup_intent" => "st_123456790"})
expect(Stripe::SetupIntent).to receive(:retrieve).with("st_123456790").and_return({"customer" => "cs_1234567890", "payment_method" => "pm_1234567890"})
expect(Stripe::Customer).to receive(:retrieve).with("cs_1234567890").and_return({"name" => "ACME Inc.", "address" => {"country" => "NL"}, "metadata" => {"company_name" => "Foo Companye Name"}}).twice
expect(Stripe::PaymentMethod).to receive(:retrieve).with("pm_1234567890").and_return({"card" => {"brand" => "visa"}}).thrice
visit project.path
within "#desktop-menu" do
click_link "Billing"
end
expect(page.title).to eq("Ubicloud - Project Billing")
click_button "Add new billing information"
billing_info = project.reload.billing_info
expect(page.status_code).to eq(200)
expect(billing_info.stripe_id).to eq("cs_1234567890")
expect(page).to have_field("Billing Name", with: "ACME Inc.")
expect(billing_info.payment_methods.first.stripe_id).to eq("pm_1234567890")
expect(page).to have_content "Visa"
expect(page).to have_no_content "Discount"
expect(page).to have_no_content "100%"
project.this.update(discount: 100)
page.refresh
expect(page).to have_content "Discount"
expect(page).to have_content "100%"
end
it "can not create billing info with unauthorized payment" do
# rubocop:disable RSpec/VerifiedDoubles
expect(Stripe::Checkout::Session).to receive(:create).and_return(double(Stripe::Checkout::Session, url: "#{project.path}/billing/success?session_id=session_123"))
expect(Stripe::PaymentIntent).to receive(:create).and_return(double(Stripe::PaymentIntent, status: "canceled", id: "pi_1234567890"))
# rubocop:enable RSpec/VerifiedDoubles
expect(Stripe::Checkout::Session).to receive(:retrieve).with("session_123").and_return({"setup_intent" => "st_123456790"})
expect(Stripe::SetupIntent).to receive(:retrieve).with("st_123456790").and_return({"customer" => "cs_1234567890", "payment_method" => "pm_1234567890"})
expect(Stripe::PaymentMethod).to receive(:retrieve).with("pm_1234567890").and_return({"card" => {"brand" => "visa"}}).once
expect(Clog).to receive(:emit)
visit project.path
within "#desktop-menu" do
click_link "Billing"
end
expect(page.title).to eq("Ubicloud - Project Billing")
click_button "Add new billing information"
expect(page.status_code).to eq(200)
expect(page).to have_flash_error("We couldn't pre-authorize your card for verification. Please make sure it can be pre-authorized up to $5 or contact our support team at support@ubicloud.com.")
end
it "can update billing info" do
expect(Stripe::Customer).to receive(:retrieve).with(billing_info.stripe_id).and_return(
{"name" => "New Inc.", "address" => {"country" => "DE"}, "metadata" => {"tax_id" => "DE456789"}}
).at_least(:once)
expect(Stripe::Customer).to receive(:update).with(billing_info.stripe_id, anything)
visit "#{project.path}/billing"
expect(page.title).to eq("Ubicloud - Project Billing")
fill_in "Billing Name", with: "New Inc."
select "United States", from: "Country"
click_button "Update"
expect(page.status_code).to eq(200)
expect(page).to have_field("Billing Name", with: "New Inc.")
expect(page).to have_field("Country", with: "DE")
end
it "can update tax id" do
expect(Stripe::Customer).to receive(:retrieve).with(billing_info.stripe_id).and_return(
{"name" => "Old Inc.", "address" => {"country" => "NL"}, "metadata" => {"tax_id" => "123456"}},
{"name" => "Old Inc.", "address" => {"country" => "NL"}, "metadata" => {"tax_id" => "123456"}},
{"name" => "New Inc.", "address" => {"country" => "US"}, "metadata" => {"tax_id" => "DE456789"}}
).at_least(:once)
expect(Stripe::Customer).to receive(:update).with(billing_info.stripe_id, anything).at_least(:once)
expect(Strand).to receive(:create).with(prog: "ValidateVat", label: "start", stack: [{subject_id: billing_info.id}])
visit "#{project.path}/billing"
expect(page.title).to eq("Ubicloud - Project Billing")
select "United States", from: "Country"
fill_in "VAT ID", with: "DE 456-789"
click_button "Update"
expect(page.status_code).to eq(200)
expect(page).to have_field("Billing Name", with: "New Inc.")
expect(page).to have_field("Country", with: "US")
expect(page).to have_field("Tax ID", with: "DE456789")
fill_in "Tax ID", with: nil
click_button "Update"
end
it "shows error if billing info update failed" do
expect(Stripe::Customer).to receive(:retrieve).with(billing_info.stripe_id).and_return(
{"name" => "Old Inc.", "address" => {"country" => "NL"}, "metadata" => {"tax_id" => "123456"}}
).at_least(:once)
expect(Stripe::Customer).to receive(:update).and_raise(Stripe::InvalidRequestError.new("Invalid email address: test@test.com", "email"))
visit "#{project.path}/billing"
expect(page.title).to eq("Ubicloud - Project Billing")
fill_in "Billing Email", with: " test@test.com"
click_button "Update"
expect(page.status_code).to eq(200)
expect(page).to have_flash_error("Invalid email address: test@test.com")
end
it "can add new payment method" do
expect(Stripe::Customer).to receive(:retrieve).with("cs_1234567890").and_return({"name" => "ACME Inc.", "address" => {"country" => "NL"}, "metadata" => {"company_name" => "Foo Companye Name"}}).twice
expect(Stripe::PaymentMethod).to receive(:retrieve).with(payment_method.stripe_id).and_return({"card" => {"brand" => "visa"}}).twice
expect(Stripe::PaymentMethod).to receive(:retrieve).with("pm_222222222").and_return({"card" => {"brand" => "mastercard"}}).twice
# rubocop:disable RSpec/VerifiedDoubles
expect(Stripe::Checkout::Session).to receive(:create).and_return(double(Stripe::Checkout::Session, url: "#{project.path}/billing/success?session_id=session_123"))
expect(Stripe::PaymentIntent).to receive(:create).and_return(double(Stripe::PaymentIntent, status: "requires_capture", id: "pi_1234567890"))
# rubocop:enable RSpec/VerifiedDoubles
expect(Stripe::Checkout::Session).to receive(:retrieve).with("session_123").and_return({"setup_intent" => "st_123456790"})
expect(Stripe::SetupIntent).to receive(:retrieve).with("st_123456790").and_return({"payment_method" => "pm_222222222"})
visit "#{project.path}/billing"
click_link "Add Payment Method"
expect(page.status_code).to eq(200)
expect(page.title).to eq("Ubicloud - Project Billing")
expect(billing_info.payment_methods.count).to eq(2)
expect(page).to have_content "Visa"
expect(page).to have_content "Mastercard"
end
it "can't add fraud payment method" do
fraud_payment_method = PaymentMethod.create_with_id(billing_info_id: billing_info.id, stripe_id: "pmi_1234567890", fraud: true, card_fingerprint: "cfg1234")
expect(Stripe::Customer).to receive(:retrieve).with("cs_1234567890").and_return({"name" => "ACME Inc.", "address" => {"country" => "NL"}, "metadata" => {"company_name" => "Foo Companye Name"}}).twice
expect(Stripe::PaymentMethod).to receive(:retrieve).with(fraud_payment_method.stripe_id).and_return({"card" => {"brand" => "visa"}}).twice
expect(Stripe::PaymentMethod).to receive(:retrieve).with("pm_222222222").and_return({"card" => {"brand" => "mastercard", "fingerprint" => "cfg1234"}})
# rubocop:disable RSpec/VerifiedDoubles
expect(Stripe::Checkout::Session).to receive(:create).and_return(double(Stripe::Checkout::Session, url: "#{project.path}/billing/success?session_id=session_123"))
# rubocop:enable RSpec/VerifiedDoubles
expect(Stripe::Checkout::Session).to receive(:retrieve).with("session_123").and_return({"setup_intent" => "st_123456790"})
expect(Stripe::SetupIntent).to receive(:retrieve).with("st_123456790").and_return({"payment_method" => "pm_222222222"})
visit "#{project.path}/billing"
click_link "Add Payment Method"
expect(page.status_code).to eq(200)
expect(page.title).to eq("Ubicloud - Project Billing")
expect(billing_info.payment_methods.count).to eq(1)
expect(page).to have_content "Visa"
expect(page).to have_flash_error("Payment method you added is labeled as fraud. Please contact support.")
end
it "raises not found when payment method not exists" do
visit "#{project.path}/billing/payment-method/08s56d4kaj94xsmrnf5v5m3mav"
expect(page.title).to eq("Ubicloud - ResourceNotFound")
expect(page.status_code).to eq(404)
expect(page).to have_content "ResourceNotFound"
end
it "raises not found when add payment method if project not exists" do
visit "#{project.path}/billing/payment-method/create"
expect(page.title).to eq("Ubicloud - ResourceNotFound")
expect(page.status_code).to eq(404)
expect(page).to have_content "ResourceNotFound"
end
it "can't delete last payment method" do
expect(Stripe::Customer).to receive(:retrieve).with("cs_1234567890").and_return({"name" => "ACME Inc.", "address" => {"country" => "NL"}, "metadata" => {"company_name" => "Foo Companye Name"}})
expect(Stripe::PaymentMethod).to receive(:retrieve).with(payment_method.stripe_id).and_return({"card" => {"brand" => "visa"}})
visit "#{project.path}/billing"
# We send delete request manually instead of just clicking to button because delete action triggered by JavaScript.
# UI tests run without a JavaScript engine.
btn = find "#payment-method-#{payment_method.ubid} .delete-btn"
page.driver.delete btn["data-url"], {_csrf: btn["data-csrf"]}
expect(page.status_code).to eq(400)
expect(page.body).to eq({error: {message: "You can't delete the last payment method of a project."}}.to_json)
expect(billing_info.reload.payment_methods.count).to eq(1)
end
it "can delete payment method" do
payment_method_2 = PaymentMethod.create_with_id(billing_info_id: billing_info.id, stripe_id: "pm_2222222222")
expect(Stripe::Customer).to receive(:retrieve).with("cs_1234567890").and_return({"name" => "John Doe", "address" => {"country" => "NL"}, "metadata" => {"company_name" => "ACME Inc."}})
expect(Stripe::PaymentMethod).to receive(:retrieve).with(payment_method.stripe_id).and_return({"card" => {"brand" => "visa"}})
expect(Stripe::PaymentMethod).to receive(:retrieve).with(payment_method_2.stripe_id).and_return({"card" => {"brand" => "mastercard"}})
expect(Stripe::PaymentMethod).to receive(:detach).with(payment_method.stripe_id)
visit "#{project.path}/billing"
# We send delete request manually instead of just clicking to button because delete action triggered by JavaScript.
# UI tests run without a JavaScript enginer.
btn = find "#payment-method-#{payment_method.ubid} .delete-btn"
page.driver.delete btn["data-url"], {_csrf: btn["data-csrf"]}
expect(page.status_code).to eq(204)
expect(page.body).to be_empty
expect(billing_info.reload.payment_methods.count).to eq(1)
end
describe "invoices" do
let(:blob_storage_client) { Aws::S3::Client.new(stub_responses: true) }
before do
allow(Aws::S3::Client).to receive(:new).and_return(blob_storage_client)
blob_storage_client.stub_responses(:get_object, "NoSuchKey")
end
def billing_record(begin_time, end_time)
vm = create_vm
BillingRecord.create_with_id(
project_id: billing_info.project.id,
resource_id: vm.id,
resource_name: vm.name,
span: Sequel::Postgres::PGRange.new(begin_time, end_time),
billing_rate_id: BillingRate.from_resource_properties("VmVCpu", vm.family, vm.location.name)["id"],
amount: vm.vcpus
)
end
it "list invoices of project" do
expect(Stripe::Customer).to receive(:retrieve).with(billing_info.stripe_id).and_return({"name" => "John Doe", "address" => {"country" => "NL"}, "metadata" => {}}).at_least(:once)
bi = billing_record(Time.utc(2023, 6), Time.utc(2023, 7))
InvoiceGenerator.new(bi.span.begin, bi.span.end, save_result: true, eur_rate: 1.1).run
invoice = Invoice.first
visit "#{project.path}/billing"
expect(page.status_code).to eq(200)
expect(page.title).to eq("Ubicloud - Project Billing")
expect(page).to have_content invoice.name
invoice.content["cost"] = 123.45
invoice.content["subtotal"] = 543.21
invoice.this.update(content: invoice.content)
page.refresh
expect(page).to have_content("$123.45 ($543.21)")
click_link invoice.name
end
it "show current usage details" do
expect(Stripe::Customer).to receive(:retrieve).with("cs_1234567890").and_return({"name" => "John Doe", "address" => {"country" => "NL"}, "metadata" => {"company_name" => "ACME Inc."}}).at_least(:once)
bi = billing_record(Time.utc(2023, 6), Time.utc(2023, 7))
100.times do
billing_record(Time.utc(2023, 6), Time.utc(2023, 6) + 10)
end
invoice = InvoiceGenerator.new(bi.span.begin, bi.span.end, save_result: true, eur_rate: 1.1).run.first
invoice.update(status: "current")
expect(InvoiceGenerator).to receive(:new).and_return(instance_double(InvoiceGenerator, run: [invoice])).at_least(:once)
visit "#{project.path}/billing/invoice/current"
expect(page.status_code).to eq(200)
expect(page.title).to eq("Ubicloud - Current Usage Summary")
expect(page).to have_content "Aggregated"
expect(page.has_css?("#invoice-discount")).to be false
expect(page.has_css?("#invoice-credit")).to be false
content = invoice.content
content["discount"] = 1
content["credit"] = 2
content["free_inference_tokens_credit"] = 3
invoice.this.update(content:)
page.refresh
expect(find_by_id("invoice-discount").text).to eq "-$1.00"
expect(find_by_id("invoice-credit").text).to eq "-$2.00"
expect(find_by_id("invoice-free-inference-tokens").text).to eq "-$3.00"
end
it "show current invoice when no usage" do
expect(Stripe::Customer).to receive(:retrieve).with(billing_info.stripe_id).and_return({"name" => "John Doe", "address" => {}, "metadata" => {}}).at_least(:once)
visit "#{project.path}/billing"
expect(page.status_code).to eq(200)
expect(page.title).to eq("Ubicloud - Project Billing")
expect(page).to have_content "current"
expect(page).to have_content "not finalized"
click_link href: "#{project.path}/billing/invoice/current"
expect(page).to have_content "Current Usage Summary"
expect(page).to have_content "No resources"
end
it "list current invoice with last month usage" do
expect(Stripe::Customer).to receive(:retrieve).with(billing_info.stripe_id).and_return({"name" => "John Doe", "address" => {"country" => "NL"}, "metadata" => {}}).at_least(:once)
br_previous = billing_record(Time.utc(2023, 6), Time.utc(2023, 7))
br_current = billing_record(Time.utc(2023, 7), Time.utc(2023, 7, 15))
invoice_previous = InvoiceGenerator.new(br_previous.span.begin, br_previous.span.end, save_result: true, eur_rate: 1.1).run.first
invoice_current = InvoiceGenerator.new(br_current.span.begin, br_current.span.end, project_ids: [project.id]).run.first
visit "#{project.path}/billing"
expect(page.status_code).to eq(200)
expect(page.title).to eq("Ubicloud - Project Billing")
expect(page).to have_content "current"
expect(page).to have_content "not finalized"
expect(page).to have_content "$%0.02f" % invoice_current.content["cost"]
expect(page).to have_content "$%0.02f" % invoice_previous.content["cost"]
click_link href: "#{project.path}/billing/invoice/current"
expect(page).to have_content "Current Usage Summary"
expect(page).to have_content "$%0.02f" % invoice_current.content["cost"]
expect(page).to have_content "less than $0.001"
end
it "show finalized invoice as PDF from US issuer without VAT" do
expect(Stripe::Customer).to receive(:retrieve).with("cs_1234567890").and_return({"name" => "John Doe", "address" => {"country" => "US"}, "metadata" => {"company_name" => "Acme Inc.", "tax_id" => "123123123"}}).at_least(:once)
bi = billing_record(Time.utc(2023, 6), Time.utc(2023, 7))
invoice = InvoiceGenerator.new(bi.span.begin, bi.span.end, save_result: true, eur_rate: 1.1).run.first
visit "#{project.path}/billing"
click_link invoice.name
expect(page.status_code).to eq(200)
text = PDF::Reader.new(StringIO.new(page.body)).pages.map(&:text).join(" ")
expect(text).to include("Ubicloud Inc.")
expect(text).to include("Acme Inc.")
expect(text).not_to include("John Doe")
expect(text).to include("test-vm")
expect(text).not_to include("VAT")
end
it "show finalized invoice as PDF from EU issuer with 21% VAT" do
expect(Stripe::Customer).to receive(:retrieve).with("cs_1234567890").and_return({"name" => "John Doe", "address" => {"country" => "DE"}, "metadata" => {"company_name" => ""}}).at_least(:once)
bi = billing_record(Time.utc(2023, 6), Time.utc(2023, 7))
invoice = InvoiceGenerator.new(bi.span.begin, bi.span.end, save_result: true, eur_rate: 1.1).run.first
visit "#{project.path}/billing"
expect(page).to have_content "EU registered business can enter their VAT ID to remove VAT from future invoices."
click_link invoice.name
expect(page.status_code).to eq(200)
text = PDF::Reader.new(StringIO.new(page.body)).pages.map(&:text).join(" ")
expect(text).to include("Ubicloud B.V.")
expect(text).to include("John Doe")
expect(text).to include("test-vm")
expect(text).to include("VAT (21%): (€5.68) $5.17")
end
it "show finalized invoice as PDF from EU issuer with reversed charge" do
expect(Stripe::Customer).to receive(:retrieve).with("cs_1234567890").and_return({"name" => "ACME Inc.", "address" => {"country" => "DE"}, "metadata" => {"tax_id" => "123123123"}}).at_least(:once)
bi = billing_record(Time.utc(2023, 6), Time.utc(2023, 7))
invoice = InvoiceGenerator.new(bi.span.begin, bi.span.end, save_result: true, eur_rate: 1.1).run.first
visit "#{project.path}/billing"
expect(page).to have_content "VAT subject to reverse charge."
click_link invoice.name
expect(page.status_code).to eq(200)
text = PDF::Reader.new(StringIO.new(page.body)).pages.map(&:text).join(" ")
expect(text).to include("Ubicloud B.V.")
expect(text).to include("test-vm")
expect(text).to include("VAT subject to reverse charge")
end
it "show finalized invoice as PDF with old issuer info" do
expect(Stripe::Customer).to receive(:retrieve).with("cs_1234567890").and_return({"name" => "John Doe", "address" => {"country" => "US"}, "metadata" => {"tax_id" => "123123123"}}).at_least(:once)
bi = billing_record(Time.utc(2023, 6), Time.utc(2023, 7))
invoice = InvoiceGenerator.new(bi.span.begin, bi.span.end, save_result: true, eur_rate: 1.1).run.first
invoice.content["issuer_info"].merge!("name" => nil, "tax_id" => "123123123", "in_eu_vat" => false)
invoice.modified!(:content)
invoice.save_changes
visit "#{project.path}/billing/invoice/#{invoice.ubid}"
expect(page.status_code).to eq(200)
text = PDF::Reader.new(StringIO.new(page.body)).pages.map(&:text).join(" ")
expect(text).to include("John Doe")
expect(text).to include("test-vm")
expect(text).to include("Tax ID: 123123123")
end
it "show persisted invoice PDF from blob storage" do
expect(Stripe::Customer).to receive(:retrieve).with("cs_1234567890").and_return({"name" => "John Doe", "address" => {"country" => "US"}, "metadata" => {"company_name" => "ACME Inc.", "tax_id" => "123123123"}}).at_least(:once)
bi = billing_record(Time.utc(2023, 6), Time.utc(2023, 7))
invoice = InvoiceGenerator.new(bi.span.begin, bi.span.end, save_result: true, eur_rate: 1.1).run.first
pdf = invoice.generate_pdf(Serializers::Invoice.serialize(invoice, {detailed: true}))
response = instance_double(Aws::S3::Types::GetObjectOutput, body: instance_double(StringIO, read: pdf))
expect(blob_storage_client).to receive(:get_object).with(bucket: "ubicloud-invoices", key: invoice.blob_key).and_return(response)
visit "#{project.path}/billing"
click_link invoice.name
expect(page.status_code).to eq(200)
text = PDF::Reader.new(StringIO.new(page.body)).pages.map(&:text).join(" ")
expect(text).to include("Ubicloud Inc.")
expect(text).to include("ACME Inc.")
end
it "raises not found when invoice not exists" do
visit "#{project.path}/billing/invoice/08s56d4kaj94xsmrnf5v5m3mav"
expect(page.title).to eq("Ubicloud - ResourceNotFound")
expect(page.status_code).to eq(404)
expect(page).to have_content "ResourceNotFound"
end
end
describe "usage alerts" do
before do
UsageAlert.create_with_id(project_id: project.id, user_id: user.id, name: "alert-1", limit: 101)
UsageAlert.create_with_id(project_id: project_wo_permissions.id, user_id: user.id, name: "alert-2", limit: 100)
end
it "can list usage alerts" do
visit "#{project.path}/billing"
expect(page).to have_content "alert-1"
expect(page).to have_no_content "alert-2"
end
it "can create usage alert" do
visit "#{project.path}/billing"
fill_in "alert_name", with: "alert-3"
fill_in "limit", with: 200
click_button "Add"
expect(page).to have_content "alert-3"
end
it "can delete usage alert" do
visit "#{project.path}/billing"
expect(page).to have_content "alert-1"
# We send delete request manually instead of just clicking to button because delete action triggered by JavaScript.
# UI tests run without a JavaScript engine.
btn = find "#alert-#{project.usage_alerts.first.ubid} .delete-btn"
page.driver.delete btn["data-url"], {_csrf: btn["data-csrf"]}
visit "#{project.path}/billing"
expect(page).to have_flash_notice "Usage alert alert-1 is deleted."
visit "#{project.path}/billing"
expect(page).to have_no_content "alert-1"
end
it "returns 404 if usage alert not found" do
visit project.path + "/billing"
expect(page).to have_content "alert-1"
btn = find "#alert-#{project.usage_alerts.first.ubid} .delete-btn"
project.usage_alerts.first.destroy
page.driver.delete btn["data-url"], {_csrf: btn["data-csrf"]}
expect(page.status_code).to eq(404)
end
end
end
end