Skip to content
This repository has been archived by the owner on Feb 8, 2018. It is now read-only.

Add bulk claiming at /on/npm/ #4488

Merged
merged 3 commits into from
Jun 16, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 38 additions & 11 deletions js/gratipay/packages.js
Original file line number Diff line number Diff line change
@@ -1,33 +1,60 @@
Gratipay.packages = {};

Gratipay.packages.init = function() {
Gratipay.packages.initBulk = function() {
$('button.apply').on('click', Gratipay.packages.postBulk);
};

Gratipay.packages.initSingle = function() {
Gratipay.Select('.gratipay-select');
$('button.apply').on('click', Gratipay.packages.post);
$('button.apply').on('click', Gratipay.packages.postOne);
};

Gratipay.packages.post = function(e) {

Gratipay.packages.postBulk = function(e) {
e.preventDefault();
var pkg, email, package_id, package_ids_by_email={};
$('table.listing td.item ').not('.disabled').each(function() {
pkg = $(this).data();
if (package_ids_by_email[pkg.email] === undefined)
package_ids_by_email[pkg.email] = [];
package_ids_by_email[pkg.email].push(pkg.packageId);
});
for (email in package_ids_by_email)
Gratipay.packages.post(email, package_ids_by_email[email], true);
};

Gratipay.packages.postOne = function(e) {
e.preventDefault();
var $this = $(this);
var action = 'start-verification';
var package_id = $('input[name=package_id]').val();
var email = $('input[name=email]:checked').val();
var package_id = $('input[name=package_id]').val();
Gratipay.packages.post(email, [package_id]);
}


var $inputs = $('input, button');
$inputs.prop('disabled', true);
Gratipay.packages.post = function(email, package_ids, show_email) {
var action = 'start-verification';
var $button = $('button.apply')

$button.prop('disabled', true);
function reenable() { $button.prop('disabled', false); }
$.ajax({
url: '/~' + Gratipay.username + '/emails/modify.json',
type: 'POST',
data: {action: action, address: email, package_id: package_id},
data: { action: action
, address: email
, package_id: package_ids
, show_address_in_message: true
},
traditional: true,
dataType: 'json',
success: function (msg) {
if (msg) {
Gratipay.notification(msg, 'success');
reenable();
}
$inputs.prop('disabled', false);
},
error: [
function () { $inputs.prop('disabled', false); },
reenable,
Gratipay.error
],
});
Expand Down
18 changes: 18 additions & 0 deletions scss/components/listing.scss
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,24 @@ table.listing {
border-bottom: 1px solid $light-brown;
}
}

&.disabled {
img.avatar {
filter: grayscale(100%) brightness(110%);
}
.package-manager img {
filter: grayscale(100%) brightness(120%);
}
.listing-name {
color: $light-gray ! important;
}
.status a {
color: $medium-gray! important;
}
.owner a {
color: $medium-gray! important;
}
}
}
}
.with-sidebar table.listing td.item .listing-details {
Expand Down
12 changes: 8 additions & 4 deletions scss/components/text-treatments.scss
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,13 @@
}
}

.sorry {
.instructions {
text-align: center;
font: normal 12px/15px $Ideal;
color: $medium-gray;
margin: 0 0 30px;
font-family: $Ideal;
}
.note {

.note, .sorry, .fine-print {
font: normal 12px/15px $Ideal;
color: $medium-gray;
a {
Expand All @@ -29,6 +30,9 @@
color: $medium-gray;
}
}
.sorry, .fine-print {
text-align: center;
}
.listing-name {
color: $black;
font: bold 20px/24px $Ideal;
Expand Down
6 changes: 0 additions & 6 deletions scss/pages/package.scss
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
#package #content {
text-align: center;

.instructions {
text-align: center;
margin: 0 0 30px;
font-family: $Ideal;
}

.selected .icon { display: block; }
.icon {
display: none;
Expand Down
10 changes: 10 additions & 0 deletions tests/py/test_www_npm_package.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,13 @@ def test_package_redirects_to_project_if_claimed(self):
def test_package_served_as_project_if_claimed(self):
self.claim_package()
assert 'owned by' in self.client.GET('/foo/').body


class Bulk(Harness):

def setUp(self):
self.make_package()

def test_anon_gets_signin_page(self):
body = self.client.GET('/on/npm/').body
assert '0 out of all 1 npm package' in body
92 changes: 91 additions & 1 deletion tests/ttw/test_package_claiming.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ def check(self, choice=0):
self.css('label')[0].click() # activate select
self.css('label')[choice].click()
self.css('button')[0].click()
assert self.wait_for_success() == 'Check your inbox for a verification link.'
address = ('alice' if choice == 0 else 'bob') + '@example.com'
assert self.wait_for_success() == 'Check {} for a verification link.'.format(address)
return self.db.one('select address from claims c join emails e on c.nonce = e.nonce')

def finish_claiming(self):
Expand Down Expand Up @@ -143,3 +144,92 @@ def test_jdorfman_can_merge_accounts(self):
payload = eval(self.css('table#events td.payload').text)
assert payload['action'] == 'take-over'
assert payload['values']['exchange_routes'] == [r.id for r in jdorfman.get_payout_routes()]


class BulkClaiming(BrowserHarness):

def setUp(self):
self.make_package()
self.make_package( name='bar'
, description='Bar barringly'
, emails=['[email protected]', '[email protected]']
)
self.make_package( name='baz'
, description='Baz bazzingly'
, emails=['[email protected]', '[email protected]', '[email protected]']
)

def visit_as(self, username):
self.visit('/')
self.sign_in(username)
self.visit('/on/npm/')

def test_anon_gets_sign_in_prompt(self):
self.visit('/on/npm/')
assert self.css('.important-button button').text == 'Sign in / Sign up'

def test_auth_without_email_gets_highlighted_link_to_email(self):
self.make_participant('alice', claimed_time='now')
self.visit_as('alice')
assert self.css('.highlight').text == 'Link an email'

def test_auth_without_claimable_packages_gets_disabled_apply_button(self):
self.make_participant('doug', claimed_time='now', email_address='[email protected]')
self.visit_as('doug')
button = self.css('.important-button button')
assert button.text == 'Apply to accept payments'
assert button['disabled'] == 'true'

def test_auth_with_claimable_packages_gets_apply_button(self):
self.make_participant('alice', claimed_time='now', email_address='[email protected]')
self.add_and_verify_email('alice', '[email protected]')
self.visit_as('alice')
button = self.css('.important-button button')
assert button.text == 'Apply to accept payments'
assert button['disabled'] is None

def test_differentiates_claimed_packages(self):
self.make_participant('bob', claimed_time='now', email_address='[email protected]')
self.make_participant('alice', claimed_time='now', email_address='[email protected]')
self.claim_package('alice', 'foo')
self.claim_package('bob', 'bar')
self.visit_as('alice')
assert self.css('.i1').has_class('disabled')
assert self.css('.i1 .owner a').text == '~bob'
assert not self.css('.i2').has_class('disabled')
assert self.css('.i3').has_class('disabled')
assert self.css('.i3 .owner a').text == 'you'

def test_sends_mail(self):
self.make_participant('cat', claimed_time='now', email_address='[email protected]')
self.visit_as('cat')
self.css('.important-button button').click()
assert self.wait_for_success() == 'Check [email protected] for a verification link.'

def test_sends_one_mail_per_address(self):
cat = self.make_participant('cat', claimed_time='now', email_address='[email protected]')
self.add_and_verify_email(cat, '[email protected]')
self.visit_as('cat')
self.css('.important-button button').click()
assert self.wait_for_success('Check [email protected] for a verification link.')
assert self.wait_for_success('Check [email protected] for a verification link.')

def test_sends_one_mail_for_multiple_packages(self):
self.make_participant('alice', claimed_time='now', email_address='[email protected]')
self.visit_as('alice')
self.css('.important-button button').click()
assert len(self.css('table.listing td.item')) == 3
assert self.wait_for_success() == 'Check [email protected] for a verification link.'
assert self.db.one('select count(*) from claims') == 3
assert self.db.one('select count(*) from email_queue') == 1

def test_doesnt_send_for_unclaimable_packages(self):
self.make_participant('alice', claimed_time='now', email_address='[email protected]')
self.make_participant('cat', claimed_time='now', email_address='[email protected]')
self.claim_package('cat', 'baz')
self.visit_as('alice')
self.css('.important-button button').click()
assert len(self.css('table.listing td.item')) == 3
assert self.wait_for_success() == 'Check [email protected] for a verification link.'
assert self.db.one('select count(*) from claims') == 2
assert self.db.one('select count(*) from email_queue') == 1
32 changes: 18 additions & 14 deletions www/on/npm/%package/index.html.spt
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ if user.participant:
{% block scripts %}
<script>
$(document).ready(function() {
Gratipay.packages.init();
Gratipay.packages.initSingle();
});
</script>
{{ super() }}
Expand All @@ -48,22 +48,22 @@ if user.participant:
{% if package.description %}
<p class="description important-thing-at-the-top{% if len(package.description) > 256 %} long{% endif %}">{{ package.description }}</p>
{% else %}
<p class="note important-thing-at-the-top">{{ _("No description available.") }}</p>
<p class="sorry important-thing-at-the-top">{{ _("No description available.") }}</p>
{% endif %}

<p class="instructions">
{{ _( 'Apply to accept payments for the {package_link} npm package:'
, package_link=('<a href="' + package.remote_human_url + '">' + package_name + '</a>')|safe
) }}
</p>
</p>

{% if user.ANON %}
<div class="important-button">
{{ sign_in_using(button_class='large') }}
</div>
{% else %}
{% if len(emails) == 0 %}
<p class="note">{{ _("No email addresses on file.") }}</p>
<p class="sorry">{{ _("No email addresses on file.") }}</p>
{% else %}
<input type="hidden" name="package_id" value="{{ package.id }}">
<div class="gratipay-select">
Expand Down Expand Up @@ -115,15 +115,19 @@ if user.participant:
</button>
</div>
{% endif %}
<p class="note">{{ _( 'Addresses are from {a}{code}maintainers{_code}{_a}.'
, a=('<a href="' + package.remote_api_url + '">')|safe
, _a='</a>'|safe
, code='<code>'|safe
, _code='</code>'|safe
) }}</p>
<p class="note">{{ _( 'Out of date? Update {a}at npm{_a} and refresh.'
, a=('<a href="' + package.remote_human_url + '">')|safe
, _a='</a>'|safe
) }}</p>
<p class="fine-print">
{{ _( 'Addresses are from {a}{code}maintainers{_code}{_a}.'
, a=('<a href="' + package.remote_api_url + '">')|safe
, _a='</a>'|safe
, code='<code>'|safe
, _code='</code>'|safe
) }}
</p>
<p class="fine-print">
{{ _( 'Out of date? Update {a}at npm{_a} and refresh.'
, a=('<a href="' + package.remote_human_url + '">')|safe
, _a='</a>'|safe
) }}
</p>
{% endif %}
{% endblock %}
Loading