Merge remote-tracking branch 'status/develop' into sort-and-filter

This commit is contained in:
pablodip 2018-01-25 10:02:17 +01:00
commit 161cd7bd99
24 changed files with 431 additions and 49 deletions

1
.gitignore vendored
View File

@ -23,4 +23,3 @@ profiles.clj
.idea .idea
resources/contracts resources/contracts
node_modules node_modules
.DS_Store

View File

@ -1,11 +1,11 @@
# Commiteth # Status Open Bounty
Allows you to set bounties for Github issues, paid out in Ether. Allows you to set bounties for Github issues, paid out in Ether or any ERC-20 token.
More information: More information:
http://wiki.status.im/proposals/commiteth/ https://wiki.status.im/Status_Open_Bounty
Live beta version: Live production version:
https://openbounty.status.im https://openbounty.status.im
The `master` branch is automatically deployed here. The `master` branch is automatically deployed here.
@ -20,6 +20,26 @@ The `develop` branch is automatically deployed here.
You will need [Leiningen](https://github.com/technomancy/leiningen) 2.0 or above installed. You will need [Leiningen](https://github.com/technomancy/leiningen) 2.0 or above installed.
### PostgreSQL ### PostgreSQL
<<<<<<< HEAD
Make sure you install [PostgreSQL](https://www.postgresql.org/) and properly set it up:
```
sudo -u postgres psql -c "CREATE USER commiteth WITH PASSWORD 'commiteth';"
sudo -u postgres createdb commiteth
```
## Running
Launch following commands each in its own shell:
```
lein run
lein figwheel
lein less auto
```
=======
Make sure you install [PostgreSQL](https://www.postgresql.org/) and properly set it up: Make sure you install [PostgreSQL](https://www.postgresql.org/) and properly set it up:

View File

@ -10,7 +10,7 @@ For testing you will need:
* a Github account with administrative access to one or more repositories * a Github account with administrative access to one or more repositories
* for approving bounty payouts you will additionally need access to an Ethereum wallet. So far, Mist and [MetaMask](https://metamask.io/) have been used, but anything that provides the web3 javascript interface should work. * for approving bounty payouts you will additionally need access to an Ethereum wallet. So far, Mist and [MetaMask](https://metamask.io/) have been used, but anything that provides the web3 javascript interface should work.
The developers can be reached on the `#commiteth` channel in the [Status slack](http://slack.status.im/). The developers can be reached on the `#openbounty` channel in the [Status slack](http://slack.status.im/).
### Signing up ### Signing up
@ -60,6 +60,6 @@ To remove issue from the Bounties list you can close it in GitHub.
### Reporting bugs ### Reporting bugs
All bugs should be reported as issues in the [CommitETH Github repository](https://github.com/status-im/commiteth/issues). All bugs should be reported as issues in the [OpenBounty Github repository](https://github.com/status-im/open-bounty/issues).
Please first check that there is not already a duplicate issue. Issues should contain exact and minimal step-by-step instructions for reproducing the problem. Please first check that there is not already a duplicate issue. Issues should contain exact and minimal step-by-step instructions for reproducing the problem.

View File

@ -33,7 +33,11 @@
<AppenderRef ref="FILE"/> <AppenderRef ref="FILE"/>
</logger> </logger>
<root level="DEBUG"> <root level="DEBUG">
<appender-ref ref="STDOUT"/> <!-- <appender-ref ref="STDOUT"/> -->
<appender-ref ref="FILE"/>
</root>
<root level="INFO">
<!-- <appender-ref ref="STDOUT"/> -->
<appender-ref ref="FILE"/> <appender-ref ref="FILE"/>
</root> </root>
</configuration> </configuration>

View File

@ -69,7 +69,6 @@
[lein-auto "0.1.2"] [lein-auto "0.1.2"]
[lein-less "1.7.5"] [lein-less "1.7.5"]
[lein-shell "0.5.0"] [lein-shell "0.5.0"]
[cider/cider-nrepl "0.15.0-SNAPSHOT"]
[lein-sha-version "0.1.1"]] [lein-sha-version "0.1.1"]]
@ -144,7 +143,7 @@
:prep-tasks ["build-contracts" "javac"] :prep-tasks ["build-contracts" "javac"]
:doo {:build "test"} :doo {:build "test"}
:source-paths ["env/dev/clj" "test/clj"] :source-paths ["env/dev/clj" "test/clj" "src/cljs" "env/dev/cljs"]
:resource-paths ["env/dev/resources"] :resource-paths ["env/dev/resources"]
:repl-options {:init-ns user :repl-options {:init-ns user
:nrepl-middleware [cemerick.piggieback/wrap-cljs-repl]} :nrepl-middleware [cemerick.piggieback/wrap-cljs-repl]}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path fill="#57A7ED" fill-rule="evenodd" d="M7.223 11.293l7.068-7.067a.999.999 0 1 1 1.414 1.414l-6.377 6.377 6.377 6.376a.999.999 0 1 1-1.414 1.415L7.223 12.74a1.001 1.001 0 0 1-.288-.827 1 1 0 0 1 .288-.62z"/>
</svg>

After

Width:  |  Height:  |  Size: 307 B

View File

@ -53,8 +53,8 @@
new-user?) new-user?)
(try (try
(hubspot/create-hubspot-contact (:email user) (hubspot/create-hubspot-contact (:email user)
(:name user "") (:name user "")
(:login user)) (:login user))
(catch Throwable t (catch Throwable t
(log/error "Failed to create hubspot contact" t)))) (log/error "Failed to create hubspot contact" t))))
(assoc (found (str (env :server-address) "/app")) (assoc (found (str (env :server-address) "/app"))

View File

@ -312,12 +312,13 @@
(defn wrap-in-try-catch [func] (defn wrap-in-try-catch [func]
(try (try
(func)
(catch Throwable t (catch Throwable t
(log/error t)))) (log/error t))))
(defn run-tasks [tasks] (defn run-tasks [tasks]
(doall (doall
(map (fn [func] (wrap-in-try-catch (func))) (map (fn [func] (wrap-in-try-catch func))
tasks))) tasks)))
(defn run-1-min-interval-tasks [time] (defn run-1-min-interval-tasks [time]

View File

@ -2,6 +2,7 @@
(:require [re-frame.core :as rf] (:require [re-frame.core :as rf]
[reagent.core :as r] [reagent.core :as r]
[commiteth.common :refer [moment-timestamp [commiteth.common :refer [moment-timestamp
display-data-page
issue-url]])) issue-url]]))
@ -56,21 +57,19 @@
(defn activity-list [activity-items] (defn activity-list [activity-page-data]
[:div.ui.container.activity-container [:div.ui.container.activity-container
(if (empty? activity-items) (if (empty? (:items activity-page-data))
[:div.view-no-data-container [:div.view-no-data-container
[:p "No recent activity yet"]] [:p "No recent activity yet"]]
(into [:div.ui.items] (display-data-page activity-page-data activity-item :set-activity-page-number))])
(for [item activity-items]
^{:key item} [activity-item item])))] )
(defn activity-page [] (defn activity-page []
(let [activity-items (rf/subscribe [:activity-feed]) (let [activity-page-data (rf/subscribe [:activities-page])
activity-feed-loading? (rf/subscribe [:get-in [:activity-feed-loading?]])] activity-feed-loading? (rf/subscribe [:get-in [:activity-feed-loading?]])]
(fn [] (fn []
(if @activity-feed-loading? (if @activity-feed-loading?
[:div.view-loading-container [:div.view-loading-container
[:div.ui.active.inverted.dimmer [:div.ui.active.inverted.dimmer
[:div.ui.text.loader.view-loading-label "Loading"]]] [:div.ui.text.loader.view-loading-label "Loading"]]]
[activity-list @activity-items])))) [activity-list @activity-page-data]))))

View File

@ -2,6 +2,8 @@
(:require [reagent.core :as r] (:require [reagent.core :as r]
[re-frame.core :as rf] [re-frame.core :as rf]
[commiteth.common :refer [moment-timestamp [commiteth.common :refer [moment-timestamp
display-data-page
items-per-page
issue-url]] issue-url]]
[commiteth.handlers :as handlers] [commiteth.handlers :as handlers]
[commiteth.db :as db] [commiteth.db :as db]
@ -203,26 +205,70 @@
(rf/dispatch [::handlers/set-open-bounties-sorting-type sorting-type]))} (rf/dispatch [::handlers/set-open-bounties-sorting-type sorting-type]))}
(ui-model/bounty-sorting-type->name sorting-type)])])])))) (ui-model/bounty-sorting-type->name sorting-type)])])]))))
(defn bounties-list [open-bounties] ;(defn bounties-list [open-bounties]
; [:div.ui.container.open-bounties-container
; [:div.open-bounties-header "Bounties"]
; [:div.open-bounties-filter-and-sort
; [bounty-filters-view]
; [bounties-sort]]
; (if (empty? open-bounties)
; [:div.view-no-data-container
; [:p "No matching bounties found."]]
; (into [:div.ui.items]
; (for [bounty open-bounties]
; [bounty-item bounty])))])
(defn bounties-list [{:keys [items item-count page-number total-count]
:as bounty-page-data}]
[:div.ui.container.open-bounties-container [:div.ui.container.open-bounties-container
[:div.open-bounties-header "Bounties"] [:div.open-bounties-header "Bounties"]
[:div.open-bounties-filter-and-sort [:div.open-bounties-filter-and-sort
[bounty-filters-view] [bounty-filters-view]
[bounties-sort]] [bounties-sort]]
(if (empty? open-bounties) (if (empty? items)
[:div.view-no-data-container [:div.view-no-data-container
[:p "No matching bounties found."]] [:p "No matching bounties found."]]
(into [:div.ui.items] [:div
(for [bounty open-bounties] (let [left (inc (* (dec page-number) items-per-page))
[bounty-item bounty])))]) right (dec (+ left item-count))]
[:div.item-counts-label
[:span (str "Showing " left "-" right " of " total-count)]])
(display-data-page bounty-page-data bounty-item :set-bounty-page-number)])])
(defn bounties-page [] (defn bounties-page []
(let [open-bounties (rf/subscribe [::subs/filtered-and-sorted-open-bounties]) (let [bounty-page-data (rf/subscribe [:open-bounties-page])
open-bounties-loading? (rf/subscribe [:get-in [:open-bounties-loading?]])] open-bounties-loading? (rf/subscribe [:get-in [:open-bounties-loading?]])]
(fn [] (fn []
(if @open-bounties-loading? (if @open-bounties-loading?
[:div.view-loading-container [:div.view-loading-container
[:div.ui.active.inverted.dimmer [:div.ui.active.inverted.dimmer
[:div.ui.text.loader.view-loading-label "Loading"]]] [:div.ui.text.loader.view-loading-label "Loading"]]]
[bounties-list @open-bounties])))) [bounties-list @bounty-page-data]))))
;
;(defn bounties-page []
; (let [open-bounties (rf/subscribe [::subs/filtered-and-sorted-open-bounties])
;=======
;(defn bounties-list [{:keys [items item-count page-number total-count]
; :as bounty-page-data}]
; [:div.ui.container.open-bounties-container
; [:div.open-bounties-header "Bounties"]
; (if (empty? items)
; [:div.view-no-data-container
; [:p "No recent activity yet"]]
; [:div
; (let [left (inc (* (dec page-number) items-per-page))
; right (dec (+ left item-count))]
; [:div.item-counts-label
; [:span (str "Showing " left "-" right " of " total-count)]])
; (display-data-page bounty-page-data bounty-item :set-bounty-page-number)])])
;
;(defn bounties-page []
; (let [bounty-page-data (rf/subscribe [:open-bounties-page])
;>>>>>>> status/develop
; open-bounties-loading? (rf/subscribe [:get-in [:open-bounties-loading?]])]
; (fn []
; (if @open-bounties-loading?
; [:div.view-loading-container
; [:div.ui.active.inverted.dimmer
; [:div.ui.text.loader.view-loading-label "Loading"]]]
; [bounties-list @bounty-page-data]))))

View File

@ -29,3 +29,87 @@
(defn issue-url [owner repo number] (defn issue-url [owner repo number]
(str "https://github.com/" owner "/" repo "/issues/" number)) (str "https://github.com/" owner "/" repo "/issues/" number))
(def items-per-page 15)
(defn draw-page-numbers [page-number page-count set-page-kw]
"Draw page numbers for the pagination component.
Inserts ellipsis when list is too long, by default
max 6 items are allowed"
(let [draw-page-num-fn (fn [current? i]
^{:key i}
[:div.rectangle-rounded
(cond-> {}
(not current?)
(assoc :class "grayed-out"
:on-click #(rf/dispatch [set-page-kw i])))
i])
max-page-nums 6]
[:div.page-nums-container
(cond (<= page-count max-page-nums)
(for [i (map inc (range page-count))]
(draw-page-num-fn (= i page-number) i))
(<= page-number (- max-page-nums 3))
(concat
(for [i (map inc (range (- max-page-nums 2)))]
(draw-page-num-fn (= i page-number) i))
[^{:key (dec max-page-nums)}
[:div.page-nav-text [:span "..."]]]
[(draw-page-num-fn false page-count)])
(>= page-number (- page-count (- max-page-nums 4)))
(concat
[(draw-page-num-fn false 1)
^{:key 2}
[:div.page-nav-text [:span "..."]]]
(for [i (map inc (range (- page-count 4) page-count))]
(draw-page-num-fn (= i page-number) i))
)
:else
(concat
[(draw-page-num-fn false 1)
^{:key 2} [:div.page-nav-text [:span "..."]]]
(for [i [(dec page-number) page-number (inc page-number)]]
(draw-page-num-fn (= i page-number) i))
[^{:key (dec page-count)} [:div.page-nav-text [:span "..."]]
(draw-page-num-fn false page-count)]))]))
(defn display-data-page [{:keys [items
item-count
total-count
page-number
page-count]}
draw-item-fn
set-page-kw]
"Draw data items along with pagination controls"
(let [draw-items (fn []
(into [:div.ui.items]
(for [item items]
^{:key item} [draw-item-fn item])))
on-direction-click (fn [forward?]
#(when (or (and (< page-number page-count)
forward?)
(and (< 1 page-number)
(not forward?)))
(rf/dispatch [set-page-kw
(if forward?
(inc page-number)
(dec page-number))])))
draw-rect (fn [direction]
(let [forward? (= direction :forward)]
[:div.rectangle-rounded
{:on-click (on-direction-click forward?)}
[:img.icon-forward-gray
(cond-> {:src "icon-forward-gray.svg"}
forward? (assoc :class "flip-horizontal"))]]))]
(cond (<= total-count items-per-page)
[draw-items]
:else
[:div
[draw-items]
[:div.page-nav-container
[draw-rect :backward]
[draw-rect :forward]
[:div.page-nav-text [:span (str "Page " page-number " of " page-count)]]
[draw-page-numbers page-number page-count set-page-kw]]])))

View File

@ -9,6 +9,8 @@
:activity-feed-loading? false :activity-feed-loading? false
:open-bounties-loading? false :open-bounties-loading? false
:open-bounties [] :open-bounties []
:bounty-page-number 1
:activity-page-number 1
::open-bounties-sorting-type ::ui-model/bounty-sorting-type|most-recent ::open-bounties-sorting-type ::ui-model/bounty-sorting-type|most-recent
::open-bounties-filters {::ui-model/bounty-filter-type|value nil ::open-bounties-filters {::ui-model/bounty-filter-type|value nil
::ui-model/bounty-filter-type|currency nil ::ui-model/bounty-filter-type|currency nil

View File

@ -68,6 +68,16 @@
(fn [db [_ page]] (fn [db [_ page]]
(assoc db :page page))) (assoc db :page page)))
(reg-event-db
:set-bounty-page-number
(fn [db [_ page]]
(assoc db :bounty-page-number page)))
(reg-event-db
:set-activity-page-number
(fn [db [_ page]]
(assoc db :activity-page-number page)))
(reg-event-fx (reg-event-fx
:set-flash-message :set-flash-message
(fn [{:keys [db]} [_ type text]] (fn [{:keys [db]} [_ type text]]

View File

@ -1,7 +1,8 @@
(ns commiteth.subscriptions (ns commiteth.subscriptions
(:require [re-frame.core :refer [reg-sub]] (:require [re-frame.core :refer [reg-sub]]
[commiteth.db :as db] [commiteth.db :as db]
[commiteth.ui-model :as ui-model])) [commiteth.ui-model :as ui-model]
[commiteth.common :refer [items-per-page]]))
(reg-sub (reg-sub
:db :db
@ -35,7 +36,28 @@
(reg-sub (reg-sub
:open-bounties :open-bounties
(fn [db _] (fn [db _]
(:open-bounties db))) (vec (:open-bounties db))))
(reg-sub
:bounty-page-number
(fn [db _]
(:bounty-page-number db)))
(reg-sub
:open-bounties-page
:<- [::filtered-and-sorted-open-bounties]
:<- [:bounty-page-number]
(fn [[open-bounties page-number] _]
(let [total-count (count open-bounties)
start (* (dec page-number) items-per-page)
end (min total-count (+ items-per-page start))
items (subvec open-bounties start end)]
{:items items
:item-count (count items)
:total-count total-count
:page-number page-number
:page-count (Math/ceil (/ total-count items-per-page))})))
(reg-sub (reg-sub
:owner-bounties :owner-bounties
@ -55,7 +77,28 @@
(reg-sub (reg-sub
:activity-feed :activity-feed
(fn [db _] (fn [db _]
(:activity-feed db))) (vec (:activity-feed db))))
(reg-sub
:activity-page-number
(fn [db _]
(:activity-page-number db)))
(reg-sub
:activities-page
:<- [:activity-feed]
:<- [:activity-page-number]
(fn [[activities page-number] _]
(let [total-count (count activities)
start (* (dec page-number) items-per-page)
end (min total-count (+ items-per-page start))
items (subvec activities start end)]
{:items items
:item-count (count items)
:total-count total-count
:page-number page-number
:page-count (Math/ceil (/ total-count items-per-page))})))
(reg-sub (reg-sub
:gh-admin-token :gh-admin-token
@ -120,4 +163,5 @@
(fn [[open-bounties filters sorting-type] _] (fn [[open-bounties filters sorting-type] _]
(cond->> open-bounties (cond->> open-bounties
filters (ui-model/filter-bounties filters) filters (ui-model/filter-bounties filters)
sorting-type (ui-model/sort-bounties-by-sorting-type sorting-type)))) sorting-type (ui-model/sort-bounties-by-sorting-type sorting-type)
filter vec)))

View File

@ -1166,3 +1166,85 @@ body {
text-rendering: optimizeLegibility; text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
} }
.rectangle-rounded {
width: 45px;
height: 45px;
color: #57a7ed;
/*opacity: 0.2;*/
border-radius: 22.5px;
/*background-color: #57a7ed;*/
background-color: rgba(87,167,237,.2);
font-family: "PostGrotesk-Medium";
font-weight: 500;
font-size: 13px;
display: flex;
margin: 0 6px;
flex: none;
align-items: center;
justify-content: center;
}
.grayed-out {
color: #8d99a4;
background-color: #f2f5f8;
opacity: 1.0;
}
.flip-horizontal {
-moz-transform: scaleX(-1);
-webkit-transform: scaleX(-1);
-o-transform: scaleX(-1);
transform: scaleX(-1);
-ms-filter: fliph; /*IE*/
filter: fliph;
}
.pagination-text {
width: 83px;
height: 15px;
font-family: PostGrotesk;
font-size: 15px;
text-align: center;
color: #8d99a4;
}
.icon-forward-gray {
width: 24px;
height: 24px;
object-fit: contain;
}
.page-nav-container {
display: flex;
margin: 0 -6px;
}
.page-nums-container {
display: flex;
margin-left: auto;
justify-content: space-between;
}
.page-nav-text {
font-family: PostGrotesk-Book;
font-size: 15px;
color: #8d99a4;
display: flex;
margin: 0 6px;
flex: none;
align-items: center;
justify-content: center;
}
.item-counts-label {
margin: auto;
font-family: "PostGrotesk-Book";
font-size: 15px;
color: #8d99a4;
padding-top: 8px;
padding-bottom: 8px;
}

View File

@ -1,5 +1,6 @@
from pages.base_page import BasePageObject from pages.base_page import BasePageObject
from pages.base_element import * from pages.base_element import *
from tests import test_data
class BountiesHeader(BaseText): class BountiesHeader(BaseText):
@ -15,11 +16,38 @@ class TopHuntersHeader(BaseText):
super(TopHuntersHeader, self).__init__(driver) super(TopHuntersHeader, self).__init__(driver)
self.locator = self.Locator.css_selector('.top-hunters-header') self.locator = self.Locator.css_selector('.top-hunters-header')
class BountyTitles(BaseText):
def __init__(self, driver):
super(BountyTitles, self).__init__(driver)
self.locator = self.Locator.css_selector('.open-bounty-item-content .header')
class BountyItemRows(BaseText):
def __init__(self, driver):
super(BountyItemRows, self).__init__(driver)
self.locator = self.Locator.css_selector('.open-bounty-item-content .bounty-item-row')
class BountyFooters(BaseText):
def __init__(self, driver):
super(BountyFooters, self).__init__(driver)
self.locator = self.Locator.css_selector('.open-bounty-item-content .footer-row')
class BountiesPage(BasePageObject): class BountiesPage(BasePageObject):
def __init__(self, driver): def __init__(self, driver):
super(BountiesPage, self).__init__(driver) super(BountiesPage, self).__init__(driver)
self.driver = driver self.driver = driver
self.bounties_header = BountiesHeader(self.driver) self.bounties_header = BountiesHeader(self.driver)
self.top_hunters_header = TopHuntersHeader(self.driver) self.top_hunters_header = TopHuntersHeader(self.driver)
self.bounty_titles = BountyTitles(self.driver)
self.bounty_item_rows = BountyItemRows(self.driver)
self.bounty_footers = BountyFooters(self.driver)
def get_bounties_page(self):
self.driver.get(test_data.config['Common']['url'] + 'app')

View File

@ -1,5 +1,6 @@
from pages.base_page import BasePageObject from pages.base_page import BasePageObject
from pages.base_element import * from pages.base_element import *
from tests import test_data
class LoginButton(BaseButton): class LoginButton(BaseButton):
@ -20,4 +21,4 @@ class LandingPage(BasePageObject):
self.login_button = LoginButton(self.driver) self.login_button = LoginButton(self.driver)
def get_landing_page(self): def get_landing_page(self):
self.driver.get('https://openbounty.status.im:444/') self.driver.get(test_data.config['Common']['url'])

View File

@ -1,6 +1,7 @@
import time, pytest import time, pytest
from pages.base_element import * from pages.base_element import *
from pages.base_page import BasePageObject from pages.base_page import BasePageObject
from tests import test_data
class EmailEditbox(BaseEditBox): class EmailEditbox(BaseEditBox):
@ -84,7 +85,7 @@ class LabelsButton(BaseButton):
class BountyLabel(BaseButton): class BountyLabel(BaseButton):
def __init__(self, driver): def __init__(self, driver):
super(LabelsButton.BountyLabel, self).__init__(driver) super(LabelsButton.BountyLabel, self).__init__(driver)
self.locator = self.Locator.css_selector("[data-name='bounty']") self.locator = self.Locator.css_selector("[data-name='748942015']")
class CrossButton(BaseButton): class CrossButton(BaseButton):
def __init__(self, driver): def __init__(self, driver):
@ -156,11 +157,14 @@ class GithubPage(BasePageObject):
def create_new_bounty(self): def create_new_bounty(self):
self.get_issues_page() self.get_issues_page()
self.new_issue_button.click() self.new_issue_button.click()
self.issue_title_input.send_keys('auto_test_bounty_%s' % self.time_now) test_data.issue = dict()
test_data.issue['title'] = 'auto_test_bounty_%s' % self.time_now
self.issue_title_input.send_keys(test_data.issue['title'])
self.labels_button.click() self.labels_button.click()
self.bounty_label.click() self.bounty_label.click()
self.cross_button.click() self.cross_button.click()
self.submit_new_issue_button.click() self.submit_new_issue_button.click()
return test_data.issue['title']
def get_deployed_contract(self, wait=120): def get_deployed_contract(self, wait=120):
for i in range(wait): for i in range(wait):

View File

@ -1,8 +1,17 @@
import configparser
class TestData(object): class TestData(object):
def __init__(self): def __init__(self):
self.test_name = None self.test_name = None
self.config = configparser.ConfigParser()
# define here path to your config.ini file
#example - config_example.ini
self.config.read('config.ini')
self.base_case_issue = dict()
self.base_case_issue['title'] = 'Very first auto_test_bounty'
test_data = TestData() test_data = TestData()

View File

@ -39,18 +39,29 @@ class BaseTestCase:
desired_caps['captureHtml'] = False desired_caps['captureHtml'] = False
return desired_caps return desired_caps
@property
def environment(self):
return pytest.config.getoption('env')
def setup_method(self): def setup_method(self):
self.errors = [] self.errors = []
self.cleanup = None
# options = webdriver.ChromeOptions() if self.environment == 'local':
# options.add_argument('--start-fullscreen') options = webdriver.ChromeOptions()
# options.add_extension(path.abspath('/resources/metamask3_12_0.crx')) options.add_argument('--start-fullscreen')
options.add_extension(
self.driver = webdriver.Remote(self.executor_sauce_lab, path.abspath(test_data.config['Paths']['tests_absolute'] + 'resources/metamask3_12_0.crx'))
desired_capabilities=self.capabilities_sauce_lab) # for chromedriver 2.35
self.driver = webdriver.Chrome(chrome_options=options)
if self.environment == 'sauce':
self.driver = webdriver.Remote(self.executor_sauce_lab,
desired_capabilities=self.capabilities_sauce_lab)
self.driver.implicitly_wait(5) self.driver.implicitly_wait(5)
def verify_no_errors(self): def verify_no_errors(self):
if self.errors: if self.errors:
msg = '' msg = ''
@ -59,9 +70,9 @@ class BaseTestCase:
pytest.fail(msg, pytrace=False) pytest.fail(msg, pytrace=False)
def teardown_method(self): def teardown_method(self):
if self.cleanup:
remove_application(self.driver) remove_application(self.driver)
remove_installation(self.driver) remove_installation(self.driver)
try: try:
self.print_sauce_lab_info(self.driver) self.print_sauce_lab_info(self.driver)

View File

@ -0,0 +1,13 @@
[Common]
;app URL
url = https://openbounty.status.im:444/
[Paths]
;AbsolutePath to 'tests' folder
tests_absolute = /usr/dir/open-bounty/test/end-to-end/tests/
[ORG]
;GitHub credentials for organization
gh_login = login
gh_password = password
;MetaMask password for organization
mm_password = password

View File

@ -11,6 +11,10 @@ def pytest_addoption(parser):
action='store', action='store',
default=True, default=True,
help='Display each test step in terminal as plain text: True/False') help='Display each test step in terminal as plain text: True/False')
parser.addoption('--env',
action='store',
default='sauce',
help='Specify environment, sauce or local')
def pytest_configure(config): def pytest_configure(config):

View File

@ -1,22 +1,42 @@
import pytest import pytest
from os import environ from os import environ
from pages.openbounty.landing import LandingPage from pages.openbounty.landing import LandingPage
from pages.openbounty.bounties import BountiesPage
from tests.basetestcase import BaseTestCase from tests.basetestcase import BaseTestCase
from tests import test_data
@pytest.mark.sanity @pytest.mark.sanity
class TestLogin(BaseTestCase): class TestLogin(BaseTestCase):
def test_deploy_new_contract(self): def test_deploy_new_contract(self):
self.cleanup = True
landing = LandingPage(self.driver) landing = LandingPage(self.driver)
landing.get_landing_page() landing.get_landing_page()
# Sign Up to SOB
github = landing.login_button.click() github = landing.login_button.click()
github.sign_in('anna04test', github.sign_in(test_data.config['ORG']['gh_login'],
'f@E23D3H15Rd') test_data.config['ORG']['gh_password'])
assert github.permission_type.text == 'Personal user data' assert github.permission_type.text == 'Personal user data'
bounties_page = github.authorize_sob.click() bounties_page = github.authorize_sob.click()
# SOB Plugin installation and navigate to "Open bounties"
github.install_sob_plugin() github.install_sob_plugin()
assert bounties_page.bounties_header.text == 'Bounties' assert bounties_page.bounties_header.text == 'Bounties'
assert bounties_page.top_hunters_header.text == 'Top hunters' assert bounties_page.top_hunters_header.text == 'Top 5 hunters'
# Waiting for deployed contract; test_data.issue created here
github.create_new_bounty() github.create_new_bounty()
github.get_deployed_contract() github.get_deployed_contract()
# Navigate and check top bounty in "Open bounties"
bounties_page = BountiesPage(self.driver)
bounties_page.get_bounties_page()
titles = bounties_page.bounty_titles.find_elements()
assert titles[0].text == test_data.issue['title']