@ -24,14 +24,14 @@ class LoginTestCase(BaseTestCase): | |||
def test_it_sends_link_with_next(self): | |||
form = {"identity": "[email protected]"} | |||
r = self.client.post("/accounts/login/?next=/integrations/add_slack/", form) | |||
r = self.client.post("/accounts/login/?next=" + self.channels_url, form) | |||
self.assertRedirects(r, "/accounts/login_link_sent/") | |||
self.assertIn("auto-login", r.cookies) | |||
# The check_token link should have a ?next= query parameter: | |||
self.assertEqual(len(mail.outbox), 1) | |||
body = mail.outbox[0].body | |||
self.assertTrue("/?next=/integrations/add_slack/" in body) | |||
self.assertTrue("/?next=" + self.channels_url in body) | |||
@override_settings(SECRET_KEY="test-secret") | |||
def test_it_rate_limits_emails(self): | |||
@ -85,7 +85,7 @@ class LoginTestCase(BaseTestCase): | |||
form = {"action": "login", "email": "[email protected]", "password": "password"} | |||
samples = ["/integrations/add_slack/", "/checks/%s/details/" % check.code] | |||
samples = [self.channels_url, "/checks/%s/details/" % check.code] | |||
for s in samples: | |||
r = self.client.post("/accounts/login/?next=%s" % s, form) | |||
@ -1,33 +1,32 @@ | |||
from django.test.utils import override_settings | |||
from hc.api.models import Channel | |||
from hc.test import BaseTestCase | |||
class AddSlackTestCase(BaseTestCase): | |||
@override_settings(SLACK_CLIENT_ID=None) | |||
def setUp(self): | |||
super(AddSlackTestCase, self).setUp() | |||
self.url = "/projects/%s/add_slack/" % self.project.code | |||
def test_instructions_work(self): | |||
self.client.login(username="[email protected]", password="password") | |||
r = self.client.get("/integrations/add_slack/") | |||
r = self.client.get(self.url) | |||
self.assertContains(r, "Integration Settings", status_code=200) | |||
@override_settings(SLACK_CLIENT_ID=None) | |||
def test_it_works(self): | |||
form = {"value": "http://example.org"} | |||
self.client.login(username="[email protected]", password="password") | |||
r = self.client.post("/integrations/add_slack/", form) | |||
self.assertRedirects(r, "/integrations/") | |||
r = self.client.post(self.url, form) | |||
self.assertRedirects(r, self.channels_url) | |||
c = Channel.objects.get() | |||
self.assertEqual(c.kind, "slack") | |||
self.assertEqual(c.value, "http://example.org") | |||
self.assertEqual(c.project, self.project) | |||
@override_settings(SLACK_CLIENT_ID=None) | |||
def test_it_rejects_bad_url(self): | |||
form = {"value": "not an URL"} | |||
self.client.login(username="[email protected]", password="password") | |||
r = self.client.post("/integrations/add_slack/", form) | |||
r = self.client.post(self.url, form) | |||
self.assertContains(r, "Enter a valid URL") |
@ -1,84 +1,22 @@ | |||
import json | |||
from django.test.utils import override_settings | |||
from hc.api.models import Channel | |||
from hc.test import BaseTestCase | |||
from mock import patch | |||
@override_settings(SLACK_CLIENT_ID="fake-client-id") | |||
class AddSlackBtnTestCase(BaseTestCase): | |||
@override_settings(SLACK_CLIENT_ID="foo") | |||
def test_it_prepares_login_link(self): | |||
r = self.client.get("/integrations/add_slack/") | |||
self.assertContains(r, "Before adding Slack integration", status_code=200) | |||
def setUp(self): | |||
super(AddSlackBtnTestCase, self).setUp() | |||
self.url = "/projects/%s/add_slack_btn/" % self.project.code | |||
self.assertContains(r, "?next=/integrations/add_slack/") | |||
def test_instructions_work(self): | |||
self.client.login(username="[email protected]", password="password") | |||
r = self.client.get(self.url) | |||
self.assertContains(r, "Setup Guide", status_code=200) | |||
@override_settings(SLACK_CLIENT_ID="foo") | |||
def test_slack_button(self): | |||
self.client.login(username="[email protected]", password="password") | |||
r = self.client.get("/integrations/add_slack/") | |||
r = self.client.get(self.url) | |||
self.assertContains(r, "slack.com/oauth/authorize", status_code=200) | |||
# There should now be a key in session | |||
self.assertTrue("slack" in self.client.session) | |||
@patch("hc.front.views.requests.post") | |||
def test_it_handles_oauth_response(self, mock_post): | |||
session = self.client.session | |||
session["slack"] = "foo" | |||
session.save() | |||
oauth_response = { | |||
"ok": True, | |||
"team_name": "foo", | |||
"incoming_webhook": {"url": "http://example.org", "channel": "bar"}, | |||
} | |||
mock_post.return_value.text = json.dumps(oauth_response) | |||
mock_post.return_value.json.return_value = oauth_response | |||
url = "/integrations/add_slack_btn/?code=12345678&state=foo" | |||
self.client.login(username="[email protected]", password="password") | |||
r = self.client.get(url, follow=True) | |||
self.assertRedirects(r, "/integrations/") | |||
self.assertContains(r, "The Slack integration has been added!") | |||
ch = Channel.objects.get() | |||
self.assertEqual(ch.slack_team, "foo") | |||
self.assertEqual(ch.slack_channel, "bar") | |||
self.assertEqual(ch.slack_webhook_url, "http://example.org") | |||
self.assertEqual(ch.project, self.project) | |||
# Session should now be clean | |||
self.assertFalse("slack" in self.client.session) | |||
def test_it_avoids_csrf(self): | |||
session = self.client.session | |||
session["slack"] = "foo" | |||
session.save() | |||
url = "/integrations/add_slack_btn/?code=12345678&state=bar" | |||
self.client.login(username="[email protected]", password="password") | |||
r = self.client.get(url) | |||
self.assertEqual(r.status_code, 400) | |||
@patch("hc.front.views.requests.post") | |||
def test_it_handles_oauth_error(self, mock_post): | |||
session = self.client.session | |||
session["slack"] = "foo" | |||
session.save() | |||
oauth_response = {"ok": False, "error": "something went wrong"} | |||
mock_post.return_value.text = json.dumps(oauth_response) | |||
mock_post.return_value.json.return_value = oauth_response | |||
url = "/integrations/add_slack_btn/?code=12345678&state=foo" | |||
self.client.login(username="[email protected]", password="password") | |||
r = self.client.get(url, follow=True) | |||
self.assertRedirects(r, "/integrations/") | |||
self.assertContains(r, "something went wrong") | |||
self.assertTrue("add_slack" in self.client.session) |
@ -0,0 +1,69 @@ | |||
import json | |||
from django.test.utils import override_settings | |||
from hc.api.models import Channel | |||
from hc.test import BaseTestCase | |||
from mock import patch | |||
@override_settings(SLACK_CLIENT_ID="fake-client-id") | |||
class AddSlackCompleteTestCase(BaseTestCase): | |||
@patch("hc.front.views.requests.post") | |||
def test_it_handles_oauth_response(self, mock_post): | |||
session = self.client.session | |||
session["add_slack"] = ("foo", str(self.project.code)) | |||
session.save() | |||
oauth_response = { | |||
"ok": True, | |||
"team_name": "foo", | |||
"incoming_webhook": {"url": "http://example.org", "channel": "bar"}, | |||
} | |||
mock_post.return_value.text = json.dumps(oauth_response) | |||
mock_post.return_value.json.return_value = oauth_response | |||
url = "/integrations/add_slack_btn/?code=12345678&state=foo" | |||
self.client.login(username="[email protected]", password="password") | |||
r = self.client.get(url, follow=True) | |||
self.assertRedirects(r, self.channels_url) | |||
self.assertContains(r, "The Slack integration has been added!") | |||
ch = Channel.objects.get() | |||
self.assertEqual(ch.slack_team, "foo") | |||
self.assertEqual(ch.slack_channel, "bar") | |||
self.assertEqual(ch.slack_webhook_url, "http://example.org") | |||
self.assertEqual(ch.project, self.project) | |||
# Session should now be clean | |||
self.assertFalse("add_slack" in self.client.session) | |||
def test_it_avoids_csrf(self): | |||
session = self.client.session | |||
session["add_slack"] = ("foo", str(self.project.code)) | |||
session.save() | |||
url = "/integrations/add_slack_btn/?code=12345678&state=bar" | |||
self.client.login(username="[email protected]", password="password") | |||
r = self.client.get(url) | |||
self.assertEqual(r.status_code, 403) | |||
@patch("hc.front.views.requests.post") | |||
def test_it_handles_oauth_error(self, mock_post): | |||
session = self.client.session | |||
session["add_slack"] = ("foo", str(self.project.code)) | |||
session.save() | |||
oauth_response = {"ok": False, "error": "something went wrong"} | |||
mock_post.return_value.text = json.dumps(oauth_response) | |||
mock_post.return_value.json.return_value = oauth_response | |||
url = "/integrations/add_slack_btn/?code=12345678&state=foo" | |||
self.client.login(username="[email protected]", password="password") | |||
r = self.client.get(url, follow=True) | |||
self.assertRedirects(r, self.channels_url) | |||
self.assertContains(r, "something went wrong") |
@ -0,0 +1,9 @@ | |||
from django.test.utils import override_settings | |||
from hc.test import BaseTestCase | |||
@override_settings(SLACK_CLIENT_ID="fake-client-id") | |||
class AddSlackHelpTestCase(BaseTestCase): | |||
def test_instructions_work(self): | |||
r = self.client.get("/integrations/add_slack/") | |||
self.assertContains(r, "Setup Guide", status_code=200) |
@ -3,97 +3,9 @@ | |||
{% block title %}Add Slack - {% site_name %}{% endblock %} | |||
{% block content %} | |||
<div class="row"> | |||
<div class="col-sm-12"> | |||
{% if slack_client_id %} | |||
<div class="jumbotron"> | |||
{% if request.user.is_authenticated %} | |||
<p>If your team uses <a href="https://slack.com/">Slack</a>, you can set | |||
up {% site_name %} to post status updates directly to an appropriate | |||
Slack channel.</p> | |||
<div class="text-center"> | |||
<a href="https://slack.com/oauth/authorize?scope=incoming-webhook&client_id={{ slack_client_id }}&state={{ state }}"> | |||
<img | |||
alt="Add to Slack" | |||
src="{% static 'img/integrations/add_to_slack.png' %}" | |||
srcset="{% static 'img/integrations/add_to_slack.png' %} 1x, {% static 'img/integrations/[email protected]' %} 2x" /> | |||
</a> | |||
</div> | |||
{% else %} | |||
<p> | |||
{% site_name %} is a <strong>free</strong> and | |||
<a href="https://github.com/healthchecks/healthchecks">open source</a> | |||
service for monitoring your cron jobs, background processes and | |||
scheduled tasks. Before adding Slack integration, please log into | |||
{% site_name %}:</p> | |||
<div class="text-center"> | |||
<a href="{% url 'hc-login' %}?next={% url 'hc-add-slack' %}" | |||
class="btn btn-primary btn-lg">Sign In</a> | |||
</div> | |||
{% endif %} | |||
</div> | |||
<h2>Setup Guide</h2> | |||
<div class="row ai-step"> | |||
<div class="col-sm-6"> | |||
<span class="step-no">1</span> | |||
<p> | |||
After {% if request.user.is_authenticated %}{% else %}signing in and{% endif %} | |||
clicking on "Add to Slack", you should | |||
be on a page that says "{% site_name %} would like access to | |||
your Slack team". Select the team you want to add the | |||
{% site_name %} integration app to. | |||
</p> | |||
</div> | |||
<div class="col-sm-6"> | |||
<img | |||
class="ai-guide-screenshot" | |||
alt="Screenshot" | |||
src="{% static 'img/integrations/setup_slack_btn_1.png' %}"> | |||
</div> | |||
</div> | |||
<div class="row ai-step"> | |||
<div class="col-sm-6"> | |||
<span class="step-no">2</span> | |||
<p> | |||
You should now be on a page that says "{% site_name %} would | |||
like access to <i>TEAM NAME</i>". Select the channel you want to | |||
post {% site_name %} notifications to. | |||
</p> | |||
</div> | |||
<div class="col-sm-6"> | |||
<img | |||
class="ai-guide-screenshot" | |||
alt="Screenshot" | |||
src="{% static 'img/integrations/setup_slack_btn_2.png' %}"> | |||
</div> | |||
</div> | |||
<div class="row ai-step"> | |||
<div class="col-sm-6"> | |||
<span class="step-no">3</span> | |||
<p> | |||
That is all! You will now be redirected back to | |||
"Integrations" page on {% site_name %} and see | |||
the new integration! | |||
</p> | |||
</div> | |||
<div class="col-sm-6"> | |||
<img | |||
class="ai-guide-screenshot" | |||
alt="Screenshot" | |||
src="{% static 'img/integrations/setup_slack_btn_3.png' %}"> | |||
</div> | |||
</div> | |||
{% else %} | |||
<h1>Slack</h1> | |||
<p>If your team uses <a href="https://slack.com/">Slack</a>, you can set | |||
@ -141,7 +53,7 @@ | |||
<h2>Integration Settings</h2> | |||
<form method="post" class="form-horizontal" action="{% url 'hc-add-slack' %}"> | |||
<form method="post" class="form-horizontal"> | |||
{% csrf_token %} | |||
<div class="form-group {{ form.value.css_classes }}"> | |||
<label for="callback-url" class="col-sm-2 control-label"> | |||
@ -169,8 +81,6 @@ | |||
</div> | |||
</div> | |||
</form> | |||
{% endif %} | |||
</div> | |||
</div> | |||
{% endblock %} |
@ -0,0 +1,97 @@ | |||
{% extends "base.html" %} | |||
{% load humanize static hc_extras %} | |||
{% block title %}Add Slack - {% site_name %}{% endblock %} | |||
{% block content %} | |||
<div class="row"> | |||
<div class="col-sm-12"> | |||
<div class="jumbotron"> | |||
{% if request.user.is_authenticated %} | |||
<p>If your team uses <a href="https://slack.com/">Slack</a>, you can set | |||
up {% site_name %} to post status updates directly to an appropriate | |||
Slack channel.</p> | |||
<div class="text-center"> | |||
<a href="{{ authorize_url }}"> | |||
<img | |||
alt="Add to Slack" | |||
src="{% static 'img/integrations/add_to_slack.png' %}" | |||
srcset="{% static 'img/integrations/add_to_slack.png' %} 1x, {% static 'img/integrations/[email protected]' %} 2x" /> | |||
</a> | |||
</div> | |||
{% else %} | |||
<p> | |||
{% site_name %} is a <strong>free</strong> and | |||
<a href="https://github.com/healthchecks/healthchecks">open source</a> | |||
service for monitoring your cron jobs, background processes and | |||
scheduled tasks. Before adding Slack integration, please log into | |||
{% site_name %}:</p> | |||
<div class="text-center"> | |||
<a href="{% url 'hc-login' %}" | |||
class="btn btn-primary btn-lg">Sign In</a> | |||
</div> | |||
{% endif %} | |||
</div> | |||
<h2>Setup Guide</h2> | |||
<div class="row ai-step"> | |||
<div class="col-sm-6"> | |||
<span class="step-no">1</span> | |||
<p> | |||
After {% if request.user.is_authenticated %}{% else %}signing in and{% endif %} | |||
clicking on "Add to Slack", you should | |||
be on a page that says "{% site_name %} would like access to | |||
your Slack team". Select the team you want to add the | |||
{% site_name %} integration app to. | |||
</p> | |||
</div> | |||
<div class="col-sm-6"> | |||
<img | |||
class="ai-guide-screenshot" | |||
alt="Screenshot" | |||
src="{% static 'img/integrations/setup_slack_btn_1.png' %}"> | |||
</div> | |||
</div> | |||
<div class="row ai-step"> | |||
<div class="col-sm-6"> | |||
<span class="step-no">2</span> | |||
<p> | |||
You should now be on a page that says "{% site_name %} would | |||
like access to <i>TEAM NAME</i>". Select the channel you want to | |||
post {% site_name %} notifications to. | |||
</p> | |||
</div> | |||
<div class="col-sm-6"> | |||
<img | |||
class="ai-guide-screenshot" | |||
alt="Screenshot" | |||
src="{% static 'img/integrations/setup_slack_btn_2.png' %}"> | |||
</div> | |||
</div> | |||
<div class="row ai-step"> | |||
<div class="col-sm-6"> | |||
<span class="step-no">3</span> | |||
<p> | |||
That is all! You will now be redirected back to | |||
"Integrations" page on {% site_name %} and see | |||
the new integration! | |||
</p> | |||
</div> | |||
<div class="col-sm-6"> | |||
<img | |||
class="ai-guide-screenshot" | |||
alt="Screenshot" | |||
src="{% static 'img/integrations/setup_slack_btn_3.png' %}"> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
{% endblock %} |