first commit
This commit is contained in:
2
modules/marketing_automation/tests/__init__.py
Normal file
2
modules/marketing_automation/tests/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
20
modules/marketing_automation/tests/email.html
Normal file
20
modules/marketing_automation/tests/email.html
Normal file
@@ -0,0 +1,20 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns:py="http://genshi.edgewall.org/">
|
||||
<head>
|
||||
<title>Hello!</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>Hello, ${record.rec_name}!</p>
|
||||
<p>
|
||||
Here is a
|
||||
<a href="http://example.com/action">
|
||||
nice link
|
||||
</a>.
|
||||
</p>
|
||||
<p>
|
||||
<a href="unsubscribe|http://example.com/unsubscribe">
|
||||
Unsubscribe
|
||||
</a>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
10
modules/marketing_automation/tests/reminder.html
Normal file
10
modules/marketing_automation/tests/reminder.html
Normal file
@@ -0,0 +1,10 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns:py="http://genshi.edgewall.org/">
|
||||
<head>
|
||||
<title>Missing you!</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>Hello, ${record.name}!</p>
|
||||
<p>We miss you!</p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,289 @@
|
||||
Marketing Automation Scenario
|
||||
=============================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> import datetime
|
||||
>>> import re
|
||||
>>> from unittest.mock import patch
|
||||
|
||||
>>> from proteus import Model
|
||||
>>> from trytond.modules.marketing_automation import marketing_automation
|
||||
>>> from trytond.pyson import Eval, PYSONEncoder
|
||||
>>> from trytond.tests.tools import (
|
||||
... activate_modules, assertEqual, assertIn, assertTrue)
|
||||
>>> from trytond.tools import file_open
|
||||
|
||||
Patch send_message_transactional::
|
||||
|
||||
>>> smtp_calls = patch.object(
|
||||
... marketing_automation, 'send_message_transactional').start()
|
||||
>>> manager = patch.object(
|
||||
... marketing_automation, 'SMTPDataManager').start()
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules('marketing_automation')
|
||||
|
||||
>>> Email = Model.get('ir.email')
|
||||
>>> ReportingScenario = Model.get('marketing.automation.reporting.scenario')
|
||||
>>> ReportingActivity = Model.get('marketing.automation.reporting.activity')
|
||||
|
||||
Create a party::
|
||||
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> party = Party()
|
||||
>>> party.name = "Michael Scott"
|
||||
>>> contact = party.contact_mechanisms.new()
|
||||
>>> contact.type = 'email'
|
||||
>>> contact.value = 'michael@example.com'
|
||||
>>> party.save()
|
||||
|
||||
Create the running scenario::
|
||||
|
||||
>>> Scenario = Model.get('marketing.automation.scenario')
|
||||
>>> Activity = Model.get('marketing.automation.activity')
|
||||
>>> scenario = Scenario()
|
||||
>>> scenario.name = "Party Scenario"
|
||||
>>> scenario.model = 'party.party'
|
||||
>>> scenario.domain = '[["contact_mechanisms", "!=", null]]'
|
||||
>>> scenario.save()
|
||||
|
||||
>>> root_activity = Activity()
|
||||
>>> root_activity.name = "First email"
|
||||
>>> root_activity.parent = scenario
|
||||
>>> root_activity.action = 'send_email'
|
||||
>>> root_activity.email_title = "Hello"
|
||||
>>> root_activity.condition = PYSONEncoder().encode(
|
||||
... Eval('self', {}).get('active'))
|
||||
>>> with file_open('marketing_automation/tests/email.html', mode='r') as fp:
|
||||
... root_activity.email_template = fp.read()
|
||||
>>> root_activity.save()
|
||||
|
||||
>>> email_opened = Activity()
|
||||
>>> email_opened.name = "Email Opened"
|
||||
>>> email_opened.parent = root_activity
|
||||
>>> email_opened.on = 'email_opened'
|
||||
>>> email_opened.save()
|
||||
|
||||
>>> email_clicked = Activity()
|
||||
>>> email_clicked.name = "Email Clicked"
|
||||
>>> email_clicked.parent = root_activity
|
||||
>>> email_clicked.on = 'email_clicked'
|
||||
>>> email_clicked.save()
|
||||
|
||||
>>> email_not_clicked = Activity()
|
||||
>>> email_not_clicked.name = "Email no clicked"
|
||||
>>> email_not_clicked.parent = root_activity
|
||||
>>> email_not_clicked.on = 'email_clicked_not'
|
||||
>>> email_not_clicked.delay = datetime.timedelta(days=2)
|
||||
>>> email_not_clicked.save()
|
||||
|
||||
>>> email_reminder = Activity()
|
||||
>>> email_reminder.name = "Email Reminder"
|
||||
>>> email_reminder.parent = root_activity
|
||||
>>> email_reminder.action = 'send_email'
|
||||
>>> email_reminder.email_title = "Reminder"
|
||||
>>> email_reminder.delay = datetime.timedelta()
|
||||
>>> with file_open('marketing_automation/tests/reminder.html', mode='r') as fp:
|
||||
... email_reminder.email_template = fp.read()
|
||||
>>> email_reminder.save()
|
||||
|
||||
>>> scenario.record_count
|
||||
1
|
||||
|
||||
>>> scenario.click('run')
|
||||
|
||||
Trigger scenario::
|
||||
|
||||
>>> Cron = Model.get('ir.cron')
|
||||
>>> cron_trigger, = Cron.find([
|
||||
... ('method', '=', 'marketing.automation.scenario|trigger'),
|
||||
... ])
|
||||
>>> cron_process, = Cron.find([
|
||||
... ('method', '=', 'marketing.automation.record.activity|process'),
|
||||
... ])
|
||||
>>> cron_trigger.click('run_once')
|
||||
>>> cron_process.click('run_once')
|
||||
|
||||
>>> Record = Model.get('marketing.automation.record')
|
||||
>>> record, = Record.find([])
|
||||
>>> assertEqual(record.record, party)
|
||||
>>> scenario.record_count
|
||||
1
|
||||
>>> scenario.record_count_blocked
|
||||
0
|
||||
>>> scenario.block_rate
|
||||
0.0
|
||||
>>> bool(scenario.block_rate_trend)
|
||||
True
|
||||
|
||||
>>> reporting, = ReportingScenario.find([])
|
||||
>>> reporting.record_count
|
||||
1
|
||||
>>> reporting.record_count_blocked
|
||||
0
|
||||
>>> reporting.block_rate
|
||||
0.0
|
||||
|
||||
Check email sent::
|
||||
|
||||
>>> ShortenedURL = Model.get('web.shortened_url')
|
||||
>>> open_url, = ShortenedURL.find([
|
||||
... ('redirect_url', 'like', '%/m/empty.gif'),
|
||||
... ])
|
||||
>>> click_url, = ShortenedURL.find([
|
||||
... ('redirect_url', '=', 'http://example.com/action'),
|
||||
... ])
|
||||
|
||||
>>> RecordActivity = Model.get('marketing.automation.record.activity')
|
||||
>>> record_activity, = RecordActivity.find([
|
||||
... ('record', '=', record.id),
|
||||
... ('activity', '=', root_activity.id),
|
||||
... ])
|
||||
>>> record_activity.state
|
||||
'done'
|
||||
>>> root_activity.reload()
|
||||
>>> root_activity.record_count
|
||||
1
|
||||
|
||||
>>> smtp_calls.call_count
|
||||
1
|
||||
>>> msg, = smtp_calls.call_args[0]
|
||||
>>> smtp_calls.reset_mock()
|
||||
>>> msg = msg.get_body().get_content()
|
||||
>>> assertEqual(re.search(r'Hello, (.*)!', msg).group(1), party.name)
|
||||
>>> open_url.shortened_url in msg
|
||||
True
|
||||
>>> assertEqual(open_url.record, record_activity)
|
||||
>>> open_url.method
|
||||
'marketing.automation.record.activity|on_email_opened'
|
||||
>>> click_url.shortened_url in msg
|
||||
True
|
||||
>>> assertEqual(click_url.record, record_activity)
|
||||
>>> click_url.method
|
||||
'marketing.automation.record.activity|on_email_clicked'
|
||||
>>> assertIn(record.uuid, msg)
|
||||
|
||||
>>> email, = Email.find([])
|
||||
>>> email.recipients
|
||||
'Michael Scott <michael@example.com>'
|
||||
>>> email.subject
|
||||
'Hello'
|
||||
>>> email.resource == party
|
||||
True
|
||||
>>> assertEqual(email.marketing_automation_activity, root_activity)
|
||||
>>> assertTrue(email.marketing_automation_record)
|
||||
|
||||
Trigger open email and reminder after delay::
|
||||
|
||||
>>> record_activity.click('on_email_opened')
|
||||
|
||||
>>> open_activity, = RecordActivity.find([
|
||||
... ('record', '=', record.id),
|
||||
... ('activity', '=', email_opened.id),
|
||||
... ])
|
||||
>>> bool(open_activity.at)
|
||||
True
|
||||
>>> open_activity.state
|
||||
'waiting'
|
||||
|
||||
>>> cron_process.click('run_once')
|
||||
|
||||
>>> open_activity.reload()
|
||||
>>> open_activity.state
|
||||
'done'
|
||||
>>> root_activity.reload()
|
||||
>>> root_activity.email_opened
|
||||
1
|
||||
>>> root_activity.email_opened
|
||||
1
|
||||
>>> root_activity.email_clicked
|
||||
0
|
||||
>>> root_activity.email_open_rate
|
||||
1.0
|
||||
>>> bool(root_activity.email_open_rate_trend)
|
||||
True
|
||||
>>> root_activity.email_click_rate
|
||||
0.0
|
||||
>>> bool(root_activity.email_click_rate_trend)
|
||||
True
|
||||
>>> root_activity.email_click_through_rate
|
||||
0.0
|
||||
>>> bool(root_activity.email_click_through_rate_trend)
|
||||
True
|
||||
|
||||
>>> reporting, = ReportingActivity.find([
|
||||
... ('activity', '=', root_activity.id),
|
||||
... ])
|
||||
>>> reporting.activity_action
|
||||
'send_email'
|
||||
>>> reporting.record_count
|
||||
1
|
||||
>>> reporting.email_opened
|
||||
1
|
||||
>>> reporting.email_clicked
|
||||
0
|
||||
>>> reporting.email_open_rate
|
||||
1.0
|
||||
>>> reporting.email_click_rate
|
||||
0.0
|
||||
>>> reporting.email_click_through_rate
|
||||
0.0
|
||||
|
||||
>>> email_reminder, = RecordActivity.find([
|
||||
... ('record', '=', record.id),
|
||||
... ('activity', '=', email_reminder.id),
|
||||
... ])
|
||||
>>> email_reminder.state
|
||||
'done'
|
||||
|
||||
>>> smtp_calls.call_count
|
||||
1
|
||||
>>> smtp_calls.reset_mock()
|
||||
|
||||
Trigger click email::
|
||||
|
||||
>>> record_activity.click('on_email_clicked')
|
||||
>>> cron_process.click('run_once')
|
||||
|
||||
>>> clicked_activity, = RecordActivity.find([
|
||||
... ('record', '=', record.id),
|
||||
... ('activity', '=', email_clicked.id),
|
||||
... ])
|
||||
>>> clicked_activity.state
|
||||
'done'
|
||||
>>> root_activity.reload()
|
||||
>>> root_activity.record_count
|
||||
1
|
||||
>>> root_activity.email_opened
|
||||
1
|
||||
>>> root_activity.email_clicked
|
||||
1
|
||||
>>> root_activity.email_open_rate
|
||||
1.0
|
||||
>>> root_activity.email_click_rate
|
||||
1.0
|
||||
>>> root_activity.email_click_through_rate
|
||||
1.0
|
||||
|
||||
>>> reporting.reload()
|
||||
>>> reporting.record_count
|
||||
1
|
||||
>>> reporting.email_opened
|
||||
1
|
||||
>>> reporting.email_clicked
|
||||
1
|
||||
>>> reporting.email_open_rate
|
||||
1.0
|
||||
>>> reporting.email_click_rate
|
||||
1.0
|
||||
>>> reporting.email_click_through_rate
|
||||
1.0
|
||||
|
||||
>>> not_clicked_activity, = RecordActivity.find([
|
||||
... ('record', '=', record.id),
|
||||
... ('activity', '=', email_not_clicked.id),
|
||||
... ])
|
||||
>>> not_clicked_activity.state
|
||||
'cancelled'
|
||||
@@ -0,0 +1,117 @@
|
||||
Marketing Automation Unsubscribable Scenario
|
||||
============================================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> from unittest.mock import patch
|
||||
|
||||
>>> from proteus import Model
|
||||
>>> from trytond.modules.company.tests.tools import create_company
|
||||
>>> from trytond.modules.marketing_automation import marketing_automation
|
||||
>>> from trytond.pyson import Eval, PYSONEncoder
|
||||
>>> from trytond.tests.tools import activate_modules, assertEqual
|
||||
>>> from trytond.tools import file_open
|
||||
|
||||
Patch send_message_transactional::
|
||||
|
||||
>>> smtp_calls = patch.object(
|
||||
... marketing_automation, 'send_message_transactional').start()
|
||||
>>> manager = patch.object(
|
||||
... marketing_automation, 'SMTPDataManager').start()
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules(['marketing_automation', 'sale'], create_company)
|
||||
|
||||
>>> Activity = Model.get('marketing.automation.activity')
|
||||
>>> Cron = Model.get('ir.cron')
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> Record = Model.get('marketing.automation.record')
|
||||
>>> Sale = Model.get('sale.sale')
|
||||
>>> Scenario = Model.get('marketing.automation.scenario')
|
||||
|
||||
>>> cron_trigger, = Cron.find([
|
||||
... ('method', '=', 'marketing.automation.scenario|trigger'),
|
||||
... ])
|
||||
>>> cron_process, = Cron.find([
|
||||
... ('method', '=', 'marketing.automation.record.activity|process'),
|
||||
... ])
|
||||
|
||||
Create a party::
|
||||
|
||||
>>> party = Party()
|
||||
>>> party.name = "Michael Scott"
|
||||
>>> contact = party.contact_mechanisms.new()
|
||||
>>> contact.type = 'email'
|
||||
>>> contact.value = 'michael@example.com'
|
||||
>>> party.save()
|
||||
|
||||
Create a sale::
|
||||
|
||||
>>> sale = Sale(party=party)
|
||||
>>> sale.save()
|
||||
|
||||
Create the running scenario::
|
||||
|
||||
>>> scenario = Scenario()
|
||||
>>> scenario.name = "Sale Scenario"
|
||||
>>> scenario.model = 'sale.sale'
|
||||
>>> scenario.domain = '[["state", "=", "draft"]]'
|
||||
>>> scenario.unsubscribable = True
|
||||
>>> scenario.save()
|
||||
|
||||
>>> activity = scenario.activities.new()
|
||||
>>> activity.name = "First email"
|
||||
>>> activity.action = 'send_email'
|
||||
>>> activity.email_title = "Pending Sale"
|
||||
>>> activity.condition = PYSONEncoder().encode(
|
||||
... Eval('self', {}).get('active'))
|
||||
>>> with file_open('marketing_automation/tests/email.html', mode='r') as fp:
|
||||
... activity.email_template = fp.read()
|
||||
|
||||
>>> scenario.click('run')
|
||||
|
||||
Trigger scenario::
|
||||
|
||||
>>> cron_trigger.click('run_once')
|
||||
>>> cron_process.click('run_once')
|
||||
|
||||
>>> record, = Record.find([])
|
||||
>>> assertEqual(record.record, sale)
|
||||
|
||||
>>> scenario.reload()
|
||||
>>> scenario.record_count
|
||||
1
|
||||
>>> scenario.record_count_blocked
|
||||
0
|
||||
>>> scenario.block_rate
|
||||
0.0
|
||||
|
||||
Block and unsubscribe::
|
||||
|
||||
>>> record.click('block')
|
||||
>>> bool(record.blocked)
|
||||
True
|
||||
>>> party.reload()
|
||||
>>> assertEqual(party.marketing_scenario_unsubscribed, [scenario])
|
||||
|
||||
>>> scenario.reload()
|
||||
>>> scenario.record_count
|
||||
1
|
||||
>>> scenario.record_count_blocked
|
||||
1
|
||||
>>> scenario.block_rate
|
||||
1.0
|
||||
|
||||
Create a new sale::
|
||||
|
||||
>>> sale = Sale(party=party)
|
||||
>>> sale.save()
|
||||
|
||||
Trigger scenario::
|
||||
|
||||
>>> cron_trigger.click('run_once')
|
||||
>>> cron_process.click('run_once')
|
||||
|
||||
>>> Record.find([('blocked', '=', False)])
|
||||
[]
|
||||
13
modules/marketing_automation/tests/test_module.py
Normal file
13
modules/marketing_automation/tests/test_module.py
Normal file
@@ -0,0 +1,13 @@
|
||||
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
|
||||
from trytond.tests.test_tryton import ModuleTestCase
|
||||
|
||||
|
||||
class MarketingAutomationTestCase(ModuleTestCase):
|
||||
'Test Marketing Automation module'
|
||||
module = 'marketing_automation'
|
||||
extras = ['sale']
|
||||
|
||||
|
||||
del ModuleTestCase
|
||||
8
modules/marketing_automation/tests/test_scenario.py
Normal file
8
modules/marketing_automation/tests/test_scenario.py
Normal file
@@ -0,0 +1,8 @@
|
||||
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
|
||||
from trytond.tests.test_tryton import load_doc_tests
|
||||
|
||||
|
||||
def load_tests(*args, **kwargs):
|
||||
return load_doc_tests(__name__, __file__, *args, **kwargs)
|
||||
Reference in New Issue
Block a user