mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-11-25 09:03:12 +03:00
Added non-Stripe members setting screen acceptance tests
refs https://github.com/TryGhost/Team/issues/1358 - added acceptance tests for members settings screen - subscription access management - default post access management - free tier management - fixed `enableLabsFlag()` test helper overwriting existing flag settings when enabling another one - updated API mocks and fixtures - matched product fixtures to default tiers-enabled products - updated product API mocks to include benefit handling
This commit is contained in:
parent
8af0ce7474
commit
4b646d40ea
@ -1831,3 +1831,8 @@ remove|ember-template-lint|no-action|7|12|7|12|fada170dae44678cba8240b4ae3233c63
|
|||||||
remove|ember-template-lint|no-action|17|12|17|12|b75d66d02a33b108a64bb94d494fa194434c82f9|1644364800000|1646956800000|1649545200000|lib/koenig-editor/addon/components/koenig-toolbar.hbs
|
remove|ember-template-lint|no-action|17|12|17|12|b75d66d02a33b108a64bb94d494fa194434c82f9|1644364800000|1646956800000|1649545200000|lib/koenig-editor/addon/components/koenig-toolbar.hbs
|
||||||
remove|ember-template-lint|no-action|28|16|28|16|e5e787868d089ae3141666004a914deff774b489|1644364800000|1646956800000|1649545200000|lib/koenig-editor/addon/components/koenig-toolbar.hbs
|
remove|ember-template-lint|no-action|28|16|28|16|e5e787868d089ae3141666004a914deff774b489|1644364800000|1646956800000|1649545200000|lib/koenig-editor/addon/components/koenig-toolbar.hbs
|
||||||
remove|ember-template-lint|no-action|70|12|70|12|03d2eaf613eae8cb3ea3c821ba63544c730364c8|1644364800000|1646956800000|1649545200000|lib/koenig-editor/addon/components/koenig-toolbar.hbs
|
remove|ember-template-lint|no-action|70|12|70|12|03d2eaf613eae8cb3ea3c821ba63544c730364c8|1644364800000|1646956800000|1649545200000|lib/koenig-editor/addon/components/koenig-toolbar.hbs
|
||||||
|
remove|ember-template-lint|no-duplicate-landmark-elements|126|24|126|24|a19c12d5f0d5fa9b890943214b862502d9f2dcee|1643760000000|1646352000000|1648940400000|app/components/modal-product.hbs
|
||||||
|
remove|ember-template-lint|no-nested-landmark|126|24|126|24|a19c12d5f0d5fa9b890943214b862502d9f2dcee|1643760000000|1646352000000|1648940400000|app/components/modal-product.hbs
|
||||||
|
remove|ember-template-lint|no-duplicate-attributes|9|4|9|4|6b5f76f812df2b84f2ed9ee5a557ca1bf98710df|1643760000000|1646352000000|1648940400000|app/components/gh-post-settings-menu/visibility-segment-select.hbs
|
||||||
|
add|ember-template-lint|no-duplicate-landmark-elements|131|24|131|24|9eb7d301f1f50334e793aafab8f6b9e8905125ab|1645142400000|1647734400000|1650322800000|app/components/modal-product.hbs
|
||||||
|
add|ember-template-lint|no-nested-landmark|131|24|131|24|9eb7d301f1f50334e793aafab8f6b9e8905125ab|1645142400000|1647734400000|1650322800000|app/components/modal-product.hbs
|
||||||
|
@ -21,19 +21,22 @@
|
|||||||
@input={{action "updateLabel" value="target.value"}}
|
@input={{action "updateLabel" value="target.value"}}
|
||||||
@keyPress={{action "clearLabelErrors"}}
|
@keyPress={{action "clearLabelErrors"}}
|
||||||
@stopEnterKeyDownPropagation={{true}}
|
@stopEnterKeyDownPropagation={{true}}
|
||||||
@focus-out={{action "updateLabel" this.name}} data-test-input="name" />
|
@focus-out={{action "updateLabel" this.name}}
|
||||||
|
data-test-input="benefit-label" />
|
||||||
|
|
||||||
<GhErrorMessage
|
<GhErrorMessage
|
||||||
@errors={{this.benefitItem.errors}}
|
@errors={{this.benefitItem.errors}}
|
||||||
@property="name" data-test-error="name" />
|
@property="name"
|
||||||
|
data-test-error="benefit-label" />
|
||||||
</GhValidationStatusContainer>
|
</GhValidationStatusContainer>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{#if this.benefitItem.isNew}}
|
{{#if this.benefitItem.isNew}}
|
||||||
<button type="button" class="gh-blognav-add" {{action "addItem" this.benefitItem}}>
|
<button type="button" class="gh-blognav-add" {{action "addItem" this.benefitItem}} data-test-button="add-benefit">
|
||||||
{{svg-jar "add"}}<span class="sr-only">Add</span>
|
{{svg-jar "add"}}<span class="sr-only">Add</span>
|
||||||
</button>
|
</button>
|
||||||
{{else}}
|
{{else}}
|
||||||
<button type="button" class="gh-blognav-delete" {{action "deleteItem" this.benefitItem}}>
|
<button type="button" class="gh-blognav-delete" {{action "deleteItem" this.benefitItem}} data-test-button="delete-benefit">
|
||||||
{{svg-jar "trash"}}<span class="sr-only">Delete</span>
|
{{svg-jar "trash"}}<span class="sr-only">Delete</span>
|
||||||
</button>
|
</button>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
@ -6,12 +6,11 @@
|
|||||||
@allowCreation={{false}}
|
@allowCreation={{false}}
|
||||||
@renderInPlace={{this.renderInPlace}}
|
@renderInPlace={{this.renderInPlace}}
|
||||||
@onChange={{this.setSegment}}
|
@onChange={{this.setSegment}}
|
||||||
@disabled={{@disabled}}
|
|
||||||
@class="select-members"
|
@class="select-members"
|
||||||
@placeholder="Select a tier"
|
@placeholder="Select a tier"
|
||||||
as |option|
|
as |option|
|
||||||
>
|
>
|
||||||
{{option.name}}
|
<span data-test-visibility-segment-option={{option.name}}>{{option.name}}</span>
|
||||||
</GhTokenInput>
|
</GhTokenInput>
|
||||||
|
|
||||||
<GhMembersSegmentCount
|
<GhMembersSegmentCount
|
||||||
|
@ -1,21 +1,21 @@
|
|||||||
<div class="gh-main-content-card gh-product-card">
|
<div class="gh-main-content-card gh-product-card" data-test-product-card={{@product.slug}}>
|
||||||
<div class="gh-product-card-block title-block">
|
<div class="gh-product-card-block title-block">
|
||||||
<h3 class="gh-product-card-name">
|
<h3 class="gh-product-card-name" data-test-name>
|
||||||
{{this.product.name}}
|
{{@product.name}}
|
||||||
</h3>
|
</h3>
|
||||||
<p class="gh-product-card-description">
|
<p class="gh-product-card-description" data-test-description>
|
||||||
{{#if this.product.description.length}}
|
{{#if @product.description.length}}
|
||||||
{{this.product.description}}
|
{{@product.description}}
|
||||||
{{else}}
|
{{else}}
|
||||||
No description added for this tier.
|
No description added for this tier.
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="gh-product-card-block benefits-block">
|
<div class="gh-product-card-block benefits-block" data-test-benefits>
|
||||||
<h4>Benefits <span class="counter">({{if this.product.benefits.length this.product.benefits.length "0"}})</span></h4>
|
<h4>Benefits <span class="counter">({{or @product.benefits.length "0"}})</span></h4>
|
||||||
{{#if this.product.benefits.length}}
|
{{#if @product.benefits.length}}
|
||||||
<ul class="benefits">
|
<ul class="benefits">
|
||||||
{{#each this.product.benefits as |benefit|}}
|
{{#each @product.benefits as |benefit|}}
|
||||||
<li>{{svg-jar "check"}} {{benefit.name}} </li>
|
<li>{{svg-jar "check"}} {{benefit.name}} </li>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</ul>
|
</ul>
|
||||||
@ -23,10 +23,10 @@
|
|||||||
<p class="gh-product-card-description">No benefits added for this tier.</p>
|
<p class="gh-product-card-description">No benefits added for this tier.</p>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
{{#if (eq this.product.type "free" )}}
|
{{#if (eq @product.type "free" )}}
|
||||||
<div class="gh-product-card-block">
|
<div class="gh-product-card-block">
|
||||||
<div class="gh-product-price-container">
|
<div class="gh-product-price-container">
|
||||||
<div class="gh-product-card-price">
|
<div class="gh-product-card-price" data-test-free-price>
|
||||||
<div class="flex items-start">
|
<div class="flex items-start">
|
||||||
<span class="currency">{{currency-symbol this.productCurrency}}</span>
|
<span class="currency">{{currency-symbol this.productCurrency}}</span>
|
||||||
<span class="amount">0</span>
|
<span class="amount">0</span>
|
||||||
@ -35,31 +35,31 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if (eq this.product.type "paid" )}}
|
{{#if (eq @product.type "paid" )}}
|
||||||
<div class="gh-product-card-block">
|
<div class="gh-product-card-block">
|
||||||
<div class="gh-product-price-container">
|
<div class="gh-product-price-container">
|
||||||
<div class="gh-product-card-price">
|
<div class="gh-product-card-price" data-test-monthly-price>
|
||||||
<div class="flex items-start">
|
<div class="flex items-start">
|
||||||
<span class="currency">{{currency-symbol this.productCurrency}}</span>
|
<span class="currency">{{currency-symbol this.productCurrency}}</span>
|
||||||
<span class="amount">{{gh-price-amount this.product.monthlyPrice.amount}}</span>
|
<span class="amount">{{gh-price-amount @product.monthlyPrice.amount}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="period">Monthly</div>
|
<div class="period">Monthly</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="gh-product-card-price">
|
<div class="gh-product-card-price" data-test-yearly-price>
|
||||||
<div class="flex items-start">
|
<div class="flex items-start">
|
||||||
<span class="currency">{{currency-symbol this.productCurrency}}</span>
|
<span class="currency">{{currency-symbol this.productCurrency}}</span>
|
||||||
<span class="amount">{{gh-price-amount this.product.yearlyPrice.amount}}</span>
|
<span class="amount">{{gh-price-amount @product.yearlyPrice.amount}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="period">Yearly</div>
|
<div class="period">Yearly</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if (eq this.product.type "paid" )}}
|
{{#if (eq @product.type "paid" )}}
|
||||||
<div class="gh-product-card-button-container">
|
<div class="gh-product-card-button-container">
|
||||||
<span class="dropdown">
|
<span class="dropdown">
|
||||||
<GhDropdownButton
|
<GhDropdownButton
|
||||||
@dropdownName="tiers-actions-menu-{{this.product.name}}"
|
@dropdownName="tiers-actions-menu-{{@product.name}}"
|
||||||
@classNames="gh-btn gh-btn-action-icon gh-btn-icon gh-btn-outline gh-product-card-actions-button icon-only"
|
@classNames="gh-btn gh-btn-action-icon gh-btn-icon gh-btn-outline gh-product-card-actions-button icon-only"
|
||||||
@title="Tiers Actions"
|
@title="Tiers Actions"
|
||||||
data-test-button="tiers-actions"
|
data-test-button="tiers-actions"
|
||||||
@ -70,19 +70,19 @@
|
|||||||
</span>
|
</span>
|
||||||
</GhDropdownButton>
|
</GhDropdownButton>
|
||||||
<GhDropdown
|
<GhDropdown
|
||||||
@name="tiers-actions-menu-{{this.product.name}}"
|
@name="tiers-actions-menu-{{@product.name}}"
|
||||||
@tagName="ul"
|
@tagName="ul"
|
||||||
@classNames="gh-tier-actions-menu dropdown-menu dropdown-triangle-top-right"
|
@classNames="gh-tier-actions-menu dropdown-menu dropdown-triangle-top-right"
|
||||||
>
|
>
|
||||||
<li>
|
<li>
|
||||||
<button class="mr2" type="button" {{on "click" (fn this.openEditProduct this.product)}}>
|
<button class="mr2" type="button" {{on "click" (fn this.openEditProduct @product)}}>
|
||||||
<span>Edit</span>
|
<span>Edit</span>
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
{{#if this.showArchiveOption}}
|
{{#if this.showArchiveOption}}
|
||||||
<li>
|
<li>
|
||||||
<Settings::Members::ArchiveTier
|
<Settings::Members::ArchiveTier
|
||||||
@product={{this.product}}
|
@product={{@product}}
|
||||||
@onUnarchive={{@onUnarchive}}
|
@onUnarchive={{@onUnarchive}}
|
||||||
/>
|
/>
|
||||||
</li>
|
</li>
|
||||||
@ -92,7 +92,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{{else}}
|
{{else}}
|
||||||
<div class="gh-product-card-button-container">
|
<div class="gh-product-card-button-container">
|
||||||
<button type="button" {{on "click" (fn this.openEditProduct this.product)}} class="gh-btn gh-btn-action-icon gh-btn-icon gh-btn-outline gh-product-card-edit-button icon-only">
|
<button type="button" {{on "click" (fn this.openEditProduct @product)}} class="gh-btn gh-btn-action-icon gh-btn-icon gh-btn-outline gh-product-card-edit-button icon-only" data-test-button="edit-product">
|
||||||
<span>
|
<span>
|
||||||
{{svg-jar "pen"}}
|
{{svg-jar "pen"}}
|
||||||
</span>
|
</span>
|
||||||
|
@ -12,7 +12,6 @@ export default class extends Component {
|
|||||||
@service config;
|
@service config;
|
||||||
|
|
||||||
@tracked showProductModal = false;
|
@tracked showProductModal = false;
|
||||||
@tracked productModel = null;
|
|
||||||
|
|
||||||
get product() {
|
get product() {
|
||||||
return this.args.product;
|
return this.args.product;
|
||||||
|
@ -4,5 +4,6 @@
|
|||||||
@input={{this.setValue}}
|
@input={{this.setValue}}
|
||||||
@focus-out={{this.validateUrlInput}}
|
@focus-out={{this.validateUrlInput}}
|
||||||
@placeholder={{this.placeholder}}
|
@placeholder={{this.placeholder}}
|
||||||
|
...attributes
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
{{svg-jar "close"}}
|
{{svg-jar "close"}}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div class="gh-product-modal-content">
|
<div class="gh-product-modal-content" data-test-modal="edit-product">
|
||||||
<header class="modal-header" data-test-modal="webhook-form">
|
<header class="modal-header">
|
||||||
<h1 data-test-text="title">{{this.title}}</h1>
|
<h1 data-test-text="title">{{this.title}}</h1>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
@ -14,7 +14,7 @@
|
|||||||
<h4 class="gh-main-section-header small bn">Basic</h4>
|
<h4 class="gh-main-section-header small bn">Basic</h4>
|
||||||
<div class="gh-main-section-content grey gh-product-priceform-block">
|
<div class="gh-main-section-content grey gh-product-priceform-block">
|
||||||
{{#unless this.isFreeProduct}}
|
{{#unless this.isFreeProduct}}
|
||||||
<GhFormGroup @errors={{this.errors}} @property="name">
|
<GhFormGroup @errors={{this.errors}} @property="name" data-test-formgroup="name">
|
||||||
<label for="name" class="fw6">Name</label>
|
<label for="name" class="fw6">Name</label>
|
||||||
<GhTextInput
|
<GhTextInput
|
||||||
@value={{readonly this.product.name}}
|
@value={{readonly this.product.name}}
|
||||||
@ -22,11 +22,13 @@
|
|||||||
@name="name"
|
@name="name"
|
||||||
@placeholder="Bronze"
|
@placeholder="Bronze"
|
||||||
@id="name"
|
@id="name"
|
||||||
@class="gh-input" />
|
@class="gh-input"
|
||||||
|
data-test-input="product-name" />
|
||||||
<GhErrorMessage @errors={{this.errors}} @property="name" />
|
<GhErrorMessage @errors={{this.errors}} @property="name" />
|
||||||
</GhFormGroup>
|
</GhFormGroup>
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
<GhFormGroup @errors={{this.errors}} @property="description">
|
|
||||||
|
<GhFormGroup @errors={{this.errors}} @property="description" data-test-formgroup="description">
|
||||||
<label for="description" class="fw6">Description</label>
|
<label for="description" class="fw6">Description</label>
|
||||||
{{#if this.isFreeProduct}}
|
{{#if this.isFreeProduct}}
|
||||||
<GhTextInput
|
<GhTextInput
|
||||||
@ -35,7 +37,8 @@
|
|||||||
@name="description"
|
@name="description"
|
||||||
@placeholder="Free preview of {{this.settings.title}}"
|
@placeholder="Free preview of {{this.settings.title}}"
|
||||||
@id="description"
|
@id="description"
|
||||||
@class="gh-input" />
|
@class="gh-input"
|
||||||
|
data-test-input="free-product-description" />
|
||||||
{{else}}
|
{{else}}
|
||||||
<GhTextInput
|
<GhTextInput
|
||||||
@value={{readonly this.product.description}}
|
@value={{readonly this.product.description}}
|
||||||
@ -43,12 +46,14 @@
|
|||||||
@name="description"
|
@name="description"
|
||||||
@placeholder="Full access to premium content"
|
@placeholder="Full access to premium content"
|
||||||
@id="description"
|
@id="description"
|
||||||
@class="gh-input" />
|
@class="gh-input"
|
||||||
|
data-test-input="product-description" />
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<GhErrorMessage @errors={{this.errors}} @property="description" />
|
<GhErrorMessage @errors={{this.errors}} @property="description" />
|
||||||
</GhFormGroup>
|
</GhFormGroup>
|
||||||
|
|
||||||
{{#unless this.isFreeProduct}}
|
{{#unless this.isFreeProduct}}
|
||||||
<GhFormGroup @errors={{this.settings.errors}} @hasValidated={{this.settings.hasValidated}} @property="prices">
|
<GhFormGroup @errors={{this.settings.errors}} @hasValidated={{this.settings.hasValidated}} @property="prices" data-test-formgroup="prices">
|
||||||
<div class="gh-settings-members-pricelabelcont">
|
<div class="gh-settings-members-pricelabelcont">
|
||||||
<label for="monthlyPrice">Prices</label>
|
<label for="monthlyPrice">Prices</label>
|
||||||
<span>–</span>
|
<span>–</span>
|
||||||
@ -138,7 +143,7 @@
|
|||||||
@focusItem={{action "focusItem"}}
|
@focusItem={{action "focusItem"}}
|
||||||
@deleteItem={{action "deleteBenefit"}}
|
@deleteItem={{action "deleteBenefit"}}
|
||||||
@updateLabel={{action "updateLabel"}}
|
@updateLabel={{action "updateLabel"}}
|
||||||
data-test-navitem={{index}} />
|
data-test-benefit-item={{index}} />
|
||||||
</DraggableObject>
|
</DraggableObject>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</SortableObjects>
|
</SortableObjects>
|
||||||
@ -148,45 +153,45 @@
|
|||||||
@addItem={{action "addBenefit"}}
|
@addItem={{action "addBenefit"}}
|
||||||
@deleteItem={{action "deleteBenefit"}}
|
@deleteItem={{action "deleteBenefit"}}
|
||||||
@updateLabel={{action "updateLabel"}}
|
@updateLabel={{action "updateLabel"}}
|
||||||
data-test-navitem="new" />
|
data-test-benefit-item="new" />
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="gh-main-section-block gh-product-form-tierpreview">
|
<div class="gh-main-section-block gh-product-form-tierpreview" data-test-tierpreview>
|
||||||
<div class="gh-product-form-tierpreview-content">
|
<div class="gh-product-form-tierpreview-content">
|
||||||
{{#if this.isFreeProduct}}
|
{{#if this.isFreeProduct}}
|
||||||
<h4 class="gh-main-section-header small bn">Free Membership Preview</h4>
|
<h4 class="gh-main-section-header small bn" data-test-tierpreview-title>Free Membership Preview</h4>
|
||||||
{{else}}
|
{{else}}
|
||||||
<h4 class="gh-main-section-header small bn">Tier Preview</h4>
|
<h4 class="gh-main-section-header small bn" data-test-tierpreview-title>Tier Preview</h4>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<div class="gh-main-section-content" style="border-color: {{this.settings.accentColor}}">
|
<div class="gh-main-section-content" style="border-color: {{this.settings.accentColor}}">
|
||||||
<span class="checkmark" style="background-color: {{this.settings.accentColor}}"></span>
|
<span class="checkmark" style="background-color: {{this.settings.accentColor}}"></span>
|
||||||
|
|
||||||
{{#if this.product.name}}
|
{{#if this.product.name}}
|
||||||
<h4>{{this.product.name}}</h4>
|
<h4 data-test-tierpreview-name>{{this.product.name}}</h4>
|
||||||
{{else}}
|
{{else}}
|
||||||
<h4 class="placeholder">Bronze</h4>
|
<h4 class="placeholder" data-test-tierpreview-name>Bronze</h4>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if this.product.description}}
|
{{#if this.product.description}}
|
||||||
<p>{{this.product.description}}</p>
|
<p data-test-tierpreview-description>{{this.product.description}}</p>
|
||||||
{{else}}
|
{{else}}
|
||||||
{{#if this.isFreeProduct}}
|
{{#if this.isFreeProduct}}
|
||||||
<p class="placeholder">Free preview of {{this.settings.title}}</p>
|
<p class="placeholder" data-test-tierpreview-description>Free preview of {{this.settings.title}}</p>
|
||||||
{{else}}
|
{{else}}
|
||||||
<p class="placeholder">Full access to premium content</p>
|
<p class="placeholder" data-test-tierpreview-description>Full access to premium content</p>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if this.benefits}}
|
{{#if this.benefits}}
|
||||||
<ul>
|
<ul data-test-tierpreview-benefits>
|
||||||
{{#each this.benefits as |benefitItem index|}}
|
{{#each this.benefits as |benefitItem index|}}
|
||||||
<li>{{svg-jar "check-2"}} <span>{{benefitItem.name}}</span></li>
|
<li>{{svg-jar "check-2"}} <span>{{benefitItem.name}}</span></li>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</ul>
|
</ul>
|
||||||
{{else}}
|
{{else}}
|
||||||
<ul class="placeholder">
|
<ul class="placeholder" data-test-tierpreview-benefits>
|
||||||
{{#if this.isFreeProduct}}
|
{{#if this.isFreeProduct}}
|
||||||
<li>{{svg-jar "check-2"}} <span>Access to all public posts</span></li>
|
<li>{{svg-jar "check-2"}} <span>Access to all public posts</span></li>
|
||||||
{{else}}
|
{{else}}
|
||||||
@ -194,12 +199,12 @@
|
|||||||
{{/if}}
|
{{/if}}
|
||||||
</ul>
|
</ul>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<div class="price">
|
<div class="price" data-test-tierpreview-price>
|
||||||
{{#if this.isFreeProduct}}
|
{{#if this.isFreeProduct}}
|
||||||
<span class="monthly-price">
|
<span class="monthly-price">
|
||||||
<span class="currency">{{currency-symbol this.currency}}</span>
|
<span class="currency">{{currency-symbol this.currency}}</span>
|
||||||
0
|
0
|
||||||
</span>
|
</span>
|
||||||
{{else}}
|
{{else}}
|
||||||
{{#if this.stripeMonthlyAmount}}
|
{{#if this.stripeMonthlyAmount}}
|
||||||
<span class="monthly-price">
|
<span class="monthly-price">
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<div class="mb4 gh-setting-large-dropdown">
|
<div class="mb4 gh-setting-large-dropdown" data-test-default-post-access>
|
||||||
<div class="gh-expandable-header">
|
<div class="gh-expandable-header">
|
||||||
<div>
|
<div>
|
||||||
<h4 class="gh-expandable-title">Default post access</h4>
|
<h4 class="gh-expandable-title">Default post access</h4>
|
||||||
@ -15,7 +15,7 @@
|
|||||||
@dropdownClass="gh-setting-dropdown-list"
|
@dropdownClass="gh-setting-dropdown-list"
|
||||||
as |option|
|
as |option|
|
||||||
>
|
>
|
||||||
<div class="gh-setting-dropdown-content">
|
<div class="gh-setting-dropdown-content" data-test-default-post-access-option={{option.value}}>
|
||||||
{{svg-jar option.icon class=(concat "w8 h8 mr2 fill-" (or option.icon_color "green"))}}
|
{{svg-jar option.icon class=(concat "w8 h8 mr2 fill-" (or option.icon_color "green"))}}
|
||||||
<div class="gh-radio-label">
|
<div class="gh-radio-label">
|
||||||
{{option.name}}<br>
|
{{option.name}}<br>
|
||||||
@ -24,7 +24,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</PowerSelect>
|
</PowerSelect>
|
||||||
{{#if this.hasVisibilityFilter}}
|
{{#if this.hasVisibilityFilter}}
|
||||||
<div class="mt2">
|
<div class="mt2" data-test-default-post-access-tiers>
|
||||||
<GhPostSettingsMenu::VisibilitySegmentSelect
|
<GhPostSettingsMenu::VisibilitySegmentSelect
|
||||||
@selectDefaultProduct={{true}}
|
@selectDefaultProduct={{true}}
|
||||||
@tiers={{this.visibilityTiers}}
|
@tiers={{this.visibilityTiers}}
|
||||||
|
@ -46,7 +46,7 @@ export default class SettingsMembersDefaultPostAccess extends Component {
|
|||||||
|
|
||||||
get visibilityTiers() {
|
get visibilityTiers() {
|
||||||
const visibilityTiersData = this.settings.get('defaultContentVisibilityTiers');
|
const visibilityTiersData = this.settings.get('defaultContentVisibilityTiers');
|
||||||
return visibilityTiersData.map((id) => {
|
return (visibilityTiersData || []).map((id) => {
|
||||||
return {id};
|
return {id};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<div class="gh-setting-richdd-container">
|
<div class="gh-setting-richdd-container" data-test-members-subscription-access>
|
||||||
<div class="gh-expandable-header">
|
<div class="gh-expandable-header">
|
||||||
<div>
|
<div>
|
||||||
<h4 class="gh-expandable-title">Subscription access</h4>
|
<h4 class="gh-expandable-title">Subscription access</h4>
|
||||||
@ -14,7 +14,7 @@
|
|||||||
@dropdownClass="gh-setting-dropdown-list"
|
@dropdownClass="gh-setting-dropdown-list"
|
||||||
as |option|
|
as |option|
|
||||||
>
|
>
|
||||||
<div class="gh-setting-dropdown-content">
|
<div class="gh-setting-dropdown-content" data-test-members-subscription-option={{option.value}}>
|
||||||
{{svg-jar option.icon class=(concat "w8 h8 mr2 fill-" (or option.icon_color "green"))}}
|
{{svg-jar option.icon class=(concat "w8 h8 mr2 fill-" (or option.icon_color "green"))}}
|
||||||
<div class="gh-radio-label">
|
<div class="gh-radio-label">
|
||||||
{{option.name}}<br>
|
{{option.name}}<br>
|
||||||
|
@ -50,7 +50,7 @@
|
|||||||
<div class="gh-setting-members-portalpreview">
|
<div class="gh-setting-members-portalpreview">
|
||||||
<div class="gh-setting-members-portal-mock {{if (feature "multipleProducts") "mock-enabled"}}">
|
<div class="gh-setting-members-portal-mock {{if (feature "multipleProducts") "mock-enabled"}}">
|
||||||
{{#if (or (eq this.settings.membersSignupAccess 'none') this.switchFromNoneTask.isRunning)}}
|
{{#if (or (eq this.settings.membersSignupAccess 'none') this.switchFromNoneTask.isRunning)}}
|
||||||
<div class="gh-setting-members-portal-disabled">
|
<div class="gh-setting-members-portal-disabled" data-test-portal-preview-disabled>
|
||||||
<span class="lightgrey">{{svg-jar "portal-logo-stroke"}}</span>
|
<span class="lightgrey">{{svg-jar "portal-logo-stroke"}}</span>
|
||||||
<h4>Portal disabled</h4>
|
<h4>Portal disabled</h4>
|
||||||
<p>Change your Subscription Access setting to re-enable Portal</p>
|
<p>Change your Subscription Access setting to re-enable Portal</p>
|
||||||
@ -61,7 +61,8 @@
|
|||||||
@src={{this.portalPreviewUrl}}
|
@src={{this.portalPreviewUrl}}
|
||||||
@invisibleUntilLoaded="portal-ready"
|
@invisibleUntilLoaded="portal-ready"
|
||||||
@onInserted={{this.portalPreviewInserted}}
|
@onInserted={{this.portalPreviewInserted}}
|
||||||
@onDestroyed={{this.portalPreviewDestroyed}} />
|
@onDestroyed={{this.portalPreviewDestroyed}}
|
||||||
|
data-test-iframe="portal-preview"/>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -85,11 +86,11 @@
|
|||||||
<h4 class="gh-expandable-title">Free</h4>
|
<h4 class="gh-expandable-title">Free</h4>
|
||||||
<p class="gh-expandable-description">Free member sign up settings</p>
|
<p class="gh-expandable-description">Free member sign up settings</p>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="gh-btn" {{on "click" (toggle "freeOpen" this)}} data-test-toggle-pub-info><span>{{if this.freeOpen "Close" "Expand"}}</span></button>
|
<button type="button" class="gh-btn" {{on "click" (toggle "freeOpen" this)}} data-test-button="toggle-free-settings"><span>{{if this.freeOpen "Close" "Expand"}}</span></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="gh-expandable-content">
|
<div class="gh-expandable-content">
|
||||||
{{#liquid-if this.freeOpen}}
|
{{#liquid-if this.freeOpen}}
|
||||||
<div class="gh-setting-content-extended">
|
<div class="gh-setting-content-extended" data-test-free-settings-expanded>
|
||||||
{{#if (feature "multipleProducts")}}
|
{{#if (feature "multipleProducts")}}
|
||||||
<GhProductCard
|
<GhProductCard
|
||||||
@product={{this.freeProduct}}
|
@product={{this.freeProduct}}
|
||||||
@ -107,6 +108,7 @@
|
|||||||
@setResult={{this.setFreeSignupRedirect}}
|
@setResult={{this.setFreeSignupRedirect}}
|
||||||
@validateUrl={{this.validateFreeSignupRedirect}}
|
@validateUrl={{this.validateFreeSignupRedirect}}
|
||||||
@placeholder={{readonly this.siteUrl}}
|
@placeholder={{readonly this.siteUrl}}
|
||||||
|
data-test-input="old-free-welcome-page"
|
||||||
/>
|
/>
|
||||||
<GhErrorMessage
|
<GhErrorMessage
|
||||||
@errors={{this.settings.errors}}
|
@errors={{this.settings.errors}}
|
||||||
@ -124,6 +126,7 @@
|
|||||||
@setResult={{this.setWelcomePageURL}}
|
@setResult={{this.setWelcomePageURL}}
|
||||||
@validateUrl={{this.validateWelcomePageURL}}
|
@validateUrl={{this.validateWelcomePageURL}}
|
||||||
@placeholder={{readonly this.siteUrl}}
|
@placeholder={{readonly this.siteUrl}}
|
||||||
|
data-test-input="free-welcome-page"
|
||||||
/>
|
/>
|
||||||
<p>Redirect to this URL after signup for a free membership</p>
|
<p>Redirect to this URL after signup for a free membership</p>
|
||||||
</GhFormGroup>
|
</GhFormGroup>
|
||||||
|
@ -21,7 +21,28 @@ export default function mockProducts(server) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
server.put('/products/:id/');
|
server.put('/products/:id/', function ({products, productBenefits}, {params}) {
|
||||||
|
const attrs = this.normalizedRequestAttrs();
|
||||||
|
const product = products.find(params.id);
|
||||||
|
|
||||||
|
const benefitAttrs = attrs.benefits;
|
||||||
|
delete attrs.benefits;
|
||||||
|
|
||||||
|
product.update(attrs);
|
||||||
|
|
||||||
|
benefitAttrs.forEach((benefit) => {
|
||||||
|
if (benefit.id) {
|
||||||
|
const productBenefit = productBenefits.find(benefit.id);
|
||||||
|
productBenefit.product = product;
|
||||||
|
productBenefit.save();
|
||||||
|
} else {
|
||||||
|
product.createProductBenefit(benefit);
|
||||||
|
product.save();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return product.save();
|
||||||
|
});
|
||||||
|
|
||||||
server.del('/products/:id/');
|
server.del('/products/:id/');
|
||||||
}
|
}
|
||||||
|
25
ghost/admin/mirage/factories/product.js
Normal file
25
ghost/admin/mirage/factories/product.js
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import {Factory} from 'ember-cli-mirage';
|
||||||
|
|
||||||
|
export default Factory.extend({
|
||||||
|
name(i) { return `Product ${i}`; },
|
||||||
|
description(i) { return `Description for product ${i}`; },
|
||||||
|
active: true,
|
||||||
|
slug(i) { return `product-${i}`;},
|
||||||
|
type: 'paid',
|
||||||
|
monthly_price() {
|
||||||
|
return {
|
||||||
|
interval: 'month',
|
||||||
|
nickname: 'Monthly',
|
||||||
|
currency: 'usd',
|
||||||
|
amount: 500
|
||||||
|
};
|
||||||
|
},
|
||||||
|
yearly_price() {
|
||||||
|
return {
|
||||||
|
interval: 'year',
|
||||||
|
nickname: 'Yearly',
|
||||||
|
currency: 'usd',
|
||||||
|
amount: 5000
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
@ -1,24 +1,31 @@
|
|||||||
/* eslint-disable camelcase */
|
/* eslint-disable camelcase */
|
||||||
export default [
|
export default [
|
||||||
{
|
{
|
||||||
id: 1,
|
id: '1',
|
||||||
name: 'Test Product',
|
active: true,
|
||||||
slug: 'test-product',
|
benefits: [],
|
||||||
monthly_price: {
|
createdAt: '2022-02-04T13:11:40.000Z',
|
||||||
interval: 'month',
|
description: null,
|
||||||
nickname: 'Monthly',
|
monthlyPrice: null,
|
||||||
currency: 'usd',
|
name: 'Free',
|
||||||
amount: 500
|
slug: 'free',
|
||||||
},
|
type: 'free',
|
||||||
yearly_price: {
|
updatedAt: '2022-02-04T13:34:53.000Z',
|
||||||
interval: 'year',
|
welcomePageUrl: null,
|
||||||
nickname: 'Yearly',
|
yearlyPrice: null
|
||||||
currency: 'usd',
|
},
|
||||||
amount: 5000
|
{
|
||||||
},
|
id: '2',
|
||||||
created_at: '2015-11-13T16:01:29.131Z',
|
active: true,
|
||||||
created_by: 1,
|
benefits: [],
|
||||||
updated_at: '2015-11-13T16:01:29.131Z',
|
createdAt: '2022-02-04T13:11:40.000Z',
|
||||||
updated_by: 1
|
description: null,
|
||||||
|
monthlyPrice: null,
|
||||||
|
name: 'Default Product',
|
||||||
|
slug: 'default-product',
|
||||||
|
type: 'paid',
|
||||||
|
updatedAt: '2022-02-04T13:11:40.000Z',
|
||||||
|
welcomePageUrl: null,
|
||||||
|
yearlyPrice: null
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
@ -222,5 +222,35 @@ export default [
|
|||||||
updated_at: '2021-11-01T15:44:43.494Z',
|
updated_at: '2021-11-01T15:44:43.494Z',
|
||||||
updated_by: 1,
|
updated_by: 1,
|
||||||
value: 'casper'
|
value: 'casper'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 28,
|
||||||
|
created_at: '2022-02-16T09:38:00.000Z',
|
||||||
|
created_by: 1,
|
||||||
|
key: 'members_signup_access',
|
||||||
|
group: 'members',
|
||||||
|
updated_at: '2022-02-16T09:38:00.000Z',
|
||||||
|
updated_by: 1,
|
||||||
|
value: 'all'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 29,
|
||||||
|
created_at: '2022-02-16T09:38:00.000Z',
|
||||||
|
created_by: 1,
|
||||||
|
key: 'default_content_visibility',
|
||||||
|
group: 'members',
|
||||||
|
updated_at: '2022-02-16T09:38:00.000Z',
|
||||||
|
updated_by: 1,
|
||||||
|
value: 'public'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 30,
|
||||||
|
created_at: '2022-02-16T09:38:00.000Z',
|
||||||
|
created_by: 1,
|
||||||
|
key: 'default_content_visibility_tiers',
|
||||||
|
group: 'members',
|
||||||
|
updated_at: '2022-02-16T09:38:00.000Z',
|
||||||
|
updated_by: 1,
|
||||||
|
value: '[]'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
5
ghost/admin/mirage/models/product-benefit.js
Normal file
5
ghost/admin/mirage/models/product-benefit.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import {Model, belongsTo} from 'ember-cli-mirage';
|
||||||
|
|
||||||
|
export default Model.extend({
|
||||||
|
product: belongsTo('product')
|
||||||
|
});
|
@ -1,4 +1,7 @@
|
|||||||
import {Model} from 'ember-cli-mirage';
|
import {Model, hasMany} from 'ember-cli-mirage';
|
||||||
|
|
||||||
export default Model.extend({
|
export default Model.extend({
|
||||||
|
// ran into odd relationship bugs when called `benefits`
|
||||||
|
// serializer will rename to `benefits`
|
||||||
|
productBenefits: hasMany()
|
||||||
});
|
});
|
||||||
|
3
ghost/admin/mirage/serializers/product-benefit.js
Normal file
3
ghost/admin/mirage/serializers/product-benefit.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import BaseSerializer from './application';
|
||||||
|
|
||||||
|
export default BaseSerializer.extend({});
|
22
ghost/admin/mirage/serializers/product.js
Normal file
22
ghost/admin/mirage/serializers/product.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import BaseSerializer from './application';
|
||||||
|
import {underscore} from '@ember/string';
|
||||||
|
|
||||||
|
export default BaseSerializer.extend({
|
||||||
|
embed: true,
|
||||||
|
|
||||||
|
include(/*request*/) {
|
||||||
|
let includes = [];
|
||||||
|
|
||||||
|
includes.push('productBenefits');
|
||||||
|
|
||||||
|
return includes;
|
||||||
|
},
|
||||||
|
|
||||||
|
keyForEmbeddedRelationship(relationshipName) {
|
||||||
|
if (relationshipName === 'productBenefits') {
|
||||||
|
return 'benefits';
|
||||||
|
}
|
||||||
|
|
||||||
|
return underscore(relationshipName);
|
||||||
|
}
|
||||||
|
});
|
@ -42,8 +42,9 @@ describe('Acceptance: Offers', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('it renders, can be navigated, can edit offer', async function () {
|
it('it renders, can be navigated, can edit offer', async function () {
|
||||||
let offer1 = this.server.create('offer', {createdAt: moment.utc().subtract(1, 'day').valueOf()});
|
const product = this.server.create('product');
|
||||||
this.server.create('offer', {createdAt: moment.utc().subtract(2, 'day').valueOf()});
|
let offer1 = this.server.create('offer', {tier: {id: product.id}, createdAt: moment.utc().subtract(1, 'day').valueOf()});
|
||||||
|
this.server.create('offer', {tier: {id: product.id}, createdAt: moment.utc().subtract(2, 'day').valueOf()});
|
||||||
|
|
||||||
await visit('/offers');
|
await visit('/offers');
|
||||||
|
|
||||||
|
283
ghost/admin/tests/acceptance/settings/membership-test.js
Normal file
283
ghost/admin/tests/acceptance/settings/membership-test.js
Normal file
@ -0,0 +1,283 @@
|
|||||||
|
import {authenticateSession, invalidateSession} from 'ember-simple-auth/test-support';
|
||||||
|
import {blur, click, currentURL, fillIn, find, findAll} from '@ember/test-helpers';
|
||||||
|
import {enableLabsFlag} from '../../helpers/labs-flag';
|
||||||
|
import {expect} from 'chai';
|
||||||
|
import {setupApplicationTest} from 'ember-mocha';
|
||||||
|
import {setupMirage} from 'ember-cli-mirage/test-support';
|
||||||
|
import {visit} from '../../helpers/visit';
|
||||||
|
|
||||||
|
describe('Acceptance: Settings - Membership', function () {
|
||||||
|
const hooks = setupApplicationTest();
|
||||||
|
setupMirage(hooks);
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
enableLabsFlag(this.server, 'multipleProducts');
|
||||||
|
enableLabsFlag(this.server, 'tierWelcomePages');
|
||||||
|
enableLabsFlag(this.server, 'tierName');
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(async function () {
|
||||||
|
this.server.loadFixtures('configs');
|
||||||
|
this.server.loadFixtures('products');
|
||||||
|
|
||||||
|
const role = this.server.create('role', {name: 'Owner'});
|
||||||
|
this.server.create('user', {roles: [role]});
|
||||||
|
|
||||||
|
return await authenticateSession();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('permissions', function () {
|
||||||
|
let visitAs;
|
||||||
|
|
||||||
|
before(function () {
|
||||||
|
visitAs = async (roleName) => {
|
||||||
|
const role = this.server.create('role', {name: roleName});
|
||||||
|
this.server.create('user', {roles: [role]});
|
||||||
|
await authenticateSession();
|
||||||
|
await visit('/settings/members');
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(async function () {
|
||||||
|
this.server.db.users.remove();
|
||||||
|
await invalidateSession();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('allows Owners', async function () {
|
||||||
|
await visitAs('Owner');
|
||||||
|
expect(currentURL()).to.equal('/settings/members');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('allows Administrators', async function () {
|
||||||
|
await visitAs('Administrator');
|
||||||
|
expect(currentURL()).to.equal('/settings/members');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('disallows Editors', async function () {
|
||||||
|
await visitAs('Editor');
|
||||||
|
expect(currentURL()).to.not.equal('/settings/members');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('disallows Authors', async function () {
|
||||||
|
await visitAs('Author');
|
||||||
|
expect(currentURL()).to.not.equal('/settings/members');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('disallows Contributors', async function () {
|
||||||
|
await visitAs('Contributor');
|
||||||
|
expect(currentURL()).to.not.equal('/settings/members');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can change subscription access', async function () {
|
||||||
|
await visit('/settings/members');
|
||||||
|
|
||||||
|
expect(this.server.db.settings.findBy({key: 'members_signup_access'}).value).to.equal('all');
|
||||||
|
expect(find('[data-test-members-subscription-option="all"]'), 'initial selection is "all"').to.exist;
|
||||||
|
expect(find('[data-test-iframe="portal-preview"]'), 'initial preview src matches "all"')
|
||||||
|
.to.have.attribute('src').match(/membersSignupAccess=all/);
|
||||||
|
|
||||||
|
// open dropdown
|
||||||
|
await click('[data-test-members-subscription-option="all"]');
|
||||||
|
|
||||||
|
// all settings exist in dropdown
|
||||||
|
expect(find('.ember-power-select-options [data-test-members-subscription-option="all"]'), 'all option').to.exist;
|
||||||
|
expect(find('.ember-power-select-options [data-test-members-subscription-option="invite"]'), 'invite option').to.exist;
|
||||||
|
expect(find('.ember-power-select-options [data-test-members-subscription-option="none"]'), 'none option').to.exist;
|
||||||
|
|
||||||
|
// switch to invite
|
||||||
|
await click('.ember-power-select-options [data-test-members-subscription-option="invite"]');
|
||||||
|
|
||||||
|
expect(find('.ember-power-select-options'), 'dropdown closes').to.not.exist;
|
||||||
|
expect(find('[data-test-members-subscription-option="invite"]'), 'invite option shown after selected').to.exist;
|
||||||
|
expect(find('[data-test-iframe="portal-preview"]'))
|
||||||
|
.to.have.attribute('src').match(/membersSignupAccess=invite/);
|
||||||
|
|
||||||
|
await click('[data-test-button="save-settings"]');
|
||||||
|
|
||||||
|
expect(this.server.db.settings.findBy({key: 'members_signup_access'}).value).to.equal('invite');
|
||||||
|
|
||||||
|
// switch to nobody
|
||||||
|
await click('[data-test-members-subscription-option="invite"]');
|
||||||
|
await click('.ember-power-select-options [data-test-members-subscription-option="none"]');
|
||||||
|
|
||||||
|
expect(find('.ember-power-select-options'), 'dropdown closes').to.not.exist;
|
||||||
|
expect(find('[data-test-members-subscription-option="none"]'), 'none option shown after selected').to.exist;
|
||||||
|
expect(find('[data-test-iframe="portal-preview"]')).to.not.exist;
|
||||||
|
expect(find('[data-test-portal-preview-disabled]')).to.exist;
|
||||||
|
|
||||||
|
expect(find('[data-test-default-post-access] .ember-basic-dropdown-trigger')).to.have.attribute('aria-disabled', 'true');
|
||||||
|
|
||||||
|
await click('[data-test-button="save-settings"]');
|
||||||
|
|
||||||
|
expect(this.server.db.settings.findBy({key: 'members_signup_access'}).value).to.equal('none');
|
||||||
|
|
||||||
|
// automatically saves when switching back off nobody
|
||||||
|
await click('[data-test-members-subscription-option="none"]');
|
||||||
|
await click('.ember-power-select-options [data-test-members-subscription-option="invite"]');
|
||||||
|
expect(this.server.db.settings.findBy({key: 'members_signup_access'}).value).to.equal('invite');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can change default post access', async function () {
|
||||||
|
await visit('/settings/members');
|
||||||
|
|
||||||
|
// fixtures match what we expect
|
||||||
|
expect(this.server.db.settings.findBy({key: 'default_content_visibility'}).value).to.equal('public');
|
||||||
|
expect(this.server.db.settings.findBy({key: 'default_content_visibility_tiers'}).value).to.equal('[]');
|
||||||
|
|
||||||
|
expect(find('[data-test-default-post-access-option="public"]'), 'initial selection is "public"').to.exist;
|
||||||
|
expect(find('[data-test-default-post-access-tiers]')).to.not.exist;
|
||||||
|
|
||||||
|
// open dropdown
|
||||||
|
await click('[data-test-default-post-access-option="public"]');
|
||||||
|
|
||||||
|
// all settings exist in dropdown
|
||||||
|
expect(find('.ember-power-select-options [data-test-default-post-access-option="public"]'), 'public option').to.exist;
|
||||||
|
expect(find('.ember-power-select-options [data-test-default-post-access-option="members"]'), 'members-only option').to.exist;
|
||||||
|
expect(find('.ember-power-select-options [data-test-default-post-access-option="paid"]'), 'paid-only option').to.exist;
|
||||||
|
expect(find('.ember-power-select-options [data-test-default-post-access-option="tiers"]'), 'specific tiers option').to.exist;
|
||||||
|
|
||||||
|
// switch to members only
|
||||||
|
await click('.ember-power-select-options [data-test-default-post-access-option="members"]');
|
||||||
|
await click('[data-test-button="save-settings"]');
|
||||||
|
|
||||||
|
expect(this.server.db.settings.findBy({key: 'default_content_visibility'}).value).to.equal('members');
|
||||||
|
expect(this.server.db.settings.findBy({key: 'default_content_visibility_tiers'}).value).to.equal('[]');
|
||||||
|
|
||||||
|
expect(find('[data-test-default-post-access-option="members"]'), 'post-members selection is "members"').to.exist;
|
||||||
|
expect(find('[data-test-default-post-access-tiers]')).to.not.exist;
|
||||||
|
|
||||||
|
// can switch to specific tiers
|
||||||
|
await click('[data-test-default-post-access-option="members"]');
|
||||||
|
await click('.ember-power-select-options [data-test-default-post-access-option="tiers"]');
|
||||||
|
|
||||||
|
// tiers input is shown
|
||||||
|
expect(find('[data-test-default-post-access-tiers]')).to.exist;
|
||||||
|
|
||||||
|
// open tiers dropdown
|
||||||
|
await click('[data-test-default-post-access-tiers] .ember-basic-dropdown-trigger');
|
||||||
|
|
||||||
|
// paid tiers are available in tiers input
|
||||||
|
expect(find('[data-test-default-post-access-tiers] [data-test-visibility-segment-option="Default Product"]')).to.exist;
|
||||||
|
|
||||||
|
// select tier
|
||||||
|
await click('[data-test-default-post-access-tiers] [data-test-visibility-segment-option="Default Product"]');
|
||||||
|
|
||||||
|
// save
|
||||||
|
await click('[data-test-button="save-settings"]');
|
||||||
|
expect(this.server.db.settings.findBy({key: 'default_content_visibility'}).value).to.equal('tiers');
|
||||||
|
expect(this.server.db.settings.findBy({key: 'default_content_visibility_tiers'}).value).to.equal('["2"]');
|
||||||
|
|
||||||
|
// switch back to non-tiers option
|
||||||
|
await click('[data-test-default-post-access-option="tiers"]');
|
||||||
|
await click('.ember-power-select-options [data-test-default-post-access-option="paid"]');
|
||||||
|
|
||||||
|
expect(find('[data-test-default-post-access-tiers]')).to.not.exist;
|
||||||
|
|
||||||
|
await click('[data-test-button="save-settings"]');
|
||||||
|
expect(this.server.db.settings.findBy({key: 'default_content_visibility'}).value).to.equal('paid');
|
||||||
|
expect(this.server.db.settings.findBy({key: 'default_content_visibility_tiers'}).value).to.equal('["2"]');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can manage free tier', async function () {
|
||||||
|
await visit('/settings/members');
|
||||||
|
await click('[data-test-button="toggle-free-settings"]');
|
||||||
|
expect(find('[data-test-free-settings-expanded]'), 'expanded free settings').to.exist;
|
||||||
|
|
||||||
|
// we aren't viewing the non-labs-flag input
|
||||||
|
expect(find('[data-test-input="old-free-welcome-page"]')).to.not.exist;
|
||||||
|
|
||||||
|
// it can set free signup welcome page
|
||||||
|
|
||||||
|
// initial value
|
||||||
|
expect(find('[data-test-input="free-welcome-page"]')).to.exist;
|
||||||
|
expect(find('[data-test-input="free-welcome-page"]')).to.have.value('');
|
||||||
|
|
||||||
|
// saving
|
||||||
|
await fillIn('[data-test-input="free-welcome-page"]', 'not a url');
|
||||||
|
await blur('[data-test-input="free-welcome-page"]');
|
||||||
|
await click('[data-test-button="save-settings"]');
|
||||||
|
|
||||||
|
expect(this.server.db.products.findBy({slug: 'free'}).welcomePageUrl)
|
||||||
|
.to.equal('/not%20a%20url');
|
||||||
|
|
||||||
|
// re-rendering will insert full URL in welcome page input
|
||||||
|
await visit('/settings');
|
||||||
|
await visit('/settings/members');
|
||||||
|
|
||||||
|
expect(find('[data-test-input="free-welcome-page"]')).to.exist;
|
||||||
|
expect(find('[data-test-input="free-welcome-page"]'))
|
||||||
|
.to.have.value('http://localhost:4200/not%20a%20url');
|
||||||
|
|
||||||
|
// it can manage free tier description and benefits
|
||||||
|
|
||||||
|
// initial free tier details are as expected
|
||||||
|
expect(find('[data-test-product-card="free"]')).to.exist;
|
||||||
|
expect(find('[data-test-product-card="free"] [data-test-name]')).to.contain.text('Free');
|
||||||
|
expect(find('[data-test-product-card="free"] [data-test-description]')).to.contain.text('No description');
|
||||||
|
expect(find('[data-test-product-card="free"] [data-test-benefits]')).to.contain.text('No benefits');
|
||||||
|
expect(find('[data-test-product-card="free"] [data-test-free-price]')).to.exist;
|
||||||
|
|
||||||
|
// open modal
|
||||||
|
await click('[data-test-product-card="free"] [data-test-button="edit-product"]');
|
||||||
|
|
||||||
|
// initial modal state is as expected
|
||||||
|
const modal = '[data-test-modal="edit-product"]';
|
||||||
|
expect(find(modal)).to.exist;
|
||||||
|
expect(find(`${modal} [data-test-input="product-name"]`)).to.not.exist;
|
||||||
|
expect(find(`${modal} [data-test-input="product-description"]`)).to.not.exist;
|
||||||
|
expect(find(`${modal} [data-test-input="free-product-description"]`)).to.exist;
|
||||||
|
expect(find(`${modal} [data-test-input="free-product-description"]`)).to.have.value('');
|
||||||
|
expect(find(`${modal} [data-test-formgroup="prices"]`)).to.not.exist;
|
||||||
|
expect(find(`${modal} [data-test-benefit-item="new"]`)).to.exist;
|
||||||
|
expect(findAll(`${modal} [data-test-benefit-item]`).length).to.equal(1);
|
||||||
|
|
||||||
|
expect(find(`${modal} [data-test-tierpreview-title]`)).to.contain.text('Free Membership Preview');
|
||||||
|
expect(find(`${modal} [data-test-tierpreview-description]`)).to.contain.text('Free preview of');
|
||||||
|
expect(find(`${modal} [data-test-tierpreview-benefits]`)).to.contain.text('Access to all public posts');
|
||||||
|
expect(find(`${modal} [data-test-tierpreview-price]`).textContent).to.match(/\$\s+0/);
|
||||||
|
|
||||||
|
// can change description
|
||||||
|
await fillIn(`${modal} [data-test-input="free-product-description"]`, 'Test description');
|
||||||
|
expect(find(`${modal} [data-test-tierpreview-description]`)).to.contain.text('Test description');
|
||||||
|
|
||||||
|
// can manage benefits
|
||||||
|
const newBenefit = `${modal} [data-test-benefit-item="new"]`;
|
||||||
|
await fillIn(`${newBenefit} [data-test-input="benefit-label"]`, 'First benefit');
|
||||||
|
await click(`${newBenefit} [data-test-button="add-benefit"]`);
|
||||||
|
|
||||||
|
expect(find(`${modal} [data-test-tierpreview-benefits]`)).to.contain.text('First benefit');
|
||||||
|
|
||||||
|
expect(find(`${modal} [data-test-benefit-item="0"]`)).to.exist;
|
||||||
|
expect(find(`${modal} [data-test-benefit-item="new"]`)).to.exist;
|
||||||
|
|
||||||
|
await click(`${newBenefit} [data-test-button="add-benefit"]`);
|
||||||
|
expect(find(`${newBenefit}`)).to.contain.text('Please enter a benefit');
|
||||||
|
|
||||||
|
await fillIn(`${newBenefit} [data-test-input="benefit-label"]`, 'Second benefit');
|
||||||
|
await click(`${newBenefit} [data-test-button="add-benefit"]`);
|
||||||
|
|
||||||
|
expect(find(`${modal} [data-test-tierpreview-benefits]`)).to.contain.text('Second benefit');
|
||||||
|
expect(findAll(`${modal} [data-test-tierpreview-benefits] li`).length).to.equal(2);
|
||||||
|
|
||||||
|
await click(`${modal} [data-test-benefit-item="0"] [data-test-button="delete-benefit"]`);
|
||||||
|
|
||||||
|
expect(find(`${modal} [data-test-tierpreview-benefits]`)).to.not.contain.text('First benefit');
|
||||||
|
expect(findAll(`${modal} [data-test-tierpreview-benefits] li`).length).to.equal(1);
|
||||||
|
|
||||||
|
await click('[data-test-button="save-product"]');
|
||||||
|
|
||||||
|
expect(find(`${modal}`)).to.not.exist;
|
||||||
|
expect(find('[data-test-product-card="free"] [data-test-name]')).to.contain.text('Free');
|
||||||
|
expect(find('[data-test-product-card="free"] [data-test-description]')).to.contain.text('Test description');
|
||||||
|
expect(find('[data-test-product-card="free"] [data-test-benefits]')).to.contain.text('Benefits (1)');
|
||||||
|
expect(find('[data-test-product-card="free"] [data-test-benefits] li:nth-of-type(1)')).to.contain.text('Second benefit');
|
||||||
|
|
||||||
|
const freeProduct = this.server.db.products.findBy({slug: 'free'});
|
||||||
|
expect(freeProduct.description).to.equal('Test description');
|
||||||
|
expect(freeProduct.welcomePageUrl).to.equal('/not%20a%20url');
|
||||||
|
expect(freeProduct.productBenefitIds.length).to.equal(1);
|
||||||
|
const benefits = this.server.db.productBenefits.find(freeProduct.productBenefitIds);
|
||||||
|
expect(benefits[0].name).to.equal('Second benefit');
|
||||||
|
});
|
||||||
|
});
|
@ -10,7 +10,8 @@ export function enableLabsFlag(server, flag) {
|
|||||||
const config = server.schema.configs.first();
|
const config = server.schema.configs.first();
|
||||||
config.update({enableDeveloperExperiments: true});
|
config.update({enableDeveloperExperiments: true});
|
||||||
|
|
||||||
const labsSetting = {};
|
const existingSetting = server.db.settings.findBy({key: 'labs'}).value;
|
||||||
|
const labsSetting = existingSetting ? JSON.parse(existingSetting) : {};
|
||||||
labsSetting[flag] = true;
|
labsSetting[flag] = true;
|
||||||
|
|
||||||
server.db.settings.update({key: 'labs'}, {value: JSON.stringify(labsSetting)});
|
server.db.settings.update({key: 'labs'}, {value: JSON.stringify(labsSetting)});
|
||||||
|
Loading…
Reference in New Issue
Block a user