DEV: Update linting (#189)

This commit is contained in:
Jarek Radosz 2024-01-16 17:51:44 +01:00 committed by GitHub
parent a3883d2a9a
commit d33cf2921e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
62 changed files with 2444 additions and 1941 deletions

View File

@ -1,6 +0,0 @@
{
"globals": {
"Stripe": true
},
"extends": "eslint-config-discourse"
}

1
.eslintrc.cjs Normal file
View File

@ -0,0 +1 @@
module.exports = require("@discourse/lint-configs/eslint");

View File

@ -1 +0,0 @@
{}

1
.prettierrc.cjs Normal file
View File

@ -0,0 +1 @@
module.exports = require("@discourse/lint-configs/prettier");

1
.template-lintrc.cjs Normal file
View File

@ -0,0 +1 @@
module.exports = require("@discourse/lint-configs/template-lint");

View File

@ -1,4 +0,0 @@
module.exports = {
plugins: ["ember-template-lint-plugin-discourse"],
extends: "discourse:recommended",
};

View File

@ -8,35 +8,45 @@ GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
ast (2.4.2) ast (2.4.2)
json (2.6.2) json (2.7.1)
parallel (1.22.1) language_server-protocol (3.17.0.3)
parser (3.1.2.1) parallel (1.24.0)
parser (3.3.0.4)
ast (~> 2.4.1) ast (~> 2.4.1)
prettier_print (1.2.0) racc
prettier_print (1.2.1)
racc (1.7.3)
rainbow (3.1.1) rainbow (3.1.1)
regexp_parser (2.6.0) regexp_parser (2.9.0)
rexml (3.2.5) rexml (3.2.6)
rubocop (1.36.0) rubocop (1.60.0)
json (~> 2.3) json (~> 2.3)
language_server-protocol (>= 3.17.0)
parallel (~> 1.10) parallel (~> 1.10)
parser (>= 3.1.2.1) parser (>= 3.3.0.2)
rainbow (>= 2.2.2, < 4.0) rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 1.8, < 3.0) regexp_parser (>= 1.8, < 3.0)
rexml (>= 3.2.5, < 4.0) rexml (>= 3.2.5, < 4.0)
rubocop-ast (>= 1.20.1, < 2.0) rubocop-ast (>= 1.30.0, < 2.0)
ruby-progressbar (~> 1.7) ruby-progressbar (~> 1.7)
unicode-display_width (>= 1.4.0, < 3.0) unicode-display_width (>= 2.4.0, < 3.0)
rubocop-ast (1.21.0) rubocop-ast (1.30.0)
parser (>= 3.1.1.0) parser (>= 3.2.1.0)
rubocop-discourse (3.0) rubocop-capybara (2.20.0)
rubocop (>= 1.1.0) rubocop (~> 1.41)
rubocop-rspec (>= 2.0.0) rubocop-discourse (3.6.0)
rubocop-rspec (2.13.2) rubocop (>= 1.59.0)
rubocop (~> 1.33) rubocop-rspec (>= 2.25.0)
ruby-progressbar (1.11.0) rubocop-factory_bot (2.25.1)
syntax_tree (5.1.0) rubocop (~> 1.41)
rubocop-rspec (2.26.1)
rubocop (~> 1.40)
rubocop-capybara (~> 2.17)
rubocop-factory_bot (~> 2.22)
ruby-progressbar (1.13.0)
syntax_tree (6.2.0)
prettier_print (>= 1.2.0) prettier_print (>= 1.2.0)
unicode-display_width (2.3.0) unicode-display_width (2.5.0)
PLATFORMS PLATFORMS
ruby ruby
@ -47,4 +57,4 @@ DEPENDENCIES
translations-manager! translations-manager!
BUNDLED WITH BUNDLED WITH
2.3.4 2.5.4

View File

@ -1,11 +1,11 @@
import { action } from "@ember/object";
import { ajax } from "discourse/lib/ajax";
import { equal } from "@ember/object/computed";
import { setting } from "discourse/lib/computed";
import Component from "@ember/component"; import Component from "@ember/component";
import discourseComputed, { observes } from "discourse-common/utils/decorators"; import { action } from "@ember/object";
import { equal } from "@ember/object/computed";
import { later } from "@ember/runloop"; import { later } from "@ember/runloop";
import { inject as service } from "@ember/service"; import { inject as service } from "@ember/service";
import { ajax } from "discourse/lib/ajax";
import { setting } from "discourse/lib/computed";
import discourseComputed, { observes } from "discourse-common/utils/decorators";
const SIDEBAR_BODY_CLASS = "subscription-campaign-sidebar"; const SIDEBAR_BODY_CLASS = "subscription-campaign-sidebar";
@ -96,6 +96,7 @@ export default Component.extend({
}, },
willDestroyElement() { willDestroyElement() {
this._super(...arguments);
document.body.classList.remove(SIDEBAR_BODY_CLASS); document.body.classList.remove(SIDEBAR_BODY_CLASS);
}, },

View File

@ -1,10 +1,10 @@
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { Input } from "@ember/component";
import { fn, hash } from "@ember/helper";
import DButton from "discourse/components/d-button"; import DButton from "discourse/components/d-button";
import DModal from "discourse/components/d-modal"; import DModal from "discourse/components/d-modal";
import Component from "@glimmer/component";
import { fn, hash } from "@ember/helper";
import i18n from "discourse-common/helpers/i18n"; import i18n from "discourse-common/helpers/i18n";
import { Input } from "@ember/component";
import { tracked } from "@glimmer/tracking";
export default class AdminCancelSubscription extends Component { export default class AdminCancelSubscription extends Component {
@tracked refund; @tracked refund;

View File

@ -1,6 +1,6 @@
import discourseComputed from "discourse-common/utils/decorators";
import { isEmpty } from "@ember/utils";
import Component from "@ember/component"; import Component from "@ember/component";
import { isEmpty } from "@ember/utils";
import discourseComputed from "discourse-common/utils/decorators";
export default Component.extend({ export default Component.extend({
classNames: ["product-list"], classNames: ["product-list"],

View File

@ -1,6 +1,6 @@
import ComboBoxComponent from "select-kit/components/combo-box";
import { computed } from "@ember/object"; import { computed } from "@ember/object";
import I18n from "I18n"; import I18n from "I18n";
import ComboBoxComponent from "select-kit/components/combo-box";
export default ComboBoxComponent.extend({ export default ComboBoxComponent.extend({
pluginApiIdentifiers: ["subscribe-ca-province-select"], pluginApiIdentifiers: ["subscribe-ca-province-select"],

View File

@ -24,5 +24,8 @@ export default Component.extend({
}, },
}); });
}, },
didDestroyElement() {},
didDestroyElement() {
this._super(...arguments);
},
}); });

View File

@ -1,6 +1,6 @@
import ComboBoxComponent from "select-kit/components/combo-box";
import { computed } from "@ember/object"; import { computed } from "@ember/object";
import I18n from "I18n"; import I18n from "I18n";
import ComboBoxComponent from "select-kit/components/combo-box";
export default ComboBoxComponent.extend({ export default ComboBoxComponent.extend({
pluginApiIdentifiers: ["subscribe-country-select"], pluginApiIdentifiers: ["subscribe-country-select"],

View File

@ -1,6 +1,6 @@
import ComboBoxComponent from "select-kit/components/combo-box";
import { computed } from "@ember/object"; import { computed } from "@ember/object";
import I18n from "I18n"; import I18n from "I18n";
import ComboBoxComponent from "select-kit/components/combo-box";
export default ComboBoxComponent.extend({ export default ComboBoxComponent.extend({
pluginApiIdentifiers: ["subscribe-us-state-select"], pluginApiIdentifiers: ["subscribe-us-state-select"],

View File

@ -1,6 +1,6 @@
import Controller from "@ember/controller"; import Controller from "@ember/controller";
import AdminCoupon from "discourse/plugins/discourse-subscriptions/discourse/models/admin-coupon";
import { popupAjaxError } from "discourse/lib/ajax-error"; import { popupAjaxError } from "discourse/lib/ajax-error";
import AdminCoupon from "discourse/plugins/discourse-subscriptions/discourse/models/admin-coupon";
export default Controller.extend({ export default Controller.extend({
creating: null, creating: null,

View File

@ -1,6 +1,6 @@
import Controller from "@ember/controller"; import Controller from "@ember/controller";
import { popupAjaxError } from "discourse/lib/ajax-error";
import { inject as service } from "@ember/service"; import { inject as service } from "@ember/service";
import { popupAjaxError } from "discourse/lib/ajax-error";
export default Controller.extend({ export default Controller.extend({
router: service(), router: service(),

View File

@ -1,8 +1,8 @@
import discourseComputed from "discourse-common/utils/decorators";
import DiscourseURL from "discourse/lib/url";
import Controller from "@ember/controller"; import Controller from "@ember/controller";
import { alias } from "@ember/object/computed"; import { alias } from "@ember/object/computed";
import { popupAjaxError } from "discourse/lib/ajax-error"; import { popupAjaxError } from "discourse/lib/ajax-error";
import DiscourseURL from "discourse/lib/url";
import discourseComputed from "discourse-common/utils/decorators";
const RECURRING = "recurring"; const RECURRING = "recurring";
const ONE_TIME = "one_time"; const ONE_TIME = "one_time";

View File

@ -1,6 +1,6 @@
import { popupAjaxError } from "discourse/lib/ajax-error";
import Controller from "@ember/controller"; import Controller from "@ember/controller";
import { inject as service } from "@ember/service"; import { inject as service } from "@ember/service";
import { popupAjaxError } from "discourse/lib/ajax-error";
export default Controller.extend({ export default Controller.extend({
router: service(), router: service(),

View File

@ -1,9 +1,9 @@
import AdminCancelSubscription from "../components/modal/admin-cancel-subscription";
import AdminSubscription from "../models/admin-subscription";
import Controller from "@ember/controller"; import Controller from "@ember/controller";
import { action } from "@ember/object"; import { action } from "@ember/object";
import { inject as service } from "@ember/service"; import { inject as service } from "@ember/service";
import I18n from "discourse-i18n"; import I18n from "discourse-i18n";
import AdminCancelSubscription from "../components/modal/admin-cancel-subscription";
import AdminSubscription from "../models/admin-subscription";
export default Controller.extend({ export default Controller.extend({
modal: service(), modal: service(),

View File

@ -1,11 +1,11 @@
import { action } from "@ember/object";
import { ajax } from "discourse/lib/ajax";
import { popupAjaxError } from "discourse/lib/ajax-error";
import Controller from "@ember/controller"; import Controller from "@ember/controller";
import discourseComputed from "discourse-common/utils/decorators"; import { action } from "@ember/object";
import I18n from "I18n";
import { inject as service } from "@ember/service"; import { inject as service } from "@ember/service";
import { htmlSafe } from "@ember/template"; import { htmlSafe } from "@ember/template";
import { ajax } from "discourse/lib/ajax";
import { popupAjaxError } from "discourse/lib/ajax-error";
import discourseComputed from "discourse-common/utils/decorators";
import I18n from "I18n";
export default Controller.extend({ export default Controller.extend({
loading: false, loading: false,

View File

@ -1,6 +1,6 @@
import Controller from "@ember/controller"; import Controller from "@ember/controller";
import discourseComputed from "discourse-common/utils/decorators";
import User from "discourse/models/user"; import User from "discourse/models/user";
import discourseComputed from "discourse-common/utils/decorators";
export default Controller.extend({ export default Controller.extend({
@discourseComputed() @discourseComputed()

View File

@ -1,10 +1,11 @@
/* global Stripe */
import Controller from "@ember/controller"; import Controller from "@ember/controller";
import { not } from "@ember/object/computed";
import { inject as service } from "@ember/service";
import discourseComputed from "discourse-common/utils/decorators";
import I18n from "I18n";
import Subscription from "discourse/plugins/discourse-subscriptions/discourse/models/subscription"; import Subscription from "discourse/plugins/discourse-subscriptions/discourse/models/subscription";
import Transaction from "discourse/plugins/discourse-subscriptions/discourse/models/transaction"; import Transaction from "discourse/plugins/discourse-subscriptions/discourse/models/transaction";
import I18n from "I18n";
import { not } from "@ember/object/computed";
import discourseComputed from "discourse-common/utils/decorators";
import { inject as service } from "@ember/service";
export default Controller.extend({ export default Controller.extend({
dialog: service(), dialog: service(),

View File

@ -1,14 +1,17 @@
/* global Stripe */
import Controller from "@ember/controller"; import Controller from "@ember/controller";
import { action } from "@ember/object"; import { action } from "@ember/object";
import { inject as service } from "@ember/service";
import { ajax } from "discourse/lib/ajax"; import { ajax } from "discourse/lib/ajax";
import { popupAjaxError } from "discourse/lib/ajax-error"; import { popupAjaxError } from "discourse/lib/ajax-error";
import I18n from "I18n"; import I18n from "I18n";
import { inject as service } from "@ember/service";
export default Controller.extend({ export default Controller.extend({
dialog: service(), dialog: service(),
loading: false, loading: false,
saved: false, saved: false,
init() { init() {
this._super(...arguments); this._super(...arguments);
this.set( this.set(

View File

@ -1,5 +1,5 @@
import { autoUpdatingRelativeAge } from "discourse/lib/formatter";
import { htmlSafe } from "@ember/template"; import { htmlSafe } from "@ember/template";
import { autoUpdatingRelativeAge } from "discourse/lib/formatter";
export default function formatUnixDate(timestamp) { export default function formatUnixDate(timestamp) {
if (timestamp) { if (timestamp) {

View File

@ -5,7 +5,7 @@ export default {
name: "setup-subscriptions", name: "setup-subscriptions",
initialize(container) { initialize(container) {
withPluginApi("0.8.11", (api) => { withPluginApi("0.8.11", (api) => {
const siteSettings = container.lookup("site-settings:main"); const siteSettings = container.lookup("service:site-settings");
const isNavLinkEnabled = const isNavLinkEnabled =
siteSettings.discourse_subscriptions_extra_nav_subscribe; siteSettings.discourse_subscriptions_extra_nav_subscribe;
if (isNavLinkEnabled) { if (isNavLinkEnabled) {

View File

@ -1,5 +1,5 @@
import { ajax } from "discourse/lib/ajax";
import EmberObject from "@ember/object"; import EmberObject from "@ember/object";
import { ajax } from "discourse/lib/ajax";
import discourseComputed from "discourse-common/utils/decorators"; import discourseComputed from "discourse-common/utils/decorators";
const AdminCoupon = EmberObject.extend({ const AdminCoupon = EmberObject.extend({

View File

@ -1,6 +1,6 @@
import Plan from "discourse/plugins/discourse-subscriptions/discourse/models/plan";
import discourseComputed from "discourse-common/utils/decorators";
import { ajax } from "discourse/lib/ajax"; import { ajax } from "discourse/lib/ajax";
import discourseComputed from "discourse-common/utils/decorators";
import Plan from "discourse/plugins/discourse-subscriptions/discourse/models/plan";
const AdminPlan = Plan.extend({ const AdminPlan = Plan.extend({
isNew: false, isNew: false,

View File

@ -1,5 +1,5 @@
import { ajax } from "discourse/lib/ajax";
import EmberObject from "@ember/object"; import EmberObject from "@ember/object";
import { ajax } from "discourse/lib/ajax";
const AdminProduct = EmberObject.extend({ const AdminProduct = EmberObject.extend({
isNew: false, isNew: false,

View File

@ -1,7 +1,7 @@
import discourseComputed from "discourse-common/utils/decorators";
import { ajax } from "discourse/lib/ajax";
import EmberObject from "@ember/object"; import EmberObject from "@ember/object";
import { ajax } from "discourse/lib/ajax";
import getURL from "discourse-common/lib/get-url"; import getURL from "discourse-common/lib/get-url";
import discourseComputed from "discourse-common/utils/decorators";
const AdminSubscription = EmberObject.extend({ const AdminSubscription = EmberObject.extend({
@discourseComputed("status") @discourseComputed("status")

View File

@ -1,6 +1,6 @@
import discourseComputed from "discourse-common/utils/decorators";
import { ajax } from "discourse/lib/ajax";
import EmberObject from "@ember/object"; import EmberObject from "@ember/object";
import { ajax } from "discourse/lib/ajax";
import discourseComputed from "discourse-common/utils/decorators";
const Subscription = EmberObject.extend({ const Subscription = EmberObject.extend({
@discourseComputed("status") @discourseComputed("status")

View File

@ -1,6 +1,6 @@
import EmberObject from "@ember/object"; import EmberObject from "@ember/object";
import discourseComputed from "discourse-common/utils/decorators";
import { ajax } from "discourse/lib/ajax"; import { ajax } from "discourse/lib/ajax";
import discourseComputed from "discourse-common/utils/decorators";
const UserPayment = EmberObject.extend({ const UserPayment = EmberObject.extend({
@discourseComputed("amount") @discourseComputed("amount")

View File

@ -1,8 +1,8 @@
import EmberObject from "@ember/object"; import EmberObject from "@ember/object";
import discourseComputed from "discourse-common/utils/decorators";
import { ajax } from "discourse/lib/ajax"; import { ajax } from "discourse/lib/ajax";
import Plan from "discourse/plugins/discourse-subscriptions/discourse/models/plan"; import discourseComputed from "discourse-common/utils/decorators";
import I18n from "I18n"; import I18n from "I18n";
import Plan from "discourse/plugins/discourse-subscriptions/discourse/models/plan";
const UserSubscription = EmberObject.extend({ const UserSubscription = EmberObject.extend({
@discourseComputed("status") @discourseComputed("status")

View File

@ -1,6 +1,6 @@
import { action } from "@ember/object";
import Route from "@ember/routing/route"; import Route from "@ember/routing/route";
import AdminCoupon from "discourse/plugins/discourse-subscriptions/discourse/models/admin-coupon"; import AdminCoupon from "discourse/plugins/discourse-subscriptions/discourse/models/admin-coupon";
import { action } from "@ember/object";
export default Route.extend({ export default Route.extend({
model() { model() {

View File

@ -1,8 +1,8 @@
import Route from "@ember/routing/route";
import AdminProduct from "discourse/plugins/discourse-subscriptions/discourse/models/admin-product";
import I18n from "I18n";
import { action } from "@ember/object"; import { action } from "@ember/object";
import Route from "@ember/routing/route";
import { inject as service } from "@ember/service"; import { inject as service } from "@ember/service";
import I18n from "I18n";
import AdminProduct from "discourse/plugins/discourse-subscriptions/discourse/models/admin-product";
export default Route.extend({ export default Route.extend({
dialog: service(), dialog: service(),

View File

@ -1,7 +1,7 @@
import Route from "@ember/routing/route"; import Route from "@ember/routing/route";
import AdminPlan from "discourse/plugins/discourse-subscriptions/discourse/models/admin-plan";
import Group from "discourse/models/group";
import { hash } from "rsvp"; import { hash } from "rsvp";
import Group from "discourse/models/group";
import AdminPlan from "discourse/plugins/discourse-subscriptions/discourse/models/admin-plan";
export default Route.extend({ export default Route.extend({
model(params) { model(params) {

View File

@ -1,10 +1,10 @@
import Route from "@ember/routing/route";
import AdminProduct from "discourse/plugins/discourse-subscriptions/discourse/models/admin-product";
import AdminPlan from "discourse/plugins/discourse-subscriptions/discourse/models/admin-plan";
import I18n from "I18n";
import { hash } from "rsvp";
import { action } from "@ember/object"; import { action } from "@ember/object";
import Route from "@ember/routing/route";
import { inject as service } from "@ember/service"; import { inject as service } from "@ember/service";
import { hash } from "rsvp";
import I18n from "I18n";
import AdminPlan from "discourse/plugins/discourse-subscriptions/discourse/models/admin-plan";
import AdminProduct from "discourse/plugins/discourse-subscriptions/discourse/models/admin-product";
export default Route.extend({ export default Route.extend({
dialog: service(), dialog: service(),

View File

@ -1,5 +1,5 @@
import Route from "@ember/routing/route";
import { action } from "@ember/object"; import { action } from "@ember/object";
import Route from "@ember/routing/route";
import { inject as service } from "@ember/service"; import { inject as service } from "@ember/service";
export default Route.extend({ export default Route.extend({

View File

@ -1,6 +1,6 @@
import Route from "@ember/routing/route"; import Route from "@ember/routing/route";
import Product from "discourse/plugins/discourse-subscriptions/discourse/models/product";
import { inject as service } from "@ember/service"; import { inject as service } from "@ember/service";
import Product from "discourse/plugins/discourse-subscriptions/discourse/models/product";
export default Route.extend({ export default Route.extend({
router: service(), router: service(),

View File

@ -1,6 +1,6 @@
import Route from "@ember/routing/route"; import Route from "@ember/routing/route";
import Product from "discourse/plugins/discourse-subscriptions/discourse/models/product";
import Plan from "discourse/plugins/discourse-subscriptions/discourse/models/plan"; import Plan from "discourse/plugins/discourse-subscriptions/discourse/models/plan";
import Product from "discourse/plugins/discourse-subscriptions/discourse/models/product";
import Subscription from "discourse/plugins/discourse-subscriptions/discourse/models/subscription"; import Subscription from "discourse/plugins/discourse-subscriptions/discourse/models/subscription";
export default Route.extend({ export default Route.extend({

View File

@ -1,8 +1,8 @@
import Route from "@ember/routing/route";
import UserSubscription from "discourse/plugins/discourse-subscriptions/discourse/models/user-subscription";
import I18n from "I18n";
import { action } from "@ember/object"; import { action } from "@ember/object";
import Route from "@ember/routing/route";
import { inject as service } from "@ember/service"; import { inject as service } from "@ember/service";
import I18n from "I18n";
import UserSubscription from "discourse/plugins/discourse-subscriptions/discourse/models/user-subscription";
export default Route.extend({ export default Route.extend({
dialog: service(), dialog: service(),

View File

@ -1,11 +1,14 @@
import Route from "@ember/routing/route"; import Route from "@ember/routing/route";
import { inject as service } from "@ember/service";
export default Route.extend({ export default Route.extend({
router: service(),
templateName: "user/billing", templateName: "user/billing",
setupController(controller, model) { setupController(controller, model) {
if (this.currentUser.id !== this.modelFor("user").id) { if (this.currentUser.id !== this.modelFor("user").id) {
this.replaceWith("userActivity"); this.router.replaceWith("userActivity");
} else { } else {
controller.setProperties({ model }); controller.setProperties({ model });
} }

View File

@ -1,12 +1,10 @@
<DSection {{body-class "user-billing-page"}}
@pageClass="user-billing"
@class="user-secondary-navigation" <section class="user-secondary-navigation">
@scrollTop="false"
>
<MobileNav <MobileNav
@class="activity-nav"
@desktopClass="action-list nav-stacked" @desktopClass="action-list nav-stacked"
@currentPath={{router._router.currentPath}} @currentPath={{router._router.currentPath}}
class="activity-nav"
> >
<li> <li>
<LinkTo @route="user.billing.subscriptions"> <LinkTo @route="user.billing.subscriptions">
@ -20,7 +18,7 @@
</LinkTo> </LinkTo>
</li> </li>
</MobileNav> </MobileNav>
</DSection> </section>
<section class="user-content"> <section class="user-content">
{{outlet}} {{outlet}}

View File

@ -90,14 +90,14 @@ def import_subscriptions(procourse_import)
product_ids = DiscourseSubscriptions::Product.all.pluck(:external_id) product_ids = DiscourseSubscriptions::Product.all.pluck(:external_id)
all_customers = get_stripe_customers all_customers = get_stripe_customers
puts "Total available Stripe Customers: #{all_customers.length.to_s}, the first of which is customer id: #{all_customers[0][:description]}" puts "Total available Stripe Customers: #{all_customers.length}, the first of which is customer id: #{all_customers[0][:description]}"
subscriptions = get_stripe_subscriptions subscriptions = get_stripe_subscriptions
puts "Total Active Subscriptions available: #{subscriptions.length.to_s}" puts "Total Active Subscriptions available: #{subscriptions.length}"
subscriptions_for_products = subscriptions_for_products =
subscriptions.select { |sub| product_ids.include?(sub[:items][:data][0][:price][:product]) } subscriptions.select { |sub| product_ids.include?(sub[:items][:data][0][:price][:product]) }
puts "Total Subscriptions matching Products to Import: #{subscriptions_for_products.length.to_s}" puts "Total Subscriptions matching Products to Import: #{subscriptions_for_products.length}"
subscriptions_for_products.each do |subscription| subscriptions_for_products.each do |subscription|
product_id = subscription[:items][:data][0][:plan][:product] product_id = subscription[:items][:data][0][:plan][:product]

View File

@ -1,10 +1,10 @@
{ {
"name": "discourse-subscriptions", "name": "discourse-subscriptions",
"version": "1.0.0", "private": true,
"repository": "https://github.com/discourse/discourse-subscriptions",
"author": "Discourse",
"license": "MIT",
"devDependencies": { "devDependencies": {
"eslint-config-discourse": "^3.4.0" "@discourse/lint-configs": "^1.3.5",
"ember-template-lint": "^5.13.0",
"eslint": "^8.56.0",
"prettier": "^2.8.8"
} }
} }

View File

@ -6,7 +6,6 @@
# version: 2.8.1 # version: 2.8.1
# url: https://github.com/discourse/discourse-subscriptions # url: https://github.com/discourse/discourse-subscriptions
# authors: Rimian Perkins, Justin DiRose # authors: Rimian Perkins, Justin DiRose
# transpile_js: true
enabled_site_setting :discourse_subscriptions_enabled enabled_site_setting :discourse_subscriptions_enabled

View File

@ -2,25 +2,23 @@
require "rails_helper" require "rails_helper"
module DiscourseSubscriptions RSpec.describe DiscourseSubscriptions::Customer do
RSpec.describe Customer do let(:user) { Fabricate(:user) }
let(:user) { Fabricate(:user) } let(:stripe_customer) { { id: "cus_id4567" } }
let(:stripe_customer) { { id: "cus_id4567" } }
it "has a table name" do it "has a table name" do
expect(described_class.table_name).to eq "discourse_subscriptions_customers" expect(described_class.table_name).to eq "discourse_subscriptions_customers"
end end
it "creates" do it "creates" do
customer = described_class.create_customer(user, stripe_customer) customer = described_class.create_customer(user, stripe_customer)
expect(customer.customer_id).to eq "cus_id4567" expect(customer.customer_id).to eq "cus_id4567"
expect(customer.user_id).to eq user.id expect(customer.user_id).to eq user.id
end end
it "has a user scope" do it "has a user scope" do
described_class.create_customer(user, stripe_customer) described_class.create_customer(user, stripe_customer)
customer = described_class.find_user(user) customer = described_class.find_user(user)
expect(customer.customer_id).to eq "cus_id4567" expect(customer.customer_id).to eq "cus_id4567"
end
end end
end end

View File

@ -2,87 +2,83 @@
require "rails_helper" require "rails_helper"
module DiscourseSubscriptions RSpec.describe DiscourseSubscriptions::Admin::CouponsController do
RSpec.describe Admin::CouponsController do it "is a subclass of AdminController" do
it "is a subclass of AdminController" do expect(DiscourseSubscriptions::Admin::CouponsController < ::Admin::AdminController).to eq(true)
expect(DiscourseSubscriptions::Admin::CouponsController < ::Admin::AdminController).to eq( end
true,
) context "when unauthenticated" do
end it "does nothing" do
::Stripe::PromotionCode.expects(:list).never
get "/s/admin/coupons.json"
expect(response.status).to eq(404)
end
end
context "when authenticated" do
let(:admin) { Fabricate(:admin) }
before { sign_in(admin) }
describe "#index" do
it "returns a list of promo codes" do
::Stripe::PromotionCode
.expects(:list)
.with({ limit: 100 })
.returns({ data: [{ id: "promo_123", coupon: { valid: true } }] })
context "when unauthenticated" do
it "does nothing" do
::Stripe::PromotionCode.expects(:list).never
get "/s/admin/coupons.json" get "/s/admin/coupons.json"
expect(response.status).to eq(404) expect(response.status).to eq(200)
expect(response.parsed_body[0]["id"]).to eq("promo_123")
end
it "only returns valid promo codes" do
::Stripe::PromotionCode
.expects(:list)
.with({ limit: 100 })
.returns({ data: [{ id: "promo_123", coupon: { valid: false } }] })
get "/s/admin/coupons.json"
expect(response.status).to eq(200)
expect(response.parsed_body).to be_blank
end end
end end
context "when authenticated" do describe "#create" do
let(:admin) { Fabricate(:admin) } it "creates a coupon with an amount off" do
::Stripe::Coupon.expects(:create).returns(id: "coup_123")
::Stripe::PromotionCode.expects(:create).returns(
{ code: "p123", coupon: { amount_off: 2000 } },
)
before { sign_in(admin) } post "/s/admin/coupons.json",
params: {
describe "#index" do promo: "p123",
it "returns a list of promo codes" do discount_type: "amount",
::Stripe::PromotionCode discount: "2000",
.expects(:list) active: true,
.with({ limit: 100 }) }
.returns({ data: [{ id: "promo_123", coupon: { valid: true } }] }) expect(response.status).to eq(200)
expect(response.parsed_body["code"]).to eq("p123")
get "/s/admin/coupons.json" expect(response.parsed_body["coupon"]["amount_off"]).to eq(2000)
expect(response.status).to eq(200)
expect(response.parsed_body[0]["id"]).to eq("promo_123")
end
it "only returns valid promo codes" do
::Stripe::PromotionCode
.expects(:list)
.with({ limit: 100 })
.returns({ data: [{ id: "promo_123", coupon: { valid: false } }] })
get "/s/admin/coupons.json"
expect(response.status).to eq(200)
expect(response.parsed_body).to be_blank
end
end end
describe "#create" do it "creates a coupon with a percent off" do
it "creates a coupon with an amount off" do ::Stripe::Coupon.expects(:create).returns(id: "coup_123")
::Stripe::Coupon.expects(:create).returns(id: "coup_123") ::Stripe::PromotionCode.expects(:create).returns(
::Stripe::PromotionCode.expects(:create).returns( { code: "p123", coupon: { percent_off: 20 } },
{ code: "p123", coupon: { amount_off: 2000 } }, )
)
post "/s/admin/coupons.json", post "/s/admin/coupons.json",
params: { params: {
promo: "p123", promo: "p123",
discount_type: "amount", discount_type: "percent",
discount: "2000", discount: "20",
active: true, active: true,
} }
expect(response.status).to eq(200) expect(response.status).to eq(200)
expect(response.parsed_body["code"]).to eq("p123") expect(response.parsed_body["code"]).to eq("p123")
expect(response.parsed_body["coupon"]["amount_off"]).to eq(2000) expect(response.parsed_body["coupon"]["percent_off"]).to eq(20)
end
it "creates a coupon with a percent off" do
::Stripe::Coupon.expects(:create).returns(id: "coup_123")
::Stripe::PromotionCode.expects(:create).returns(
{ code: "p123", coupon: { percent_off: 20 } },
)
post "/s/admin/coupons.json",
params: {
promo: "p123",
discount_type: "percent",
discount: "20",
active: true,
}
expect(response.status).to eq(200)
expect(response.parsed_body["code"]).to eq("p123")
expect(response.parsed_body["coupon"]["percent_off"]).to eq(20)
end
end end
end end
end end

View File

@ -2,172 +2,166 @@
require "rails_helper" require "rails_helper"
module DiscourseSubscriptions RSpec.describe DiscourseSubscriptions::Admin::PlansController do
module Admin it "is a subclass of AdminController" do
RSpec.describe PlansController do expect(DiscourseSubscriptions::Admin::PlansController < ::Admin::AdminController).to eq(true)
it "is a subclass of AdminController" do end
expect(DiscourseSubscriptions::Admin::PlansController < ::Admin::AdminController).to eq(
true, context "when not authenticated" do
) describe "index" do
it "does not get the plans" do
::Stripe::Price.expects(:list).never
get "/s/admin/plans.json"
end end
context "when not authenticated" do it "not ok" do
describe "index" do get "/s/admin/plans.json"
it "does not get the plans" do expect(response.status).to eq 404
::Stripe::Price.expects(:list).never end
get "/s/admin/plans.json" end
end
it "not ok" do describe "create" do
get "/s/admin/plans.json" it "does not create a plan" do
expect(response.status).to eq 404 ::Stripe::Price.expects(:create).never
end post "/s/admin/plans.json", params: { name: "Rick Astley", amount: 1, interval: "week" }
end
describe "create" do
it "does not create a plan" do
::Stripe::Price.expects(:create).never
post "/s/admin/plans.json", params: { name: "Rick Astley", amount: 1, interval: "week" }
end
it "is not ok" do
post "/s/admin/plans.json", params: { name: "Rick Astley", amount: 1, interval: "week" }
expect(response.status).to eq 404
end
end
describe "show" do
it "does not show the plan" do
::Stripe::Price.expects(:retrieve).never
get "/s/admin/plans/plan_12345.json"
end
it "is not ok" do
get "/s/admin/plans/plan_12345.json"
expect(response.status).to eq 404
end
end
describe "update" do
it "does not update a plan" do
::Stripe::Price.expects(:update).never
delete "/s/admin/plans/plan_12345.json"
end
end
end end
context "when authenticated" do it "is not ok" do
let(:admin) { Fabricate(:admin) } post "/s/admin/plans.json", params: { name: "Rick Astley", amount: 1, interval: "week" }
expect(response.status).to eq 404
end
end
before { sign_in(admin) } describe "show" do
it "does not show the plan" do
::Stripe::Price.expects(:retrieve).never
get "/s/admin/plans/plan_12345.json"
end
describe "index" do it "is not ok" do
it "lists the plans" do get "/s/admin/plans/plan_12345.json"
::Stripe::Price.expects(:list).with(nil) expect(response.status).to eq 404
get "/s/admin/plans.json" end
end end
it "lists the plans for the product" do describe "update" do
::Stripe::Price.expects(:list).with({ product: "prod_id123" }) it "does not update a plan" do
get "/s/admin/plans.json", params: { product_id: "prod_id123" } ::Stripe::Price.expects(:update).never
end delete "/s/admin/plans/plan_12345.json"
end end
end
end
describe "show" do context "when authenticated" do
it "shows a plan" do let(:admin) { Fabricate(:admin) }
::Stripe::Price.expects(:retrieve).with("plan_12345").returns(currency: "aud")
get "/s/admin/plans/plan_12345.json"
expect(response.status).to eq 200
end
it "upcases the currency" do before { sign_in(admin) }
::Stripe::Price
.expects(:retrieve)
.with("plan_12345")
.returns(currency: "aud", recurring: { interval: "year" })
get "/s/admin/plans/plan_12345.json"
plan = response.parsed_body describe "index" do
expect(plan["currency"]).to eq "AUD" it "lists the plans" do
expect(plan["interval"]).to eq "year" ::Stripe::Price.expects(:list).with(nil)
end get "/s/admin/plans.json"
end end
describe "create" do it "lists the plans for the product" do
it "creates a plan with a nickname" do ::Stripe::Price.expects(:list).with({ product: "prod_id123" })
::Stripe::Price.expects(:create).with(has_entry(:nickname, "Veg")) get "/s/admin/plans.json", params: { product_id: "prod_id123" }
post "/s/admin/plans.json", params: { nickname: "Veg", metadata: { group_name: "" } } end
end end
it "creates a plan with a currency" do describe "show" do
::Stripe::Price.expects(:create).with(has_entry(:currency, "AUD")) it "shows a plan" do
post "/s/admin/plans.json", params: { currency: "AUD", metadata: { group_name: "" } } ::Stripe::Price.expects(:retrieve).with("plan_12345").returns(currency: "aud")
end get "/s/admin/plans/plan_12345.json"
expect(response.status).to eq 200
end
it "creates a plan with an interval" do it "upcases the currency" do
::Stripe::Price.expects(:create).with(has_entry(recurring: { interval: "week" })) ::Stripe::Price
post "/s/admin/plans.json", .expects(:retrieve)
params: { .with("plan_12345")
type: "recurring", .returns(currency: "aud", recurring: { interval: "year" })
interval: "week", get "/s/admin/plans/plan_12345.json"
metadata: {
group_name: "",
},
}
end
it "creates a plan as a one-time purchase" do plan = response.parsed_body
::Stripe::Price.expects(:create).with(Not(has_key(:recurring))) expect(plan["currency"]).to eq "AUD"
post "/s/admin/plans.json", params: { metadata: { group_name: "" } } expect(plan["interval"]).to eq "year"
end end
end
it "creates a plan with an amount" do describe "create" do
::Stripe::Price.expects(:create).with(has_entry(:unit_amount, "102")) it "creates a plan with a nickname" do
post "/s/admin/plans.json", params: { amount: "102", metadata: { group_name: "" } } ::Stripe::Price.expects(:create).with(has_entry(:nickname, "Veg"))
end post "/s/admin/plans.json", params: { nickname: "Veg", metadata: { group_name: "" } }
end
it "creates a plan with a product" do it "creates a plan with a currency" do
::Stripe::Price.expects(:create).with(has_entry(product: "prod_walterwhite")) ::Stripe::Price.expects(:create).with(has_entry(:currency, "AUD"))
post "/s/admin/plans.json", post "/s/admin/plans.json", params: { currency: "AUD", metadata: { group_name: "" } }
params: { end
product: "prod_walterwhite",
metadata: {
group_name: "",
},
}
end
it "creates a plan with an active status" do it "creates a plan with an interval" do
::Stripe::Price.expects(:create).with(has_entry(:active, "false")) ::Stripe::Price.expects(:create).with(has_entry(recurring: { interval: "week" }))
post "/s/admin/plans.json", params: { active: "false", metadata: { group_name: "" } } post "/s/admin/plans.json",
end params: {
type: "recurring",
interval: "week",
metadata: {
group_name: "",
},
}
end
# TODO: Need to fix the metadata tests it "creates a plan as a one-time purchase" do
# I think mocha has issues with the metadata fields here. ::Stripe::Price.expects(:create).with(Not(has_key(:recurring)))
post "/s/admin/plans.json", params: { metadata: { group_name: "" } }
end
#it 'has metadata' do it "creates a plan with an amount" do
# ::Stripe::Price.expects(:create).with(has_entry(:group_name, "discourse-user-group-name")) ::Stripe::Price.expects(:create).with(has_entry(:unit_amount, "102"))
# post "/s/admin/plans.json", params: { amount: "100", metadata: { group_name: 'discourse-user-group-name' } } post "/s/admin/plans.json", params: { amount: "102", metadata: { group_name: "" } }
#end end
#it "creates a plan with a trial period" do it "creates a plan with a product" do
# ::Stripe::Price.expects(:create).with(has_entry(trial_period_days: '14')) ::Stripe::Price.expects(:create).with(has_entry(product: "prod_walterwhite"))
# post "/s/admin/plans.json", params: { trial_period_days: '14' } post "/s/admin/plans.json",
#end params: {
end product: "prod_walterwhite",
metadata: {
group_name: "",
},
}
end
describe "update" do it "creates a plan with an active status" do
it "updates a plan" do ::Stripe::Price.expects(:create).with(has_entry(:active, "false"))
::Stripe::Price.expects(:update) post "/s/admin/plans.json", params: { active: "false", metadata: { group_name: "" } }
patch "/s/admin/plans/plan_12345.json", end
params: {
trial_period_days: "14", # TODO: Need to fix the metadata tests
metadata: { # I think mocha has issues with the metadata fields here.
group_name: "discourse-user-group-name",
}, #it 'has metadata' do
} # ::Stripe::Price.expects(:create).with(has_entry(:group_name, "discourse-user-group-name"))
end # post "/s/admin/plans.json", params: { amount: "100", metadata: { group_name: 'discourse-user-group-name' } }
end #end
#it "creates a plan with a trial period" do
# ::Stripe::Price.expects(:create).with(has_entry(trial_period_days: '14'))
# post "/s/admin/plans.json", params: { trial_period_days: '14' }
#end
end
describe "update" do
it "updates a plan" do
::Stripe::Price.expects(:update)
patch "/s/admin/plans/plan_12345.json",
params: {
trial_period_days: "14",
metadata: {
group_name: "discourse-user-group-name",
},
}
end end
end end
end end

View File

@ -2,132 +2,126 @@
require "rails_helper" require "rails_helper"
module DiscourseSubscriptions RSpec.describe DiscourseSubscriptions::Admin::ProductsController do
module Admin it "is a subclass of AdminController" do
RSpec.describe ProductsController do expect(DiscourseSubscriptions::Admin::ProductsController < ::Admin::AdminController).to eq(true)
it "is a subclass of AdminController" do end
expect(DiscourseSubscriptions::Admin::ProductsController < ::Admin::AdminController).to eq(
true, context "when unauthenticated" do
it "does not list the products" do
::Stripe::Product.expects(:list).never
get "/s/admin/products.json"
expect(response.status).to eq(404)
end
it "does not create the product" do
::Stripe::Product.expects(:create).never
post "/s/admin/products.json"
expect(response.status).to eq(404)
end
it "does not show the product" do
::Stripe::Product.expects(:retrieve).never
get "/s/admin/products/prod_qwerty123.json"
expect(response.status).to eq(404)
end
it "does not update the product" do
::Stripe::Product.expects(:update).never
put "/s/admin/products/prod_qwerty123.json"
expect(response.status).to eq(404)
end
it "does not delete the product" do
::Stripe::Product.expects(:delete).never
delete "/s/admin/products/u2.json"
expect(response.status).to eq(404)
end
end
context "when authenticated" do
let(:admin) { Fabricate(:admin) }
before { sign_in(admin) }
describe "index" do
it "gets the empty products" do
SiteSetting.discourse_subscriptions_public_key = "public-key"
SiteSetting.discourse_subscriptions_secret_key = "secret-key"
get "/s/admin/products.json"
expect(response.parsed_body).to be_empty
end
end
describe "create" do
it "is of product type service" do
::Stripe::Product.expects(:create).with(has_entry(:type, "service"))
post "/s/admin/products.json", params: {}
end
it "has a name" do
::Stripe::Product.expects(:create).with(has_entry(:name, "Jesse Pinkman"))
post "/s/admin/products.json", params: { name: "Jesse Pinkman" }
end
it "has an active attribute" do
::Stripe::Product.expects(:create).with(has_entry(active: "false"))
post "/s/admin/products.json", params: { active: "false" }
end
it "has a statement descriptor" do
::Stripe::Product.expects(:create).with(
has_entry(statement_descriptor: "Blessed are the cheesemakers"),
) )
post "/s/admin/products.json",
params: {
statement_descriptor: "Blessed are the cheesemakers",
}
end end
context "when unauthenticated" do it "has no statement descriptor if empty" do
it "does not list the products" do ::Stripe::Product.expects(:create).with(has_key(:statement_descriptor)).never
::Stripe::Product.expects(:list).never post "/s/admin/products.json", params: { statement_descriptor: "" }
get "/s/admin/products.json"
expect(response.status).to eq(404)
end
it "does not create the product" do
::Stripe::Product.expects(:create).never
post "/s/admin/products.json"
expect(response.status).to eq(404)
end
it "does not show the product" do
::Stripe::Product.expects(:retrieve).never
get "/s/admin/products/prod_qwerty123.json"
expect(response.status).to eq(404)
end
it "does not update the product" do
::Stripe::Product.expects(:update).never
put "/s/admin/products/prod_qwerty123.json"
expect(response.status).to eq(404)
end
it "does not delete the product" do
::Stripe::Product.expects(:delete).never
delete "/s/admin/products/u2.json"
expect(response.status).to eq(404)
end
end end
context "when authenticated" do it "has metadata" do
let(:admin) { Fabricate(:admin) } ::Stripe::Product.expects(:create).with(
has_entry(
metadata: {
description: "Oi, I think he just said bless be all the bignoses!",
repurchaseable: "false",
},
),
)
before { sign_in(admin) } post "/s/admin/products.json",
params: {
metadata: {
description: "Oi, I think he just said bless be all the bignoses!",
repurchaseable: "false",
},
}
end
end
describe "index" do describe "show" do
it "gets the empty products" do it "retrieves the product" do
SiteSetting.discourse_subscriptions_public_key = "public-key" ::Stripe::Product.expects(:retrieve).with("prod_walterwhite")
SiteSetting.discourse_subscriptions_secret_key = "secret-key" get "/s/admin/products/prod_walterwhite.json"
get "/s/admin/products.json" end
expect(response.parsed_body).to be_empty end
end
end
describe "create" do describe "update" do
it "is of product type service" do it "updates the product" do
::Stripe::Product.expects(:create).with(has_entry(:type, "service")) ::Stripe::Product.expects(:update)
post "/s/admin/products.json", params: {} patch "/s/admin/products/prod_walterwhite.json", params: {}
end end
end
it "has a name" do describe "delete" do
::Stripe::Product.expects(:create).with(has_entry(:name, "Jesse Pinkman")) it "deletes the product" do
post "/s/admin/products.json", params: { name: "Jesse Pinkman" } ::Stripe::Product.expects(:delete).with("prod_walterwhite")
end delete "/s/admin/products/prod_walterwhite.json"
it "has an active attribute" do
::Stripe::Product.expects(:create).with(has_entry(active: "false"))
post "/s/admin/products.json", params: { active: "false" }
end
it "has a statement descriptor" do
::Stripe::Product.expects(:create).with(
has_entry(statement_descriptor: "Blessed are the cheesemakers"),
)
post "/s/admin/products.json",
params: {
statement_descriptor: "Blessed are the cheesemakers",
}
end
it "has no statement descriptor if empty" do
::Stripe::Product.expects(:create).with(has_key(:statement_descriptor)).never
post "/s/admin/products.json", params: { statement_descriptor: "" }
end
it "has metadata" do
::Stripe::Product.expects(:create).with(
has_entry(
metadata: {
description: "Oi, I think he just said bless be all the bignoses!",
repurchaseable: "false",
},
),
)
post "/s/admin/products.json",
params: {
metadata: {
description: "Oi, I think he just said bless be all the bignoses!",
repurchaseable: "false",
},
}
end
end
describe "show" do
it "retrieves the product" do
::Stripe::Product.expects(:retrieve).with("prod_walterwhite")
get "/s/admin/products/prod_walterwhite.json"
end
end
describe "update" do
it "updates the product" do
::Stripe::Product.expects(:update)
patch "/s/admin/products/prod_walterwhite.json", params: {}
end
end
describe "delete" do
it "deletes the product" do
::Stripe::Product.expects(:delete).with("prod_walterwhite")
delete "/s/admin/products/prod_walterwhite.json"
end
end
end end
end end
end end

View File

@ -2,141 +2,139 @@
require "rails_helper" require "rails_helper"
module DiscourseSubscriptions RSpec.describe DiscourseSubscriptions::Admin::SubscriptionsController do
RSpec.describe Admin::SubscriptionsController do it "is a subclass of AdminController" do
it "is a subclass of AdminController" do expect(DiscourseSubscriptions::Admin::SubscriptionsController < ::Admin::AdminController).to eq(
expect( true,
DiscourseSubscriptions::Admin::SubscriptionsController < ::Admin::AdminController, )
).to eq(true) end
let(:user) { Fabricate(:user) }
let(:customer) do
Fabricate(:customer, user_id: user.id, customer_id: "c_123", product_id: "pr_34578")
end
before do
Fabricate(:subscription, external_id: "sub_12345", customer_id: customer.id)
Fabricate(:subscription, external_id: "sub_77777", customer_id: customer.id)
end
context "when unauthenticated" do
it "does nothing" do
::Stripe::Subscription.expects(:list).never
get "/s/admin/subscriptions.json"
expect(response.status).to eq(404)
end end
let(:user) { Fabricate(:user) } it "does not destroy a subscription" do
let(:customer) do ::Stripe::Subscription.expects(:delete).never
Fabricate(:customer, user_id: user.id, customer_id: "c_123", product_id: "pr_34578") patch "/s/admin/subscriptions/sub_12345.json"
end end
end
before do context "when authenticated" do
Fabricate(:subscription, external_id: "sub_12345", customer_id: customer.id) let(:admin) { Fabricate(:admin) }
Fabricate(:subscription, external_id: "sub_77777", customer_id: customer.id)
end
context "when unauthenticated" do before { sign_in(admin) }
it "does nothing" do
::Stripe::Subscription.expects(:list).never describe "index" do
before do
SiteSetting.discourse_subscriptions_public_key = "public-key"
SiteSetting.discourse_subscriptions_secret_key = "secret-key"
end
it "gets the subscriptions and products" do
::Stripe::Subscription
.expects(:list)
.with(expand: ["data.plan.product"], limit: 10, starting_after: nil)
.returns(has_more: false, data: [{ id: "sub_12345" }, { id: "sub_nope" }])
get "/s/admin/subscriptions.json" get "/s/admin/subscriptions.json"
expect(response.status).to eq(404) subscriptions = response.parsed_body["data"][0]["id"]
expect(response.status).to eq(200)
expect(subscriptions).to eq("sub_12345")
end end
it "does not destroy a subscription" do it "handles starting at a different point in the set" do
::Stripe::Subscription.expects(:delete).never ::Stripe::Subscription
patch "/s/admin/subscriptions/sub_12345.json" .expects(:list)
.with(expand: ["data.plan.product"], limit: 10, starting_after: "sub_nope")
.returns(has_more: false, data: [{ id: "sub_77777" }, { id: "sub_yepnoep" }])
get "/s/admin/subscriptions.json", params: { last_record: "sub_nope" }
subscriptions = response.parsed_body["data"][0]["id"]
expect(response.status).to eq(200)
expect(subscriptions).to eq("sub_77777")
end end
end end
context "when authenticated" do describe "destroy" do
let(:admin) { Fabricate(:admin) } let(:group) { Fabricate(:group, name: "subscribers") }
before { sign_in(admin) } before { group.add(user) }
describe "index" do it "deletes a customer" do
before do ::Stripe::Subscription
SiteSetting.discourse_subscriptions_public_key = "public-key" .expects(:delete)
SiteSetting.discourse_subscriptions_secret_key = "secret-key" .with("sub_12345")
end .returns(plan: { product: "pr_34578" }, customer: "c_123")
it "gets the subscriptions and products" do expect { delete "/s/admin/subscriptions/sub_12345.json" }.to change {
::Stripe::Subscription DiscourseSubscriptions::Customer.count
.expects(:list) }.by(-1)
.with(expand: ["data.plan.product"], limit: 10, starting_after: nil)
.returns(has_more: false, data: [{ id: "sub_12345" }, { id: "sub_nope" }])
get "/s/admin/subscriptions.json"
subscriptions = response.parsed_body["data"][0]["id"]
expect(response.status).to eq(200)
expect(subscriptions).to eq("sub_12345")
end
it "handles starting at a different point in the set" do
::Stripe::Subscription
.expects(:list)
.with(expand: ["data.plan.product"], limit: 10, starting_after: "sub_nope")
.returns(has_more: false, data: [{ id: "sub_77777" }, { id: "sub_yepnoep" }])
get "/s/admin/subscriptions.json", params: { last_record: "sub_nope" }
subscriptions = response.parsed_body["data"][0]["id"]
expect(response.status).to eq(200)
expect(subscriptions).to eq("sub_77777")
end
end end
describe "destroy" do it "removes the user from the group" do
let(:group) { Fabricate(:group, name: "subscribers") } ::Stripe::Subscription
.expects(:delete)
before { group.add(user) } .with("sub_12345")
.returns(
it "deletes a customer" do plan: {
::Stripe::Subscription product: "pr_34578",
.expects(:delete) metadata: {
.with("sub_12345") group_name: "subscribers",
.returns(plan: { product: "pr_34578" }, customer: "c_123")
expect { delete "/s/admin/subscriptions/sub_12345.json" }.to change {
DiscourseSubscriptions::Customer.count
}.by(-1)
end
it "removes the user from the group" do
::Stripe::Subscription
.expects(:delete)
.with("sub_12345")
.returns(
plan: {
product: "pr_34578",
metadata: {
group_name: "subscribers",
},
}, },
customer: "c_123", },
) customer: "c_123",
)
expect { delete "/s/admin/subscriptions/sub_12345.json" }.to change { expect { delete "/s/admin/subscriptions/sub_12345.json" }.to change {
user.groups.count user.groups.count
}.by(-1) }.by(-1)
end end
it "does not remove the user from the group" do it "does not remove the user from the group" do
::Stripe::Subscription ::Stripe::Subscription
.expects(:delete) .expects(:delete)
.with("sub_12345") .with("sub_12345")
.returns( .returns(
plan: { plan: {
product: "pr_34578", product: "pr_34578",
metadata: { metadata: {
group_name: "group_does_not_exist", group_name: "group_does_not_exist",
},
}, },
customer: "c_123", },
) customer: "c_123",
)
expect { delete "/s/admin/subscriptions/sub_12345.json" }.not_to change { expect { delete "/s/admin/subscriptions/sub_12345.json" }.not_to change {
user.groups.count user.groups.count
} }
end end
it "refunds if params[:refund] present" do it "refunds if params[:refund] present" do
::Stripe::Subscription ::Stripe::Subscription
.expects(:delete) .expects(:delete)
.with("sub_12345") .with("sub_12345")
.returns(plan: { product: "pr_34578" }, customer: "c_123") .returns(plan: { product: "pr_34578" }, customer: "c_123")
::Stripe::Subscription ::Stripe::Subscription
.expects(:retrieve) .expects(:retrieve)
.with("sub_12345") .with("sub_12345")
.returns(latest_invoice: "in_123") .returns(latest_invoice: "in_123")
::Stripe::Invoice.expects(:retrieve).with("in_123").returns(payment_intent: "pi_123") ::Stripe::Invoice.expects(:retrieve).with("in_123").returns(payment_intent: "pi_123")
::Stripe::Refund.expects(:create).with({ payment_intent: "pi_123" }) ::Stripe::Refund.expects(:create).with({ payment_intent: "pi_123" })
delete "/s/admin/subscriptions/sub_12345.json", params: { refund: true } delete "/s/admin/subscriptions/sub_12345.json", params: { refund: true }
end
end end
end end
end end

View File

@ -2,19 +2,17 @@
require "rails_helper" require "rails_helper"
module DiscourseSubscriptions RSpec.describe DiscourseSubscriptions::AdminController do
RSpec.describe AdminController do let(:admin) { Fabricate(:admin) }
let(:admin) { Fabricate(:admin) }
before { sign_in(admin) } before { sign_in(admin) }
it "is a subclass of AdminController" do it "is a subclass of AdminController" do
expect(DiscourseSubscriptions::AdminController < ::Admin::AdminController).to eq(true) expect(DiscourseSubscriptions::AdminController < ::Admin::AdminController).to eq(true)
end end
it "is ok" do it "is ok" do
get "/s/admin.json" get "/s/admin.json"
expect(response.status).to eq(200) expect(response.status).to eq(200)
end
end end
end end

View File

@ -2,109 +2,105 @@
require "rails_helper" require "rails_helper"
module DiscourseSubscriptions RSpec.describe DiscourseSubscriptions::HooksController do
RSpec.describe HooksController do before { SiteSetting.discourse_subscriptions_webhook_secret = "zascharoo" }
before { SiteSetting.discourse_subscriptions_webhook_secret = "zascharoo" }
it "contructs a webhook event" do it "constructs a webhook event" do
payload = "we-want-a-shrubbery" payload = "we-want-a-shrubbery"
headers = { HTTP_STRIPE_SIGNATURE: "stripe-webhook-signature" } headers = { HTTP_STRIPE_SIGNATURE: "stripe-webhook-signature" }
::Stripe::Webhook ::Stripe::Webhook
.expects(:construct_event) .expects(:construct_event)
.with("we-want-a-shrubbery", "stripe-webhook-signature", "zascharoo") .with("we-want-a-shrubbery", "stripe-webhook-signature", "zascharoo")
.returns(type: "something") .returns(type: "something")
post "/s/hooks.json", params: payload, headers: headers post "/s/hooks.json", params: payload, headers: headers
expect(response.status).to eq 200 expect(response.status).to eq 200
end
describe "event types" do
let(:user) { Fabricate(:user) }
let(:customer) do
Fabricate(:customer, customer_id: "c_575768", product_id: "p_8654", user_id: user.id)
end end
let(:group) { Fabricate(:group, name: "subscribers-group") }
describe "event types" do let(:event_data) do
let(:user) { Fabricate(:user) } {
let(:customer) do object: {
Fabricate(:customer, customer_id: "c_575768", product_id: "p_8654", user_id: user.id) customer: customer.customer_id,
end plan: {
let(:group) { Fabricate(:group, name: "subscribers-group") } product: customer.product_id,
metadata: {
let(:event_data) do group_name: group.name,
{
object: {
customer: customer.customer_id,
plan: {
product: customer.product_id,
metadata: {
group_name: group.name,
},
}, },
}, },
} },
}
end
describe "customer.subscription.updated" do
before do
event = { type: "customer.subscription.updated", data: event_data }
::Stripe::Webhook.stubs(:construct_event).returns(event)
end end
describe "customer.subscription.updated" do it "is successfull" do
before do post "/s/hooks.json"
event = { type: "customer.subscription.updated", data: event_data } expect(response.status).to eq 200
::Stripe::Webhook.stubs(:construct_event).returns(event)
end
it "is successfull" do
post "/s/hooks.json"
expect(response.status).to eq 200
end
describe "completing the subscription" do
it "does not add the user to the group" do
event_data[:object][:status] = "incomplete"
event_data[:previous_attributes] = { status: "incomplete" }
expect { post "/s/hooks.json" }.not_to change { user.groups.count }
expect(response.status).to eq 200
end
it "does not add the user to the group" do
event_data[:object][:status] = "incomplete"
event_data[:previous_attributes] = { status: "something-else" }
expect { post "/s/hooks.json" }.not_to change { user.groups.count }
expect(response.status).to eq 200
end
it "adds the user to the group when completing the transaction" do
event_data[:object][:status] = "complete"
event_data[:previous_attributes] = { status: "incomplete" }
expect { post "/s/hooks.json" }.to change { user.groups.count }.by(1)
expect(response.status).to eq 200
end
end
end end
describe "customer.subscription.deleted" do describe "completing the subscription" do
before do it "does not add the user to the group" do
event = { type: "customer.subscription.deleted", data: event_data } event_data[:object][:status] = "incomplete"
event_data[:previous_attributes] = { status: "incomplete" }
::Stripe::Webhook.stubs(:construct_event).returns(event) expect { post "/s/hooks.json" }.not_to change { user.groups.count }
group.add(user)
end
it "deletes the customer" do
expect { post "/s/hooks.json" }.to change { DiscourseSubscriptions::Customer.count }.by(
-1,
)
expect(response.status).to eq 200 expect(response.status).to eq 200
end end
it "removes the user from the group" do it "does not add the user to the group" do
expect { post "/s/hooks.json" }.to change { user.groups.count }.by(-1) event_data[:object][:status] = "incomplete"
event_data[:previous_attributes] = { status: "something-else" }
expect { post "/s/hooks.json" }.not_to change { user.groups.count }
expect(response.status).to eq 200 expect(response.status).to eq 200
end end
it "adds the user to the group when completing the transaction" do
event_data[:object][:status] = "complete"
event_data[:previous_attributes] = { status: "incomplete" }
expect { post "/s/hooks.json" }.to change { user.groups.count }.by(1)
expect(response.status).to eq 200
end
end
end
describe "customer.subscription.deleted" do
before do
event = { type: "customer.subscription.deleted", data: event_data }
::Stripe::Webhook.stubs(:construct_event).returns(event)
group.add(user)
end
it "deletes the customer" do
expect { post "/s/hooks.json" }.to change { DiscourseSubscriptions::Customer.count }.by(-1)
expect(response.status).to eq 200
end
it "removes the user from the group" do
expect { post "/s/hooks.json" }.to change { user.groups.count }.by(-1)
expect(response.status).to eq 200
end end
end end
end end

File diff suppressed because it is too large Load Diff

View File

@ -2,63 +2,61 @@
require "rails_helper" require "rails_helper"
module DiscourseSubscriptions RSpec.describe DiscourseSubscriptions::User::PaymentsController do
RSpec.describe User::PaymentsController do it "is a subclass of ApplicationController" do
it "is a subclass of ApplicationController" do expect(DiscourseSubscriptions::User::PaymentsController < ::ApplicationController).to eq(true)
expect(DiscourseSubscriptions::User::PaymentsController < ::ApplicationController).to eq(true) end
context "when not authenticated" do
it "does not get the payment intents" do
::Stripe::PaymentIntent.expects(:list).never
get "/s/user/payments.json"
expect(response.status).to eq(403)
end
end
context "when authenticated" do
let(:user) { Fabricate(:user, email: "zasch@example.com") }
before do
sign_in(user)
Fabricate(:customer, customer_id: "c_345678", user_id: user.id)
Fabricate(:product, external_id: "prod_8675309")
Fabricate(:product, external_id: "prod_8675310")
end end
context "when not authenticated" do it "gets payment intents" do
it "does not get the payment intents" do created_time = Time.now
::Stripe::PaymentIntent.expects(:list).never ::Stripe::Invoice
get "/s/user/payments.json" .expects(:list)
expect(response.status).to eq(403) .with(customer: "c_345678")
end .returns(
end data: [
{ id: "inv_900007", lines: { data: [plan: { product: "prod_8675309" }] } },
{ id: "inv_900008", lines: { data: [plan: { product: "prod_8675310" }] } },
{ id: "inv_900008", lines: { data: [plan: { product: "prod_8675310" }] } },
],
)
context "when authenticated" do ::Stripe::PaymentIntent
let(:user) { Fabricate(:user, email: "zasch@example.com") } .expects(:list)
.with(customer: "c_345678")
.returns(
data: [
{ id: "pi_900008", invoice: "inv_900008", created: created_time },
{ id: "pi_900008", invoice: "inv_900008", created: created_time },
{ id: "pi_900007", invoice: "inv_900007", created: Time.now },
{ id: "pi_007", invoice: "inv_007", created: Time.now },
],
)
before do get "/s/user/payments.json"
sign_in(user)
Fabricate(:customer, customer_id: "c_345678", user_id: user.id)
Fabricate(:product, external_id: "prod_8675309")
Fabricate(:product, external_id: "prod_8675310")
end
it "gets payment intents" do parsed_body = response.parsed_body
created_time = Time.now invoice = parsed_body[0]["invoice"]
::Stripe::Invoice
.expects(:list)
.with(customer: "c_345678")
.returns(
data: [
{ id: "inv_900007", lines: { data: [plan: { product: "prod_8675309" }] } },
{ id: "inv_900008", lines: { data: [plan: { product: "prod_8675310" }] } },
{ id: "inv_900008", lines: { data: [plan: { product: "prod_8675310" }] } },
],
)
::Stripe::PaymentIntent expect(invoice).to eq("inv_900007")
.expects(:list) expect(parsed_body.count).to eq(2)
.with(customer: "c_345678")
.returns(
data: [
{ id: "pi_900008", invoice: "inv_900008", created: created_time },
{ id: "pi_900008", invoice: "inv_900008", created: created_time },
{ id: "pi_900007", invoice: "inv_900007", created: Time.now },
{ id: "pi_007", invoice: "inv_007", created: Time.now },
],
)
get "/s/user/payments.json"
parsed_body = response.parsed_body
invoice = parsed_body[0]["invoice"]
expect(invoice).to eq("inv_900007")
expect(parsed_body.count).to eq(2)
end
end end
end end
end end

View File

@ -2,105 +2,103 @@
require "rails_helper" require "rails_helper"
module DiscourseSubscriptions RSpec.describe DiscourseSubscriptions::User::SubscriptionsController do
RSpec.describe User::SubscriptionsController do it "is a subclass of ApplicationController" do
it "is a subclass of ApplicationController" do expect(DiscourseSubscriptions::User::SubscriptionsController < ::ApplicationController).to eq(
expect(DiscourseSubscriptions::User::SubscriptionsController < ::ApplicationController).to eq( true,
true, )
) end
context "when not authenticated" do
it "does not get the subscriptions" do
::Stripe::Customer.expects(:list).never
get "/s/user/subscriptions.json"
end end
context "when not authenticated" do it "does not destroy a subscription" do
it "does not get the subscriptions" do ::Stripe::Subscription.expects(:delete).never
::Stripe::Customer.expects(:list).never patch "/s/user/subscriptions/sub_12345.json"
end
it "doesn't update payment method for subscription" do
::Stripe::Subscription.expects(:update).never
::Stripe::PaymentMethod.expects(:attach).never
put "/s/user/subscriptions/sub_12345.json", params: { payment_method: "pm_abc123abc" }
end
end
context "when authenticated" do
let(:user) { Fabricate(:user, email: "beanie@example.com") }
let(:customer) do
Fabricate(:customer, user_id: user.id, customer_id: "cus_23456", product_id: "prod_123")
end
before do
sign_in(user)
Fabricate(:subscription, customer_id: customer.id, external_id: "sub_1234")
end
describe "index" do
let(:plans) do
{
data: [
{ id: "plan_1", product: { name: "ACME Subscriptions" } },
{ id: "plan_2", product: { name: "ACME Other Subscriptions" } },
],
}
end
let(:customers) do
{
data: [
{
id: "cus_23456",
subscriptions: {
data: [
{ id: "sub_1234", items: { data: [price: { id: "plan_1" }] } },
{ id: "sub_4567", items: { data: [price: { id: "plan_2" }] } },
],
},
},
],
}
end
it "gets subscriptions" do
::Stripe::Price.expects(:list).with(expand: ["data.product"], limit: 100).returns(plans)
::Stripe::Customer
.expects(:list)
.with(email: user.email, expand: ["data.subscriptions"])
.returns(customers)
get "/s/user/subscriptions.json" get "/s/user/subscriptions.json"
end
it "does not destroy a subscription" do subscription = response.parsed_body.first
::Stripe::Subscription.expects(:delete).never
patch "/s/user/subscriptions/sub_12345.json"
end
it "doesn't update payment method for subscription" do expect(subscription).to eq(
::Stripe::Subscription.expects(:update).never "id" => "sub_1234",
::Stripe::PaymentMethod.expects(:attach).never "items" => {
put "/s/user/subscriptions/sub_12345.json", params: { payment_method: "pm_abc123abc" } "data" => [{ "price" => { "id" => "plan_1" } }],
end },
end "plan" => {
"id" => "plan_1",
context "when authenticated" do
let(:user) { Fabricate(:user, email: "beanie@example.com") }
let(:customer) do
Fabricate(:customer, user_id: user.id, customer_id: "cus_23456", product_id: "prod_123")
end
before do
sign_in(user)
Fabricate(:subscription, customer_id: customer.id, external_id: "sub_1234")
end
describe "index" do
let(:plans) do
{
data: [
{ id: "plan_1", product: { name: "ACME Subscriptions" } },
{ id: "plan_2", product: { name: "ACME Other Subscriptions" } },
],
}
end
let(:customers) do
{
data: [
{
id: "cus_23456",
subscriptions: {
data: [
{ id: "sub_1234", items: { data: [price: { id: "plan_1" }] } },
{ id: "sub_4567", items: { data: [price: { id: "plan_2" }] } },
],
},
},
],
}
end
it "gets subscriptions" do
::Stripe::Price.expects(:list).with(expand: ["data.product"], limit: 100).returns(plans)
::Stripe::Customer
.expects(:list)
.with(email: user.email, expand: ["data.subscriptions"])
.returns(customers)
get "/s/user/subscriptions.json"
subscription = response.parsed_body.first
expect(subscription).to eq(
"id" => "sub_1234",
"items" => {
"data" => [{ "price" => { "id" => "plan_1" } }],
},
"plan" => {
"id" => "plan_1",
"product" => {
"name" => "ACME Subscriptions",
},
},
"product" => { "product" => {
"name" => "ACME Subscriptions", "name" => "ACME Subscriptions",
}, },
) },
end "product" => {
"name" => "ACME Subscriptions",
},
)
end end
end
describe "update" do describe "update" do
it "updates the payment method for subscription" do it "updates the payment method for subscription" do
::Stripe::Subscription.expects(:update).once ::Stripe::Subscription.expects(:update).once
::Stripe::PaymentMethod.expects(:attach).once ::Stripe::PaymentMethod.expects(:attach).once
put "/s/user/subscriptions/sub_1234.json", params: { payment_method: "pm_abc123abc" } put "/s/user/subscriptions/sub_1234.json", params: { payment_method: "pm_abc123abc" }
end
end end
end end
end end

View File

@ -11,6 +11,7 @@ describe SiteSerializer do
SiteSetting.discourse_subscriptions_enabled = true SiteSetting.discourse_subscriptions_enabled = true
SiteSetting.discourse_subscriptions_campaign_enabled = true SiteSetting.discourse_subscriptions_campaign_enabled = true
end end
it "is false if the goal_met date is < 7 days old" do it "is false if the goal_met date is < 7 days old" do
Discourse.redis.set("subscriptions_goal_met_date", 10.days.ago) Discourse.redis.set("subscriptions_goal_met_date", 10.days.ago)
data = described_class.new(Site.new(guardian), scope: guardian, root: false).as_json data = described_class.new(Site.new(guardian), scope: guardian, root: false).as_json

View File

@ -4,12 +4,8 @@ require "rails_helper"
describe DiscourseSubscriptions::Campaign do describe DiscourseSubscriptions::Campaign do
describe "campaign data is refreshed" do describe "campaign data is refreshed" do
let (:user) { let(:user) { Fabricate(:user) }
Fabricate(:user) let(:user2) { Fabricate(:user) }
}
let (:user2) {
Fabricate(:user)
}
let(:subscription) do let(:subscription) do
{ {
id: "sub_1234", id: "sub_1234",

View File

@ -1,7 +1,7 @@
import { acceptance, count } from "discourse/tests/helpers/qunit-helpers";
import { stubStripe } from "discourse/plugins/discourse-subscriptions/helpers/stripe";
import { visit } from "@ember/test-helpers"; import { visit } from "@ember/test-helpers";
import { test } from "qunit"; import { test } from "qunit";
import { acceptance, count } from "discourse/tests/helpers/qunit-helpers";
import { stubStripe } from "discourse/plugins/discourse-subscriptions/helpers/stripe";
acceptance("Discourse Subscriptions", function (needs) { acceptance("Discourse Subscriptions", function (needs) {
needs.user(); needs.user();

View File

@ -1,8 +1,8 @@
import { acceptance, count } from "discourse/tests/helpers/qunit-helpers";
import { stubStripe } from "discourse/plugins/discourse-subscriptions/helpers/stripe";
import { click, visit } from "@ember/test-helpers"; import { click, visit } from "@ember/test-helpers";
import { test } from "qunit"; import { test } from "qunit";
import pretender, { response } from "discourse/tests/helpers/create-pretender"; import pretender, { response } from "discourse/tests/helpers/create-pretender";
import { acceptance, count } from "discourse/tests/helpers/qunit-helpers";
import { stubStripe } from "discourse/plugins/discourse-subscriptions/helpers/stripe";
function singleProductPretender() { function singleProductPretender() {
pretender.get("/s", () => { pretender.get("/s", () => {

View File

@ -1,8 +1,8 @@
import { count, discourseModule } from "discourse/tests/helpers/qunit-helpers"; import hbs from "htmlbars-inline-precompile";
import componentTest, { import componentTest, {
setupRenderingTest, setupRenderingTest,
} from "discourse/tests/helpers/component-test"; } from "discourse/tests/helpers/component-test";
import hbs from "htmlbars-inline-precompile"; import { count, discourseModule } from "discourse/tests/helpers/qunit-helpers";
discourseModule("payment-options", function (hooks) { discourseModule("payment-options", function (hooks) {
setupRenderingTest(hooks); setupRenderingTest(hooks);

View File

@ -1,12 +1,12 @@
import hbs from "htmlbars-inline-precompile";
import componentTest, {
setupRenderingTest,
} from "discourse/tests/helpers/component-test";
import { import {
count, count,
discourseModule, discourseModule,
query, query,
} from "discourse/tests/helpers/qunit-helpers"; } from "discourse/tests/helpers/qunit-helpers";
import componentTest, {
setupRenderingTest,
} from "discourse/tests/helpers/component-test";
import hbs from "htmlbars-inline-precompile";
discourseModule("payment-plan", function (hooks) { discourseModule("payment-plan", function (hooks) {
setupRenderingTest(hooks); setupRenderingTest(hooks);

1777
yarn.lock

File diff suppressed because it is too large Load Diff