Restructure omni services and add Chatwoot research snapshot

This commit is contained in:
Ruslan Bakiev
2026-02-21 11:11:27 +07:00
parent edea7a0034
commit b73babbbf6
7732 changed files with 978203 additions and 32 deletions

View File

@@ -0,0 +1,31 @@
FactoryBot.define do
factory :account_saml_settings do
account
sso_url { 'https://idp.example.com/saml/sso' }
certificate do
key = OpenSSL::PKey::RSA.new(2048)
cert = OpenSSL::X509::Certificate.new
cert.version = 2
cert.serial = 1
cert.subject = OpenSSL::X509::Name.parse('/C=US/ST=Test/L=Test/O=Test/CN=test.example.com')
cert.issuer = cert.subject
cert.public_key = key.public_key
cert.not_before = Time.zone.now
cert.not_after = cert.not_before + (365 * 24 * 60 * 60)
cert.sign(key, OpenSSL::Digest.new('SHA256'))
cert.to_pem
end
idp_entity_id { 'https://idp.example.com/saml/metadata' }
role_mappings { {} }
trait :with_role_mappings do
role_mappings do
{
'Administrators' => { 'role' => 1 },
'Agents' => { 'role' => 0 },
'Custom-Team' => { 'custom_role_id' => 5 }
}
end
end
end
end

View File

@@ -0,0 +1,9 @@
# frozen_string_literal: true
FactoryBot.define do
factory :account_user do
account
user
role { 'agent' }
end
end

View File

@@ -0,0 +1,10 @@
# frozen_string_literal: true
FactoryBot.define do
factory :account do
sequence(:name) { |n| "Account #{n}" }
status { 'active' }
domain { 'test.com' }
support_email { 'support@test.com' }
end
end

View File

@@ -0,0 +1,7 @@
FactoryBot.define do
factory :agent_bot_inbox do
inbox
agent_bot
status { 'active' }
end
end

View File

@@ -0,0 +1,17 @@
FactoryBot.define do
factory :agent_bot do
name { 'MyString' }
description { 'MyString' }
outgoing_url { 'localhost' }
bot_config { {} }
bot_type { 'webhook' }
trait :skip_validate do
to_create { |instance| instance.save(validate: false) }
end
trait :with_avatar do
avatar { fixture_file_upload(Rails.root.join('spec/assets/avatar.png'), 'image/png') }
end
end
end

View File

@@ -0,0 +1,21 @@
FactoryBot.define do
factory :agent_capacity_policy do
account
sequence(:name) { |n| "Agent Capacity Policy #{n}" }
description { 'Test agent capacity policy' }
exclusion_rules { {} }
trait :with_overall_capacity do
exclusion_rules { { 'overall_capacity' => 10 } }
end
trait :with_time_exclusions do
exclusion_rules do
{
'hours' => [0, 1, 2, 3, 4, 5],
'days' => %w[saturday sunday]
}
end
end
end
end

View File

@@ -0,0 +1,11 @@
FactoryBot.define do
factory :applied_sla do
sla_policy
conversation
sla_status { 'active' }
after(:build) do |applied_sla|
applied_sla.account ||= applied_sla.conversation&.account || create(:account)
end
end
end

View File

@@ -0,0 +1,14 @@
FactoryBot.define do
factory :article, class: 'Article' do
account
category { nil }
portal
locale { 'en' }
association :author, factory: :user
title { "#{Faker::Movie.title} #{SecureRandom.hex}" }
content { 'MyText' }
description { 'MyDescrption' }
status { :published }
views { 0 }
end
end

View File

@@ -0,0 +1,12 @@
FactoryBot.define do
factory :assignment_policy do
account
sequence(:name) { |n| "Assignment Policy #{n}" }
description { 'Test assignment policy description' }
assignment_order { 0 }
conversation_priority { 0 }
fair_distribution_limit { 10 }
fair_distribution_window { 3600 }
enabled { true }
end
end

View File

@@ -0,0 +1,20 @@
FactoryBot.define do
factory :automation_rule do
account
name { 'Test Automation Rule' }
event_name { 'conversation_status_changed' }
conditions { [{ 'values': ['resolved'], 'attribute_key': 'status', 'query_operator': nil, 'filter_operator': 'equal_to' }] }
actions do
[
{
'action_name' => 'send_email_to_team', 'action_params' => {
'message' => 'Please pay attention to this conversation, its from high priority customer', 'team_ids' => [1]
}
},
{ 'action_name' => 'assign_team', 'action_params' => [1] },
{ 'action_name' => 'add_label', 'action_params' => %w[support priority_customer] },
{ 'action_name' => 'assign_agent', 'action_params' => [1, 2, 3, 4] }
]
end
end
end

View File

@@ -0,0 +1,22 @@
# frozen_string_literal: true
FactoryBot.define do
factory :bot_message_card, class: Hash do
title { Faker::Book.name }
description { Faker::Movie.quote }
media_url { 'https://chatwoot-assets.local/sample.png' }
actions do
[{
text: 'Select',
type: 'postback',
payload: 'TACOS'
}, {
text: 'More info',
type: 'link',
uri: 'http://example.org'
}]
end
initialize_with { attributes }
end
end

View File

@@ -0,0 +1,10 @@
# frozen_string_literal: true
FactoryBot.define do
factory :bot_message_select, class: Hash do
title { Faker::Book.name }
value { Faker::Book.name }
initialize_with { attributes }
end
end

View File

@@ -0,0 +1,33 @@
# frozen_string_literal: true
FactoryBot.define do
factory :campaign do
sequence(:title) { |n| "Campaign #{n}" }
sequence(:message) { |n| "Campaign message #{n}" }
after(:build) do |campaign|
campaign.account ||= create(:account)
campaign.inbox ||= create(
:inbox,
account: campaign.account,
channel: create(:channel_widget, account: campaign.account)
)
end
trait :whatsapp do
after(:build) do |campaign|
campaign.inbox = create(
:inbox,
account: campaign.account,
channel: create(:channel_whatsapp, account: campaign.account)
)
campaign.template_params = {
'name' => 'ticket_status_updated',
'namespace' => '23423423_2342423_324234234_2343224',
'category' => 'UTILITY',
'language' => 'en',
'processed_params' => { 'name' => 'John', 'ticket_id' => '2332' }
}
end
end
end
end

View File

@@ -0,0 +1,9 @@
# frozen_string_literal: true
FactoryBot.define do
factory :canned_response do
content { 'Content' }
sequence(:short_code) { |n| "CODE#{n}" }
account
end
end

View File

@@ -0,0 +1,7 @@
FactoryBot.define do
factory :captain_assistant, class: 'Captain::Assistant' do
sequence(:name) { |n| "Assistant #{n}" }
description { 'Test description' }
association :account
end
end

View File

@@ -0,0 +1,13 @@
FactoryBot.define do
factory :captain_assistant_response, class: 'Captain::AssistantResponse' do
association :assistant, factory: :captain_assistant
association :account
sequence(:question) { |n| "Test question #{n}?" }
sequence(:answer) { |n| "Test answer #{n}" }
embedding { Array.new(1536) { rand(-1.0..1.0) } }
trait :with_document do
association :document, factory: :captain_document
end
end
end

View File

@@ -0,0 +1,8 @@
FactoryBot.define do
factory :captain_copilot_message, class: 'CopilotMessage' do
account
copilot_thread { association :captain_copilot_thread }
message { { content: 'This is a test message' } }
message_type { 0 }
end
end

View File

@@ -0,0 +1,8 @@
FactoryBot.define do
factory :captain_copilot_thread, class: 'CopilotThread' do
account
user
title { Faker::Lorem.sentence }
assistant { create(:captain_assistant, account: account) }
end
end

View File

@@ -0,0 +1,51 @@
FactoryBot.define do
factory :captain_custom_tool, class: 'Captain::CustomTool' do
sequence(:title) { |n| "Custom Tool #{n}" }
description { 'A custom HTTP tool for external API integration' }
endpoint_url { 'https://api.example.com/endpoint' }
http_method { 'GET' }
auth_type { 'none' }
auth_config { {} }
param_schema { [] }
enabled { true }
association :account
trait :with_post do
http_method { 'POST' }
request_template { '{ "key": "{{ value }}" }' }
end
trait :with_bearer_auth do
auth_type { 'bearer' }
auth_config { { token: 'test_bearer_token_123' } }
end
trait :with_basic_auth do
auth_type { 'basic' }
auth_config { { username: 'test_user', password: 'test_pass' } }
end
trait :with_api_key do
auth_type { 'api_key' }
auth_config { { key: 'test_api_key', location: 'header', name: 'X-API-Key' } }
end
trait :with_templates do
request_template { '{ "order_id": "{{ order_id }}", "source": "chatwoot" }' }
response_template { 'Order status: {{ response.status }}' }
end
trait :with_params do
param_schema do
[
{ 'name' => 'order_id', 'type' => 'string', 'description' => 'The order ID', 'required' => true },
{ 'name' => 'include_details', 'type' => 'boolean', 'description' => 'Include order details', 'required' => false }
]
end
end
trait :disabled do
enabled { false }
end
end
end

View File

@@ -0,0 +1,9 @@
FactoryBot.define do
factory :captain_document, class: 'Captain::Document' do
name { Faker::File.file_name }
external_link { Faker::Internet.unique.url }
content { Faker::Lorem.paragraphs.join("\n\n") }
association :assistant, factory: :captain_assistant
association :account
end
end

View File

@@ -0,0 +1,6 @@
FactoryBot.define do
factory :captain_inbox, class: 'CaptainInbox' do
association :captain_assistant, factory: :captain_assistant
association :inbox
end
end

View File

@@ -0,0 +1,11 @@
FactoryBot.define do
factory :captain_scenario, class: 'Captain::Scenario' do
sequence(:title) { |n| "Scenario #{n}" }
description { 'Test scenario description' }
instruction { 'Test scenario instruction for the assistant to follow' }
tools { [] }
enabled { true }
association :assistant, factory: :captain_assistant
association :account
end
end

View File

@@ -0,0 +1,13 @@
FactoryBot.define do
factory :category, class: 'Category' do
portal
name { 'MyString' }
description { 'MyText' }
position { 1 }
slug { name.parameterize }
after(:build) do |category|
category.account ||= category.portal.account
end
end
end

View File

@@ -0,0 +1,9 @@
FactoryBot.define do
factory :channel_api, class: 'Channel::Api' do
webhook_url { 'http://example.com' }
account
after(:create) do |channel_api|
create(:inbox, channel: channel_api, account: channel_api.account)
end
end
end

View File

@@ -0,0 +1,38 @@
# frozen_string_literal: true
FactoryBot.define do
factory :channel_email, class: 'Channel::Email' do
sequence(:email) { |n| "care-#{n}@example.com" }
sequence(:forward_to_email) { |n| "forward-#{n}@chatwoot.com" }
account
after(:create) do |channel_email|
create(:inbox, channel: channel_email, account: channel_email.account)
end
trait :microsoft_email do
imap_enabled { true }
imap_address { 'outlook.office365.com' }
imap_port { 993 }
imap_login { 'email@example.com' }
imap_password { '' }
imap_enable_ssl { true }
provider_config do
{
expires_on: Time.zone.now + 3600,
access_token: SecureRandom.hex,
refresh_token: SecureRandom.hex
}
end
provider { 'microsoft' }
end
trait :imap_email do
imap_enabled { true }
imap_address { 'imap.gmail.com' }
imap_port { 993 }
imap_login { 'email@example.com' }
imap_password { 'random-password' }
imap_enable_ssl { true }
end
end
end

View File

@@ -0,0 +1,28 @@
FactoryBot.define do
factory :channel_instagram, class: 'Channel::Instagram' do
account
access_token { SecureRandom.hex(32) }
instagram_id { SecureRandom.hex(16) }
expires_at { 60.days.from_now }
updated_at { 25.hours.ago }
before :create do |channel|
WebMock::API.stub_request(:post, "https://graph.instagram.com/v22.0/#{channel.instagram_id}/subscribed_apps")
.with(query: {
access_token: channel.access_token,
subscribed_fields: %w[messages message_reactions messaging_seen]
})
.to_return(status: 200, body: '', headers: {})
WebMock::API.stub_request(:delete, "https://graph.instagram.com/v22.0/#{channel.instagram_id}/subscribed_apps")
.with(query: {
access_token: channel.access_token
})
.to_return(status: 200, body: '', headers: {})
end
after(:create) do |channel|
create(:inbox, channel: channel, account: channel.account)
end
end
end

View File

@@ -0,0 +1,11 @@
# frozen_string_literal: true
FactoryBot.define do
factory :channel_line, class: 'Channel::Line' do
line_channel_id { SecureRandom.uuid }
line_channel_secret { SecureRandom.uuid }
line_channel_token { SecureRandom.uuid }
inbox
account
end
end

View File

@@ -0,0 +1,16 @@
FactoryBot.define do
factory :channel_sms, class: 'Channel::Sms' do
sequence(:phone_number) { |n| "+123456789#{n}1" }
account
provider_config do
{ 'account_id' => '1',
'application_id' => '1',
'api_key' => '1',
'api_secret' => '1' }
end
after(:create) do |channel_sms|
create(:inbox, channel: channel_sms, account: channel_sms.account)
end
end
end

View File

@@ -0,0 +1,16 @@
FactoryBot.define do
factory :channel_telegram, class: 'Channel::Telegram' do
bot_token { '2324234324' }
account
before(:create) do |channel_telegram|
# we are skipping some of the validation methods
channel_telegram.define_singleton_method(:ensure_valid_bot_token) { nil }
channel_telegram.define_singleton_method(:setup_telegram_webhook) { nil }
end
after(:create) do |channel_telegram|
create(:inbox, channel: channel_telegram, account: channel_telegram.account)
end
end
end

View File

@@ -0,0 +1,16 @@
# frozen_string_literal: true
FactoryBot.define do
factory :channel_tiktok, class: 'Channel::Tiktok' do
account
business_id { SecureRandom.hex(16) }
access_token { SecureRandom.hex(32) }
refresh_token { SecureRandom.hex(32) }
expires_at { 1.day.from_now }
refresh_token_expires_at { 30.days.from_now }
after(:create) do |channel|
create(:inbox, channel: channel, account: channel.account)
end
end
end

View File

@@ -0,0 +1,21 @@
# frozen_string_literal: true
FactoryBot.define do
factory :channel_voice, class: 'Channel::Voice' do
sequence(:phone_number) { |n| "+155512345#{n.to_s.rjust(2, '0')}" }
provider_config do
{
account_sid: "AC#{SecureRandom.hex(16)}",
auth_token: SecureRandom.hex(16),
api_key_sid: SecureRandom.hex(8),
api_key_secret: SecureRandom.hex(16),
twiml_app_sid: "AP#{SecureRandom.hex(16)}"
}
end
account
after(:create) do |channel_voice|
create(:inbox, channel: channel_voice, account: channel_voice.account)
end
end
end

View File

@@ -0,0 +1,116 @@
FactoryBot.define do
factory :channel_whatsapp, class: 'Channel::Whatsapp' do
sequence(:phone_number) { |n| "+123456789#{n}1" }
account
provider_config { { 'api_key' => 'test_key', 'phone_number_id' => 'random_id' } }
message_templates do
[{ 'name' => 'sample_shipping_confirmation',
'status' => 'approved',
'category' => 'SHIPPING_UPDATE',
'language' => 'id',
'namespace' => '2342384942_32423423_23423fdsdaf',
'components' =>
[{ 'text' => 'Paket Anda sudah dikirim. Paket akan sampai dalam {{1}} hari kerja.', 'type' => 'BODY' },
{ 'text' => 'Pesan ini berasal dari bisnis yang tidak terverifikasi.', 'type' => 'FOOTER' }],
'rejected_reason' => 'NONE' },
{ 'name' => 'customer_yes_no',
'status' => 'approved',
'category' => 'SHIPPING_UPDATE',
'language' => 'ar',
'namespace' => '2342384942_32423423_23423fdsdaf23',
'components' =>
[{ 'text' => 'عميلنا العزيز الرجاء الرد على هذه الرسالة بكلمة *نعم* للرد على إستفساركم من قبل خدمة العملاء.',
'type' => 'BODY' }],
'rejected_reason' => 'NONE' },
{ 'name' => 'sample_shipping_confirmation',
'status' => 'approved',
'category' => 'SHIPPING_UPDATE',
'language' => 'en_US',
'namespace' => '23423423_2342423_324234234_2343224',
'components' =>
[{ 'text' => 'Your package has been shipped. It will be delivered in {{1}} business days.', 'type' => 'BODY' },
{ 'text' => 'This message is from an unverified business.', 'type' => 'FOOTER' }],
'rejected_reason' => 'NONE' },
{
'name' => 'ticket_status_updated',
'status' => 'APPROVED',
'category' => 'UTILITY',
'language' => 'en',
'namespace' => '23423423_2342423_324234234_2343224',
'components' => [
{ 'text' => "Hello {{name}}, Your support ticket with ID: \#{{ticket_id}} has been updated by the support agent.",
'type' => 'BODY',
'example' => { 'body_text_named_params' => [
{ 'example' => 'John', 'param_name' => 'name' },
{ 'example' => '2332', 'param_name' => 'ticket_id' }
] } }
],
'sub_category' => 'CUSTOM',
'parameter_format' => 'NAMED'
},
{
'name' => 'ticket_status_updated',
'status' => 'APPROVED',
'category' => 'UTILITY',
'language' => 'en_US',
'components' => [
{ 'text' => "Hello {{last_name}}, Your support ticket with ID: \#{{ticket_id}} has been updated by the support agent.",
'type' => 'BODY',
'example' => { 'body_text_named_params' => [
{ 'example' => 'Dale', 'param_name' => 'last_name' },
{ 'example' => '2332', 'param_name' => 'ticket_id' }
] } }
],
'sub_category' => 'CUSTOM',
'parameter_format' => 'NAMED'
},
{
'name' => 'test_no_params_template',
'status' => 'APPROVED',
'category' => 'UTILITY',
'language' => 'en',
'namespace' => 'ed41a221_133a_4558_a1d6_192960e3aee9',
'id' => '9876543210987654',
'length' => 1,
'parameter_format' => 'POSITIONAL',
'previous_category' => 'MARKETING',
'sub_category' => 'CUSTOM',
'components' => [
{
'text' => 'Thank you for contacting us! Your request has been processed successfully. Have a great day! 🙂',
'type' => 'BODY'
}
],
'rejected_reason' => 'NONE'
}]
end
message_templates_last_updated { Time.now.utc }
transient do
sync_templates { true }
validate_provider_config { true }
end
before(:create) do |channel_whatsapp, options|
# since factory already has the required message templates, we just need to bypass it getting updated
channel_whatsapp.define_singleton_method(:sync_templates) { nil } unless options.sync_templates
channel_whatsapp.define_singleton_method(:validate_provider_config) { nil } unless options.validate_provider_config
if channel_whatsapp.provider == 'whatsapp_cloud'
# Add 'source' => 'embedded_signup' to skip after_commit :setup_webhooks callback in tests
# The callback is for manual setup flow; embedded signup handles webhook setup explicitly
# Only set source if not already provided (allows tests to override)
default_config = {
'api_key' => 'test_key',
'phone_number_id' => '123456789',
'business_account_id' => '123456789'
}
default_config['source'] = 'embedded_signup' unless channel_whatsapp.provider_config.key?('source')
channel_whatsapp.provider_config = channel_whatsapp.provider_config.merge(default_config)
end
end
after(:create) do |channel_whatsapp|
create(:inbox, channel: channel_whatsapp, account: channel_whatsapp.account)
end
end
end

View File

@@ -0,0 +1,12 @@
# frozen_string_literal: true
FactoryBot.define do
factory :channel_widget, class: 'Channel::WebWidget' do
sequence(:website_url) { |n| "https://example-#{n}.com" }
sequence(:widget_color, &:to_s)
account
after(:create) do |channel_widget|
create(:inbox, channel: channel_widget, account: channel_widget.account)
end
end
end

View File

@@ -0,0 +1,11 @@
# frozen_string_literal: true
FactoryBot.define do
factory :channel_facebook_page, class: 'Channel::FacebookPage' do
page_access_token { SecureRandom.uuid }
user_access_token { SecureRandom.uuid }
page_id { SecureRandom.uuid }
inbox
account
end
end

View File

@@ -0,0 +1,14 @@
# frozen_string_literal: true
FactoryBot.define do
factory :channel_instagram_fb_page, class: 'Channel::FacebookPage' do
page_access_token { SecureRandom.uuid }
user_access_token { SecureRandom.uuid }
page_id { SecureRandom.uuid }
account
before :create do |_channel|
WebMock::API.stub_request(:post, 'https://graph.facebook.com/v3.2/me/subscribed_apps')
end
end
end

View File

@@ -0,0 +1,21 @@
FactoryBot.define do
factory :channel_twilio_sms, class: 'Channel::TwilioSms' do
auth_token { SecureRandom.uuid }
account_sid { SecureRandom.uuid }
messaging_service_sid { "MG#{Faker::Number.hexadecimal(digits: 32)}" }
medium { :sms }
account
after(:build) do |channel|
channel.inbox ||= create(:inbox, account: channel.account)
end
trait :with_phone_number do
sequence(:phone_number) { |n| "+123456789#{n}1" }
messaging_service_sid { nil }
end
trait :whatsapp do
medium { :whatsapp }
end
end
end

View File

@@ -0,0 +1,10 @@
# frozen_string_literal: true
FactoryBot.define do
factory :channel_twitter_profile, class: 'Channel::TwitterProfile' do
twitter_access_token { SecureRandom.uuid }
twitter_access_token_secret { SecureRandom.uuid }
profile_id { SecureRandom.uuid }
account
end
end

View File

@@ -0,0 +1,29 @@
# spec/factories/response_bodies.rb
FactoryBot.define do
factory :clearbit_combined_response, class: Hash do
skip_create
initialize_with do
{
'person' => {
'name' => {
'fullName' => 'John Doe'
},
'avatar' => 'https://example.com/avatar.png'
},
'company' => {
'name' => 'Doe Inc.',
'timeZone' => 'Asia/Kolkata',
'category' => {
'sector' => 'Technology',
'industryGroup' => 'Software',
'industry' => 'Software'
},
'metrics' => {
'employees' => '1-10'
}
}
}.to_json
end
end
end

View File

@@ -0,0 +1,20 @@
FactoryBot.define do
factory :company do
sequence(:name) { |n| "Company #{n}" }
sequence(:domain) { |n| "company#{n}.com" }
description { 'A sample company description' }
account
trait :without_domain do
domain { nil }
end
trait :with_avatar do
avatar { fixture_file_upload(Rails.root.join('spec/assets/avatar.png'), 'image/png') }
end
trait :with_long_description do
description { 'A' * 500 }
end
end
end

View File

@@ -0,0 +1,23 @@
# frozen_string_literal: true
FactoryBot.define do
factory :contact_inbox do
contact
inbox
after(:build) { |contact_inbox| contact_inbox.source_id ||= generate_source_id(contact_inbox) }
end
end
def generate_source_id(contact_inbox)
case contact_inbox.inbox.channel_type
when 'Channel::TwilioSms'
contact_inbox.inbox.channel.medium == 'sms' ? Faker::PhoneNumber.cell_phone_in_e164 : "whatsapp:#{Faker::PhoneNumber.cell_phone_in_e164}"
when 'Channel::Email'
"#{SecureRandom.uuid}@acme.inc"
when 'Channel::Whatsapp'
Faker::PhoneNumber.cell_phone_in_e164.delete('+')
else
SecureRandom.uuid
end
end

View File

@@ -0,0 +1,20 @@
# frozen_string_literal: true
FactoryBot.define do
factory :contact do
sequence(:name) { |n| "Contact #{n}" }
account
trait :with_avatar do
avatar { fixture_file_upload(Rails.root.join('spec/assets/avatar.png'), 'image/png') }
end
trait :with_email do
sequence(:email) { |n| "contact-#{n}@example.com" }
end
trait :with_phone_number do
phone_number { Faker::PhoneNumber.cell_phone_in_e164 }
end
end
end

View File

@@ -0,0 +1,13 @@
FactoryBot.define do
factory :conversation_participant do
conversation
account
before(:build) do |conversation|
if conversation.user.blank?
conversation.user = create(:user, account: conversation.account)
create(:inbox_member, user: conversation.user, inbox: conversation.conversation.inbox)
end
end
end
end

View File

@@ -0,0 +1,32 @@
# frozen_string_literal: true
FactoryBot.define do
factory :conversation do
status { 'open' }
agent_last_seen_at { Time.current }
identifier { SecureRandom.hex }
after(:build) do |conversation|
conversation.account ||= create(:account)
conversation.inbox ||= create(
:inbox,
account: conversation.account,
channel: create(:channel_widget, account: conversation.account)
)
conversation.contact ||= create(:contact, :with_email, account: conversation.account)
conversation.contact_inbox ||= create(:contact_inbox, contact: conversation.contact, inbox: conversation.inbox)
end
trait :with_team do
after(:build) do |conversation|
conversation.team ||= create(:team, account: conversation.account)
end
end
trait :with_assignee do
after(:build) do |conversation|
conversation.assignee ||= create(:user, account: conversation.account, role: :agent)
end
end
end
end

View File

@@ -0,0 +1,12 @@
# frozen_string_literal: true
FactoryBot.define do
factory :csat_survey_response do
rating { 1 }
feedback_message { Faker::Movie.quote }
account
conversation
message
contact
end
end

View File

@@ -0,0 +1,12 @@
# frozen_string_literal: true
FactoryBot.define do
factory :custom_attribute_definition do
sequence(:attribute_display_name) { |n| "Custom Attribute Definition #{n}" }
sequence(:attribute_key) { |n| "custom_attribute_#{n}" }
attribute_display_type { 1 }
attribute_model { 0 }
default_value { nil }
account
end
end

View File

@@ -0,0 +1,11 @@
# frozen_string_literal: true
FactoryBot.define do
factory :custom_filter do
sequence(:name) { |n| "Custom Filter #{n}" }
filter_type { 0 }
query { { labels: ['customer-support'], status: 'open' } }
user
account
end
end

View File

@@ -0,0 +1,8 @@
FactoryBot.define do
factory :custom_role do
account
name { Faker::Name.name }
description { Faker::Lorem.sentence }
permissions { CustomRole::PERMISSIONS.sample(SecureRandom.random_number(1..4)) }
end
end

View File

@@ -0,0 +1,10 @@
# frozen_string_literal: true
FactoryBot.define do
factory :dashboard_app do
sequence(:title) { |n| "Dashboard App #{n}" }
content { [{ type: 'frame', url: 'https://chatwoot.com' }] }
user
account
end
end

View File

@@ -0,0 +1,7 @@
FactoryBot.define do
factory :data_import do
data_type { 'contacts' }
import_file { Rack::Test::UploadedFile.new(Rails.root.join('spec/assets/contacts.csv'), 'text/csv') }
account
end
end

View File

@@ -0,0 +1,5 @@
FactoryBot.define do
factory :email_template do
name { 'MyString' }
end
end

View File

@@ -0,0 +1,48 @@
# frozen_string_literal: true
FactoryBot.define do
factory :incoming_fb_text_message, class: Hash do
messaging do
{ sender: { id: '3383290475046708' },
recipient: { id: '117172741761305' },
message: { mid: 'm_KXGKDUpO6xbVdAmZFBVpzU1AhKVJdAIUnUH4cwkvb_K3iZsWhowDRyJ_DcowEpJjncaBwdCIoRrixvCbbO1PcA', text: 'facebook message' } }
end
initialize_with { attributes }
end
factory :mocked_message_text, class: Hash do
transient do
sender_id { '3383290475046708' }
end
initialize_with do
{ messaging: { sender: { id: sender_id },
recipient: { id: '117172741761305' },
message: { mid: 'm_KXGKDUpO6xbVdAmZFBVpzU1AhKVJdAIUnUH4cwkvb_K3iZsWhowDRyJ_DcowEpJjncaBwdCIoRrixvCbbO1PcA',
text: 'facebook message' } } }
end
# initialize_with { attributes }
end
factory :message_deliveries, class: Hash do
messaging do
{ sender: { id: '3383290475046708' },
recipient: { id: '117172741761305' },
delivery: { watermark: '1648581633369' } }
end
initialize_with { attributes }
end
factory :message_reads, class: Hash do
messaging do
{ sender: { id: '3383290475046708' },
recipient: { id: '117172741761305' },
read: { watermark: '1648581633369' } }
end
initialize_with { attributes }
end
end

View File

@@ -0,0 +1,8 @@
FactoryBot.define do
factory :folder, class: 'Folder' do
account_id { 1 }
name { 'MyString' }
description { 'MyText' }
category_id { 1 }
end
end

View File

@@ -0,0 +1,6 @@
FactoryBot.define do
factory :inbox_assignment_policy do
inbox
assignment_policy
end
end

View File

@@ -0,0 +1,7 @@
FactoryBot.define do
factory :inbox_capacity_limit do
association :agent_capacity_policy, factory: :agent_capacity_policy
inbox
conversation_limit { 5 }
end
end

View File

@@ -0,0 +1,8 @@
# frozen_string_literal: true
FactoryBot.define do
factory :inbox_member do
user { create(:user, :with_avatar) }
inbox
end
end

View File

@@ -0,0 +1,18 @@
# frozen_string_literal: true
FactoryBot.define do
factory :inbox do
account
channel { FactoryBot.build(:channel_widget, account: account) }
name { 'Inbox' }
after(:create) do |inbox|
inbox.channel.save!
end
trait :with_email do
channel { FactoryBot.build(:channel_email, account: account) }
name { 'Email Inbox' }
end
end
end

View File

@@ -0,0 +1,520 @@
FactoryBot.define do
factory :instagram_message_create_event, class: Hash do
transient do
ig_entry_id { SecureRandom.uuid }
sender_id { "Sender-id-#{SecureRandom.hex(4)}" }
end
entry do
[
{
'id': ig_entry_id,
'time': '2021-09-08T06:34:04+0000',
'messaging': [
{
'sender': {
'id': sender_id
},
'recipient': {
'id': 'chatwoot-app-user-id-1'
},
'timestamp': '2021-09-08T06:34:04+0000',
'message': {
'mid': 'message-id-1',
'text': 'This is the first message from the customer'
}
}
]
}
]
end
initialize_with { attributes }
end
factory :instagram_message_standby_event, class: Hash do
transient do
ig_entry_id { SecureRandom.uuid }
sender_id { "Sender-id-#{SecureRandom.hex(4)}" }
end
entry do
[
{
'time': '2021-09-08T06:34:04+0000',
'id': ig_entry_id,
'standby': [
{
'sender': {
'id': sender_id
},
'recipient': {
'id': 'chatwoot-app-user-id-1'
},
'timestamp': '2021-09-08T06:34:04+0000',
'message': {
'mid': 'message-id-1',
'text': 'This is the first standby message from the customer, after 24 hours.'
}
}
]
}
]
end
initialize_with { attributes }
end
factory :instagram_story_reply_event, class: Hash do
transient do
ig_entry_id { SecureRandom.uuid }
sender_id { "Sender-id-#{SecureRandom.hex(4)}" }
end
entry do
[
{
'id': ig_entry_id,
'time': '2021-09-08T06:34:04+0000',
'messaging': [
{
'sender': {
'id': sender_id
},
'recipient': {
'id': 'chatwoot-app-user-id-1'
},
'timestamp': '2021-09-08T06:34:04+0000',
'message': {
'mid': 'message-id-1',
'text': 'This is the story reply',
'reply_to': {
'story': {
'id': 'chatwoot-app-user-id-1',
'url': 'https://chatwoot-assets.local/sample.png'
}
}
}
}
]
}
]
end
initialize_with { attributes }
end
factory :instagram_message_reply_event, class: Hash do
transient do
ig_entry_id { SecureRandom.uuid }
sender_id { "Sender-id-#{SecureRandom.hex(4)}" }
end
entry do
[
{
'id': ig_entry_id,
'time': '2021-09-08T06:35:04+0000',
'messaging': [
{
'sender': {
'id': sender_id
},
'recipient': {
'id': 'chatwoot-app-user-id-1'
},
'timestamp': '2021-09-08T06:34:04+0000',
'message': {
'mid': 'message-id-2',
'text': 'This is message with replyto mid',
'reply_to': {
'mid': 'message-id-1'
}
}
}
]
}
]
end
initialize_with { attributes }
end
factory :instagram_test_text_event, class: Hash do
transient do
ig_entry_id { SecureRandom.uuid }
end
entry do
[
{
'time' => 1_661_141_837_537,
'id' => ig_entry_id,
'messaging' => [
{
'sender' => {
'id' => '12334'
},
'recipient' => {
'id' => '23245'
},
'timestamp' => '1527459824',
'message' => {
'mid' => 'random_mid',
'text' => 'random_text'
}
}
]
}
]
end
initialize_with { attributes }
end
factory :instagram_message_unsend_event, class: Hash do
transient do
ig_entry_id { SecureRandom.uuid }
sender_id { "Sender-id-#{SecureRandom.hex(4)}" }
end
entry do
[
{
'id': ig_entry_id,
'time': '2021-09-08T06:34:04+0000',
'messaging': [
{
'sender': {
'id': sender_id
},
'recipient': {
'id': 'chatwoot-app-user-id-1'
},
'timestamp': '2021-09-08T06:34:04+0000',
'message': {
'mid': 'message-id-to-delete',
'is_deleted': true
}
}
]
}
]
end
initialize_with { attributes }
end
factory :instagram_message_attachment_event, class: Hash do
transient do
ig_entry_id { SecureRandom.uuid }
sender_id { "Sender-id-#{SecureRandom.hex(4)}" }
end
entry do
[
{
'id': ig_entry_id,
'time': '2021-09-08T06:34:04+0000',
'messaging': [
{
'sender': {
'id': sender_id
},
'recipient': {
'id': 'chatwoot-app-user-id-1'
},
'timestamp': '2021-09-08T06:34:04+0000',
'message': {
'mid': 'message-id-1',
'attachments': [
{
'type': 'share',
'payload': {
'url': 'https://www.example.com/test.jpeg'
}
}
]
}
}
]
}
]
end
initialize_with { attributes }
end
factory :instagram_shared_reel_event, class: Hash do
transient do
ig_entry_id { SecureRandom.uuid }
sender_id { "Sender-id-#{SecureRandom.hex(4)}" }
end
entry do
[
{
'id': ig_entry_id,
'time': '2021-09-08T06:34:04+0000',
'messaging': [
{
'sender': {
'id': sender_id
},
'recipient': {
'id': 'chatwoot-app-user-id-1'
},
'timestamp': '2021-09-08T06:34:04+0000',
'message': {
'mid': 'message-id-1',
'attachments': [
{
'type': 'ig_reel',
'payload': {
'reel_video_id': '1234',
'title': 'Reel title',
'url': 'https://www.example.com/test.jpeg'
}
}
]
}
}
]
}
]
end
initialize_with { attributes }
end
factory :instagram_story_mention_event, class: Hash do
transient do
ig_entry_id { SecureRandom.uuid }
sender_id { "Sender-id-#{SecureRandom.hex(4)}" }
end
entry do
[
{
'id': ig_entry_id,
'time': '2021-09-08T06:34:04+0000',
'messaging': [
{
'sender': {
'id': sender_id
},
'recipient': {
'id': 'chatwoot-app-user-id-1'
},
'timestamp': '2021-09-08T06:34:04+0000',
'message': {
'mid': 'mention-message-id-1',
'attachments': [
{
'type': 'story_mention',
'payload': {
'url': 'https://www.example.com/test.jpeg'
}
}
]
}
}
]
}
]
end
initialize_with { attributes }
end
factory :instagram_story_mention_event_with_echo, class: Hash do
transient do
ig_entry_id { SecureRandom.uuid }
sender_id { "Sender-id-#{SecureRandom.hex(4)}" }
end
entry do
[
{
'id': ig_entry_id,
'time': '2021-09-08T06:34:04+0000',
'messaging': [
{
'sender': {
'id': sender_id
},
'recipient': {
'id': 'chatwoot-app-user-id-1'
},
'timestamp': '2021-09-08T06:34:04+0000',
'message': {
'mid': 'message-id-1',
'attachments': [
{
'type': 'story_mention',
'payload': {
'url': 'https://www.example.com/test.jpeg'
}
}
],
'is_echo': true
}
}
]
}
]
end
initialize_with { attributes }
end
factory :instagram_ig_story_event, class: Hash do
transient do
ig_entry_id { SecureRandom.uuid }
sender_id { "Sender-id-#{SecureRandom.hex(4)}" }
end
entry do
[
{
'id': ig_entry_id,
'time': '2021-09-08T06:34:04+0000',
'messaging': [
{
'sender': {
'id': sender_id
},
'recipient': {
'id': 'chatwoot-app-user-id-1'
},
'timestamp': '2021-09-08T06:34:04+0000',
'message': {
'mid': 'ig-story-message-id-1',
'attachments': [
{
'type': 'ig_story',
'payload': {
'story_media_id': '17949487764033669',
'story_media_url': 'https://lookaside.fbsbx.com/ig_messaging_cdn/?asset_id=17949487764033669&signature=test'
}
}
]
}
}
]
}
]
end
initialize_with { attributes }
end
factory :instagram_ig_post_event, class: Hash do
transient do
ig_entry_id { SecureRandom.uuid }
sender_id { "Sender-id-#{SecureRandom.hex(4)}" }
end
entry do
[
{
'id': ig_entry_id,
'time': '2021-09-08T06:34:04+0000',
'messaging': [
{
'sender': {
'id': sender_id
},
'recipient': {
'id': 'chatwoot-app-user-id-1'
},
'timestamp': '2021-09-08T06:34:04+0000',
'message': {
'mid': 'ig-post-message-id-1',
'attachments': [
{
'type': 'ig_post',
'payload': {
'ig_post_media_id': '18091626484740369',
'title': 'Shared Instagram post',
'url': 'https://lookaside.fbsbx.com/ig_messaging_cdn/?asset_id=18091626484740369&signature=test'
}
}
]
}
}
]
}
]
end
initialize_with { attributes }
end
factory :instagram_message_unsupported_event, class: Hash do
transient do
ig_entry_id { SecureRandom.uuid }
sender_id { "Sender-id-#{SecureRandom.hex(4)}" }
end
entry do
[
{
'id': ig_entry_id,
'time': '2021-09-08T06:34:04+0000',
'messaging': [
{
'sender': {
'id': sender_id
},
'recipient': {
'id': 'chatwoot-app-user-id-1'
},
'timestamp': '2021-09-08T06:34:04+0000',
'message': {
'mid': 'unsupported-message-id-1',
'is_unsupported': true
}
}
]
}
]
end
initialize_with { attributes }
end
factory :messaging_seen_event, class: Hash do
transient do
ig_entry_id { SecureRandom.uuid }
sender_id { "Sender-id-#{SecureRandom.hex(4)}" }
end
entry do
[
{
'id': ig_entry_id,
'time': '2021-09-08T06:34:04+0000',
'messaging': [
{
'sender': {
'id': sender_id
},
'recipient': {
'id': 'chatwoot-app-user-id-1'
},
'timestamp': '2021-09-08T06:34:04+0000',
'read': {
'mid': 'message-id-1'
}
}
]
}
]
end
initialize_with { attributes }
end
factory :instagram_test_event, class: Hash do
entry do
[
{
'id': '0',
'time': '2021-09-08T06:34:04+0000',
'changes': [
{
'field': 'messages',
'value': {
'sender': {
'id': '12334'
},
'recipient': {
'id': '23245'
},
'timestamp': '1527459824',
'message': {
'mid': 'random_mid',
'text': 'random_text'
}
}
}
]
}
]
end
initialize_with { attributes }
end
end

View File

@@ -0,0 +1,31 @@
# frozen_string_literal: true
FactoryBot.define do
factory :incoming_ig_text_message, class: Hash do
messaging do
[
{
'id': 'instagram-message-id-123',
'time': '2021-09-08T06:34:04+0000',
'messaging': [
{
'sender': {
'id': 'Sender-id-1'
},
'recipient': {
'id': 'chatwoot-app-user-id-1'
},
'timestamp': '2021-09-08T06:34:04+0000',
'message': {
'mid': 'message-id-1',
'text': 'This is the first message from the customer'
}
}
]
}
]
end
initialize_with { attributes }
end
end

View File

@@ -0,0 +1,7 @@
FactoryBot.define do
factory :installation_config do
name { 'xyc' }
value { 1.5 }
locked { true }
end
end

View File

@@ -0,0 +1,52 @@
FactoryBot.define do
factory :integrations_hook, class: 'Integrations::Hook' do
app_id { 'slack' }
account
settings { { test: 'test' } }
status { Integrations::Hook.statuses['enabled'] }
access_token { SecureRandom.hex }
reference_id { SecureRandom.hex }
trait :dialogflow do
app_id { 'dialogflow' }
settings { { project_id: 'test', credentials: {}, region: 'global' } }
end
trait :dyte do
app_id { 'dyte' }
settings { { api_key: 'api_key', organization_id: 'org_id' } }
end
trait :google_translate do
app_id { 'google_translate' }
settings { { project_id: 'test', credentials: {} } }
end
trait :openai do
app_id { 'openai' }
settings { { api_key: 'api_key' } }
end
trait :linear do
app_id { 'linear' }
access_token { SecureRandom.hex }
end
trait :shopify do
app_id { 'shopify' }
access_token { SecureRandom.hex }
reference_id { 'test-store.myshopify.com' }
end
trait :leadsquared do
app_id { 'leadsquared' }
settings do
{
'access_key' => SecureRandom.hex,
'secret_key' => SecureRandom.hex,
'endpoint_url' => 'https://api.leadsquared.com/'
}
end
end
end
end

View File

@@ -0,0 +1,8 @@
# frozen_string_literal: true
FactoryBot.define do
factory :label do
account
sequence(:title) { |n| "Label_#{n}" }
end
end

View File

@@ -0,0 +1,11 @@
FactoryBot.define do
factory :macro do
account
name { 'wrong_message_actions' }
actions do
[
{ 'action_name' => 'add_label', 'action_params' => %w[wrong_chat] }
]
end
end
end

View File

@@ -0,0 +1,10 @@
# frozen_string_literal: true
FactoryBot.define do
factory :mention do
mentioned_at { Time.current }
account
conversation
user
end
end

View File

@@ -0,0 +1,43 @@
# frozen_string_literal: true
FactoryBot.define do
factory :message do
content { 'Incoming Message' }
status { 'sent' }
message_type { 'incoming' }
content_type { 'text' }
account { create(:account) }
trait :instagram_story_mention do
content_attributes { { image_type: 'story_mention' } }
after(:build) do |message|
unless message.inbox.instagram?
message.inbox = create(:inbox, account: message.account,
channel: create(:channel_instagram_fb_page, account: message.account, instagram_id: 'instagram-123'))
end
attachment = message.attachments.new(account_id: message.account_id, file_type: :image, external_url: 'https://www.example.com/test.jpeg')
attachment.file.attach(io: Rails.root.join('spec/assets/avatar.png').open, filename: 'avatar.png', content_type: 'image/png')
end
end
trait :with_attachment do
after(:build) do |message|
attachment = message.attachments.new(account_id: message.account_id, file_type: :image)
attachment.file.attach(io: Rails.root.join('spec/assets/avatar.png').open, filename: 'avatar.png', content_type: 'image/png')
end
end
trait :bot_message do
message_type { 'outgoing' }
after(:build) do |message|
message.sender = nil
end
end
after(:build) do |message|
message.sender ||= message.outgoing? ? create(:user, account: message.account) : create(:contact, account: message.account)
message.inbox ||= message.conversation&.inbox || create(:inbox, account: message.account)
message.conversation ||= create(:conversation, account: message.account, inbox: message.inbox)
end
end
end

View File

@@ -0,0 +1,10 @@
# frozen_string_literal: true
FactoryBot.define do
factory :note do
content { 'Hey welcome to chatwoot' }
account
user
contact
end
end

View File

@@ -0,0 +1,18 @@
# frozen_string_literal: true
FactoryBot.define do
factory :notification_subscription do
user
identifier { 'test' }
subscription_type { 'browser_push' }
subscription_attributes { { endpoint: 'test', auth: 'test' } }
trait :browser_push do
subscription_type { 'browser_push' }
end
trait :fcm do
subscription_type { 'fcm' }
end
end
end

View File

@@ -0,0 +1,20 @@
# frozen_string_literal: true
FactoryBot.define do
factory :notification do
primary_actor { create(:conversation, account: account) }
notification_type { 'conversation_assignment' }
user
account
read_at { nil }
snoozed_until { nil }
end
trait :read do
read_at { DateTime.now.utc - 3.days }
end
trait :snoozed do
snoozed_until { DateTime.now.utc + 3.days }
end
end

View File

@@ -0,0 +1,5 @@
FactoryBot.define do
factory :platform_app do
name { Faker::Book.name }
end
end

View File

@@ -0,0 +1,6 @@
FactoryBot.define do
factory :platform_app_permissible do
platform_app
permissible { create(:user) }
end
end

View File

@@ -0,0 +1,7 @@
FactoryBot.define do
factory :portal, class: 'Portal' do
account
name { Faker::Book.name }
slug { SecureRandom.hex }
end
end

View File

@@ -0,0 +1,6 @@
FactoryBot.define do
factory :related_category do
category
related_category
end
end

View File

@@ -0,0 +1,13 @@
FactoryBot.define do
factory :reporting_event do
name { 'first_response' }
value { 1.5 }
value_in_business_hours { 1 }
account
inbox { association :inbox, account: account }
user { association :user, account: account }
conversation { association :conversation, account: account, inbox: inbox }
event_start_time { 2.hours.ago }
event_end_time { 1.hour.ago }
end
end

View File

@@ -0,0 +1,10 @@
FactoryBot.define do
factory :sla_event do
applied_sla
conversation
event_type { 'frt' }
account { conversation.account }
inbox { conversation.inbox }
sla_policy { applied_sla.sla_policy }
end
end

View File

@@ -0,0 +1,11 @@
FactoryBot.define do
factory :sla_policy do
account
name { 'sla_1' }
first_response_time_threshold { 2000 }
description { 'SLA policy for enterprise customers' }
next_response_time_threshold { 1000 }
resolution_time_threshold { 3000 }
only_during_business_hours { false }
end
end

View File

@@ -0,0 +1,9 @@
FactoryBot.define do
factory :super_admin do
name { Faker::Name.name }
email { "admin@#{SecureRandom.uuid}.com" }
password { 'Password1!' }
type { 'SuperAdmin' }
confirmed_at { Time.zone.now }
end
end

View File

@@ -0,0 +1,8 @@
# frozen_string_literal: true
FactoryBot.define do
factory :team_member do
user
team
end
end

View File

@@ -0,0 +1,8 @@
FactoryBot.define do
factory :team do
sequence(:name) { |n| "Team #{n}" }
description { 'MyText' }
allow_auto_assign { true }
account
end
end

View File

@@ -0,0 +1,48 @@
# frozen_string_literal: true
FactoryBot.define do
factory :tweet_create_event, class: Hash do
for_user_id { '1' }
user_has_blocked { false }
tweet_create_events do
[
{
'created_at' => 'Wed Feb 05 08:39:31 +0000 2020',
'id' => 1,
'id_str' => '1',
'text' => '@chatwootapp Testing a sample tweet with Twitter client',
'source' => '<a href="http://twitter.com/download/iphone" rel="nofollow">Twitter for iPhone</a>',
'truncated' => false,
'in_reply_to_status_id' => nil,
'in_reply_to_status_id_str' => nil,
'in_reply_to_user_id' => 1,
'in_reply_to_user_id_str' => '1',
'in_reply_to_screen_name' => 'chatwootapp',
'user' => {
'id' => 2,
'name' => 'SurveyJoy',
'screen_name' => 'surveyjoyHQ',
'location' => 'Bangalore',
'url' => 'https://surveyjoy.co?utm_source=twitter',
'description' => 'Delightful in-product customer satisfaction surveys',
'followers_count' => 21,
'friends_count' => 13,
'profile_image_url' => 'http://pbs.twimg.com/profile_images/1114792399597985792/iHc5Gmez_normal.png',
'profile_image_url_https' => 'https://pbs.twimg.com/profile_images/1114792399597985792/iHc5Gmez_normal.png',
'profile_banner_url' => 'https://pbs.twimg.com/profile_banners/1109349707783041024/1554622013'
},
'geo' => nil,
'coordinates' => nil,
'place' => nil,
'contributors' => nil,
'is_quote_status' => false,
'quote_count' => 0,
'reply_count' => 0,
'retweet_count' => 0,
'favorite_count' => 0
}
]
end
initialize_with { attributes }
end
end

View File

@@ -0,0 +1,75 @@
# frozen_string_literal: true
FactoryBot.define do
factory :twitter_dm_image_event, class: Hash do
for_user_id { '1' }
direct_message_events do
[{
'type' => 'message_create',
'id' => '123',
'message_create' => {
'target' => { 'recipient_id' => '1' },
'sender_id' => '2',
'source_app_id' => '268278',
'message_data' => {
'text' => 'Blue Bird',
'attachment' => {
'media' => {
'display_url' => 'pic.twitter.com/5J1WJSRCy9',
'expanded_url' => 'https://twitter.com/nolan_test/status/930077847535812610/photo/1',
'id' => 9.300778475358126e17,
'id_str' => '930077847535812610',
'indices' => [
13,
36
],
'media_url' => 'http://pbs.twimg.com/media/DOhM30VVwAEpIHq.jpg',
'media_url_https' => 'https://pbs.twimg.com/media/DOhM30VVwAEpIHq.jpg',
'sizes' => {
'thumb' => {
'h' => 150,
'resize' => 'crop',
'w' => 150
},
'large' => {
'h' => 1366,
'resize' => 'fit',
'w' => 2048
},
'medium' => {
'h' => 800,
'resize' => 'fit',
'w' => 1200
},
'small' => {
'h' => 454,
'resize' => 'fit',
'w' => 680
}
},
'type' => 'photo',
'url' => 'https://t.co/5J1WJSRCy9'
}
}.with_indifferent_access
}
}
}.with_indifferent_access]
end
users do
{
'1' => {
id: '1',
name: 'person 1',
profile_image_url: 'https://chatwoot-assets.local/sample.png'
},
'2' => {
id: '1',
name: 'person 1',
profile_image_url: 'https://chatwoot-assets.local/sample.png'
}
}
end
initialize_with { attributes }
end
end

View File

@@ -0,0 +1,37 @@
# frozen_string_literal: true
FactoryBot.define do
factory :twitter_message_create_event, class: Hash do
for_user_id { '1' }
direct_message_events do
[{
'type' => 'message_create',
'id' => '123',
'message_create' => {
:target => { 'recipient_id' => '1' },
'sender_id' => '2',
'source_app_id' => '268278',
'message_data' => {
'text' => 'Blue Bird'
}
}
}]
end
users do
{
'1' => {
id: '1',
name: 'person 1',
profile_image_url: 'https://chatwoot-assets.local/sample.png'
},
'2' => {
id: '1',
name: 'person 1',
profile_image_url: 'https://chatwoot-assets.local/sample.png'
}
}
end
initialize_with { attributes }
end
end

View File

@@ -0,0 +1,36 @@
# frozen_string_literal: true
FactoryBot.define do
factory :user do
transient do
skip_confirmation { true }
role { 'agent' }
auto_offline { true }
account { nil }
inviter { nil }
end
provider { 'email' }
uid { SecureRandom.uuid }
name { Faker::Name.name }
display_name { Faker::Name.first_name }
email { display_name + "@#{SecureRandom.uuid}.com" }
password { 'Password1!' }
after(:build) do |user, evaluator|
user.skip_confirmation! if evaluator.skip_confirmation
if evaluator.account
create(:account_user, user: user, account: evaluator.account, role: evaluator.role, inviter: evaluator.inviter,
auto_offline: evaluator.auto_offline)
end
end
trait :with_avatar do
avatar { fixture_file_upload(Rails.root.join('spec/assets/avatar.png'), 'image/png') }
end
trait :administrator do
role { 'administrator' }
end
end
end

View File

@@ -0,0 +1,20 @@
FactoryBot.define do
factory :webhook do
account_id { 1 }
inbox_id { 1 }
url { 'https://api.chatwoot.com' }
name { 'My Webhook' }
subscriptions do
%w[
conversation_status_changed
conversation_updated
conversation_created
contact_created
contact_updated
message_created
message_updated
webwidget_triggered
]
end
end
end

View File

@@ -0,0 +1,12 @@
# frozen_string_literal: true
FactoryBot.define do
factory :working_hour do
inbox
day_of_week { 1 }
open_hour { 9 }
open_minutes { 0 }
close_hour { 17 }
close_minutes { 0 }
end
end