## Description Branded email layouts and other email templates can now contain up to 262,144 characters instead of the generic 20,000-character text limit. This supports customer layouts around 41 KB and 100 KB while retaining a defined application-level ceiling. The account and inbox API schemas now expose the same maximum. ## Closes [CW-7682](https://linear.app/chatwoot/issue/CW-7682/allow-larger-branded-email-templates) Related: [CW-7514](https://linear.app/chatwoot/issue/CW-7514/branded-html-email-templates-per-inboxbrand) ## Type of change - [x] Bug fix (non-breaking change which fixes an issue) - [x] This change requires a documentation update ## How Has This Been Tested? 1. As an administrator with `branded_email_templates` enabled, update an account branded layout with a 100 KB Liquid layout containing `{{ content_for_layout }}` and confirm the request succeeds. 2. Update an Email inbox layout with more than 262,144 characters and confirm the API returns `422`. 3. Confirm a layout at exactly 262,144 characters remains valid. ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [ ] I have commented on my code, particularly in hard-to-understand areas - [x] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [x] Any dependent changes have been merged and published in downstream modules
144 lines
5.7 KiB
Ruby
144 lines
5.7 KiB
Ruby
require 'rails_helper'
|
|
|
|
RSpec.describe EmailTemplate do
|
|
describe 'validations' do
|
|
it 'allows the same layout name across installation, account, and inbox scopes' do
|
|
account = create(:account)
|
|
inbox = create(:inbox, :with_email, account: account)
|
|
|
|
create(:email_template, :layout, account: nil)
|
|
create(:email_template, :layout, account: account)
|
|
inbox_template = build(:email_template, :layout, account: account, inbox: inbox)
|
|
|
|
expect(inbox_template).to be_valid
|
|
end
|
|
|
|
it 'allows an account-scoped layout after an inbox-scoped layout' do
|
|
account = create(:account)
|
|
inbox = create(:inbox, :with_email, account: account)
|
|
create(:email_template, :layout, account: account, inbox: inbox)
|
|
|
|
account_template = build(:email_template, :layout, account: account)
|
|
|
|
expect(account_template).to be_valid
|
|
end
|
|
|
|
it 'allows an installation-scoped layout after account and inbox-scoped layouts' do
|
|
account = create(:account)
|
|
inbox = create(:inbox, :with_email, account: account)
|
|
create(:email_template, :layout, account: account)
|
|
create(:email_template, :layout, account: account, inbox: inbox)
|
|
|
|
installation_template = build(:email_template, :layout, account: nil)
|
|
|
|
expect(installation_template).to be_valid
|
|
end
|
|
|
|
it 'rejects duplicate installation-scoped templates' do
|
|
create(:email_template)
|
|
duplicate_template = build(:email_template)
|
|
|
|
expect(duplicate_template).not_to be_valid
|
|
expect(duplicate_template.errors[:name]).to include('has already been taken')
|
|
end
|
|
|
|
it 'rejects duplicate account-scoped templates' do
|
|
account = create(:account)
|
|
create(:email_template, account: account)
|
|
duplicate_template = build(:email_template, account: account)
|
|
|
|
expect(duplicate_template).not_to be_valid
|
|
expect(duplicate_template.errors[:name]).to include('has already been taken')
|
|
end
|
|
|
|
it 'rejects duplicate inbox-scoped templates' do
|
|
account = create(:account)
|
|
inbox = create(:inbox, :with_email, account: account)
|
|
create(:email_template, account: account, inbox: inbox)
|
|
duplicate_template = build(:email_template, account: account, inbox: inbox)
|
|
|
|
expect(duplicate_template).not_to be_valid
|
|
expect(duplicate_template.errors[:name]).to include('has already been taken')
|
|
end
|
|
|
|
it 'requires branded layouts to include content_for_layout' do
|
|
template = build(:email_template, name: EmailTemplate::BRANDED_LAYOUT_NAME, template_type: :layout, body: '<html><body>No slot</body></html>')
|
|
|
|
expect(template).not_to be_valid
|
|
expect(template.errors[:body]).to include('must include {{ content_for_layout }}')
|
|
end
|
|
|
|
it 'allows email templates up to 262,144 characters' do
|
|
slot = '{{ content_for_layout }}'
|
|
body = "#{'a' * (described_class::MAX_BODY_LENGTH - slot.length)}#{slot}"
|
|
template = build(:email_template, :layout, account: create(:account), body: body)
|
|
|
|
expect(template).to be_valid
|
|
end
|
|
|
|
it 'rejects email templates larger than 262,144 characters' do
|
|
slot = '{{ content_for_layout }}'
|
|
body = "#{'a' * (described_class::MAX_BODY_LENGTH - slot.length + 1)}#{slot}"
|
|
template = build(:email_template, :layout, account: create(:account), body: body)
|
|
|
|
expect(template).not_to be_valid
|
|
expect(template.errors[:body]).to include('is too long (maximum is 262144 characters)')
|
|
end
|
|
|
|
it 'validates liquid syntax' do
|
|
template = build(:email_template, body: '{{ broken ')
|
|
|
|
expect(template).not_to be_valid
|
|
expect(template.errors[:body].first).to include('has invalid Liquid syntax')
|
|
end
|
|
|
|
it 'requires account to match inbox account when both are present' do
|
|
inbox = create(:inbox, :with_email)
|
|
other_account = create(:account)
|
|
template = build(:email_template, :layout, account: other_account, inbox: inbox)
|
|
|
|
expect(template).not_to be_valid
|
|
expect(template.errors[:account]).to include('must match inbox account')
|
|
end
|
|
end
|
|
|
|
describe '.branded_layout_for' do
|
|
it 'uses inbox, account, then installation fallback order' do
|
|
account = create(:account)
|
|
inbox = create(:inbox, :with_email, account: account)
|
|
create(:email_template, :layout, body: 'Global {{ content_for_layout }}')
|
|
account_template = create(:email_template, :layout, account: account, body: 'Account {{ content_for_layout }}')
|
|
|
|
expect(described_class.branded_layout_for(inbox: inbox, account: account, locale: :en).id).to eq(account_template.id)
|
|
|
|
inbox_template = create(:email_template, :layout, account: account, inbox: inbox, body: 'Inbox {{ content_for_layout }}')
|
|
|
|
expect(described_class.branded_layout_for(inbox: inbox, account: account, locale: :en).id).to eq(inbox_template.id)
|
|
end
|
|
end
|
|
|
|
describe '.update_account_branded_layout!' do
|
|
it 'creates and updates the account-scoped branded layout' do
|
|
account = create(:account)
|
|
|
|
described_class.update_account_branded_layout!(account: account, body: 'Account {{ content_for_layout }}')
|
|
|
|
template = described_class.account_branded_layout_template_for(account)
|
|
expect(template.body).to eq('Account {{ content_for_layout }}')
|
|
|
|
described_class.update_account_branded_layout!(account: account, body: 'Updated {{ content_for_layout }}')
|
|
|
|
expect(template.reload.body).to eq('Updated {{ content_for_layout }}')
|
|
end
|
|
|
|
it 'clears the account-scoped branded layout for blank bodies' do
|
|
account = create(:account)
|
|
create(:email_template, :layout, account: account)
|
|
|
|
described_class.update_account_branded_layout!(account: account, body: '')
|
|
|
|
expect(described_class.account_branded_layout_template_for(account)).to be_nil
|
|
end
|
|
end
|
|
end
|