def test_3(self): """ User object created """ self.user = Account.objects().get(username=self.USER['email'], include="Store.Subscription,Store.Settings") return self.user is not None
def clean_email(self): email = self.cleaned_data.get('email').strip().lower() if Account.objects().count(email=email) > 0: if self.account.email != email: raise forms.ValidationError("Email is already " +\ "being used.") return email
def test_3(self): """ User object created """ self.user = Account.objects().get( username=self.USER['email'], include="Store.Subscription,Store.Settings") return self.user is not None
def delete(request, employee_id): """ This will also remove the employee from the ACL, delete the employee object and also delete the Parse.User object if and only if it has no pointer to a Store or a Patron. """ # get from the employees_approved_list in session cache employees_approved_list = SESSION.get_employees_approved_list(\ request.session) i_remove, employee = 0, None for ind, m in enumerate(employees_approved_list): if m.objectId == employee_id: employee = m i_remove = ind break if not employee: return redirect(reverse('employees_index')+ "?%s" %\ urllib.urlencode({'success': 'Employee has already been removed.'})) employees_approved_list.pop(i_remove) request.session['employees_approved_list'] =\ employees_approved_list acc = Account.objects().get(Employee=employee.objectId) if not acc: # employee may have been deleted return redirect(reverse('employees_index')+ "?%s" %\ urllib.urlencode({'success': 'Employee has already been deleted.'})) # Always save session first whenever calling a cloud code request.session.save() res = cloud_call("delete_employee", {"employee_id": employee.objectId}) request.session.clear() request.session.update(SessionStore(request.session.session_key)) if 'error' not in res: store = SESSION.get_store(request.session) payload = { COMET_RECEIVE_KEY_NAME: COMET_RECEIVE_KEY } if acc.objectId in store.ACL and not store.is_owner(acc): del store.ACL[acc.objectId] store.update() payload["updatedStore"] = store.jsonify() request.session['store'] = store # only need to pass in the objectId deleted_employee = Employee(objectId=employee.objectId) payload["deletedEmployee"] = deleted_employee.jsonify() comet_receive(store.objectId, payload) return redirect(reverse('employees_index')+ "?%s" %\ urllib.urlencode({'success': 'Employee has been deleted.'})) return redirect(reverse('employees_index')+ "?%s" %\ urllib.urlencode({'success': 'Employee has already been deleted.'}))
def delete(request, employee_id): """ This will also remove the employee from the ACL, delete the employee object and also delete the Parse.User object if and only if it has no pointer to a Store or a Patron. """ # get from the employees_approved_list in session cache employees_approved_list = SESSION.get_employees_approved_list(\ request.session) i_remove, employee = 0, None for ind, m in enumerate(employees_approved_list): if m.objectId == employee_id: employee = m i_remove = ind break if not employee: return redirect(reverse('employees_index')+ "?%s" %\ urllib.urlencode({'success': 'Employee has already been removed.'})) employees_approved_list.pop(i_remove) request.session['employees_approved_list'] =\ employees_approved_list acc = Account.objects().get(Employee=employee.objectId) if not acc: # employee may have been deleted return redirect(reverse('employees_index')+ "?%s" %\ urllib.urlencode({'success': 'Employee has already been deleted.'})) # Always save session first whenever calling a cloud code request.session.save() res = cloud_call("delete_employee", {"employee_id": employee.objectId}) request.session.clear() request.session.update(SessionStore(request.session.session_key)) if 'error' not in res: store = SESSION.get_store(request.session) payload = {COMET_RECEIVE_KEY_NAME: COMET_RECEIVE_KEY} if acc.objectId in store.ACL and not store.is_owner(acc): del store.ACL[acc.objectId] store.update() payload["updatedStore"] = store.jsonify() request.session['store'] = store # only need to pass in the objectId deleted_employee = Employee(objectId=employee.objectId) payload["deletedEmployee"] = deleted_employee.jsonify() comet_receive(store.objectId, payload) return redirect(reverse('employees_index')+ "?%s" %\ urllib.urlencode({'success': 'Employee has been deleted.'})) return redirect(reverse('employees_index')+ "?%s" %\ urllib.urlencode({'success': 'Employee has already been deleted.'}))
def deny(request, employee_id): """ same as delete except this uses the pending list """ # get from the employees_pending_list in session cache employees_pending_list = SESSION.get_employees_pending_list(\ request.session) i_remove, employee = 0, None for ind, m in enumerate(employees_pending_list): if m.objectId == employee_id: employee = m i_remove = ind break if not employee: return redirect(reverse('employees_index')+ "?%s" %\ urllib.urlencode({'success': 'Pending employee not found.'})) # update session cache for employees_pending_list employees_pending_list.pop(i_remove) request.session['employees_pending_list'] =\ employees_pending_list acc = Account.objects().get(Employee=employee.objectId) if not acc: # employee may have been deleted return redirect(reverse('employees_index')+ "?show_pending&%s" %\ urllib.urlencode({'success': 'Employee has already been denied.'})) # Always save session first whenever calling a cloud code request.session.save() res = cloud_call("delete_employee", {"employee_id": employee.objectId}) request.session.clear() request.session.update(SessionStore(request.session.session_key)) if 'error' not in res: payload = { COMET_RECEIVE_KEY_NAME: COMET_RECEIVE_KEY } store = SESSION.get_store(request.session) # no need to check acl here but just in case if acc.objectId in store.ACL and not store.is_owner(acc): del store.ACL[acc.objectId] store.update() payload["updatedStore"] = store.jsonify() request.session['store'] = store # only need to pass in the objectId deleted_employee = Employee(objectId=employee.objectId) payload["deletedEmployee"] = deleted_employee.jsonify() comet_receive(store.objectId, payload) return redirect(reverse('employees_index')+ "?show_pending&%s" %\ urllib.urlencode({'success': 'Employee has been denied.'})) return redirect(reverse('employees_index')+ "?%s" %\ urllib.urlencode({'success': 'Employee has already been deleted.'}))
def deny(request, employee_id): """ same as delete except this uses the pending list """ # get from the employees_pending_list in session cache employees_pending_list = SESSION.get_employees_pending_list(\ request.session) i_remove, employee = 0, None for ind, m in enumerate(employees_pending_list): if m.objectId == employee_id: employee = m i_remove = ind break if not employee: return redirect(reverse('employees_index')+ "?%s" %\ urllib.urlencode({'success': 'Pending employee not found.'})) # update session cache for employees_pending_list employees_pending_list.pop(i_remove) request.session['employees_pending_list'] =\ employees_pending_list acc = Account.objects().get(Employee=employee.objectId) if not acc: # employee may have been deleted return redirect(reverse('employees_index')+ "?show_pending&%s" %\ urllib.urlencode({'success': 'Employee has already been denied.'})) # Always save session first whenever calling a cloud code request.session.save() res = cloud_call("delete_employee", {"employee_id": employee.objectId}) request.session.clear() request.session.update(SessionStore(request.session.session_key)) if 'error' not in res: payload = {COMET_RECEIVE_KEY_NAME: COMET_RECEIVE_KEY} store = SESSION.get_store(request.session) # no need to check acl here but just in case if acc.objectId in store.ACL and not store.is_owner(acc): del store.ACL[acc.objectId] store.update() payload["updatedStore"] = store.jsonify() request.session['store'] = store # only need to pass in the objectId deleted_employee = Employee(objectId=employee.objectId) payload["deletedEmployee"] = deleted_employee.jsonify() comet_receive(store.objectId, payload) return redirect(reverse('employees_index')+ "?show_pending&%s" %\ urllib.urlencode({'success': 'Employee has been denied.'})) return redirect(reverse('employees_index')+ "?%s" %\ urllib.urlencode({'success': 'Employee has already been deleted.'}))
def manage_admin_controls(request): """ To turn on god_mode: ...repunch.com/manage/admin-controls?action=god_mode&flag=1& [email protected]&key=9p8437wk34z5ymurukdp9w34px7iuhsruhio """ if request.method == "GET": params = request.GET.dict() key = params.get("key") action = params.get("action") if key == ADMIN_CONTROL_KEY: if action == "god_mode": flag = params.get("flag") email = params.get("email", "") acc = Account.objects().get(email=email, Store__ne=None, include="Store.Subscription") if not acc: return HttpResponse("User with email %s not found." %\ (email,)) sub = acc.store.subscription sub.god_mode = flag != "0" sub.update() payload = { COMET_RECEIVE_KEY_NAME: COMET_RECEIVE_KEY, "updatedSubscription": sub.jsonify(), } comet_receive(acc.store.objectId, payload) # go out with the latest session in case this user is # the one that triggered this action request.session.clear() request.session.update( SessionStore(request.session.session_key)) if sub.god_mode: on_off = "on" else: on_off = "off" return HttpResponse("Successfully turned god mode "+\ "%s for user with email %s" % (on_off, email)) else: return HttpResponse("Invalid action: %s" % (action, )) else: return HttpResponse("Wrong key: %s" % (key, )) return HttpResponse("Bad Request")
def rename_punchcode_username_to_userid(): """ PunchCode username column changed to user_id. WARNING! Assumes this assumes that # Patrons < 1000. """ for acc in Account.objects().filter(Patron__ne=None, include="Patron", limit=999): pc = PunchCode.objects().get(punch_code=acc.patron.punch_code) pc.user_id = acc.objectId pc.update() print "Updated PunchCode " + acc.patron.punch_code
def clean_email(self): # If an Account exist already with a Store object - then bad e = self.cleaned_data.get('email') if e: e = e.strip().lower() acc = Account.objects().get(email=e) if acc and acc.Store: raise forms.ValidationError(\ "Email is already being used.") elif acc: # save the object for later use setattr(self, "associated_account", acc) return e
def manage_admin_controls(request): """ To turn on god_mode: ...repunch.com/manage/admin-controls?action=god_mode&flag=1& [email protected]&key=9p8437wk34z5ymurukdp9w34px7iuhsruhio """ if request.method == "GET": params = request.GET.dict() key = params.get("key") action = params.get("action") if key == ADMIN_CONTROL_KEY: if action == "god_mode": flag = params.get("flag") email = params.get("email", "") acc = Account.objects().get(email=email, Store__ne=None, include="Store.Subscription") if not acc: return HttpResponse("User with email %s not found." %\ (email,)) sub = acc.store.subscription sub.god_mode = flag != "0" sub.update() payload = { COMET_RECEIVE_KEY_NAME: COMET_RECEIVE_KEY, "updatedSubscription": sub.jsonify(), } comet_receive(acc.store.objectId, payload) # go out with the latest session in case this user is # the one that triggered this action request.session.clear() request.session.update(SessionStore(request.session.session_key)) if sub.god_mode: on_off = "on" else: on_off = "off" return HttpResponse("Successfully turned god mode "+\ "%s for user with email %s" % (on_off, email)) else: return HttpResponse("Invalid action: %s" % (action,)) else: return HttpResponse("Wrong key: %s" % (key,)) return HttpResponse("Bad Request")
def edit(request, employee_id): data = {'employees_nav': True, 'employee_id': employee_id} # get from the employees_approved_list in session cache employees_approved_list = SESSION.get_employees_approved_list(\ request.session) employee = None for m in employees_approved_list: if m.objectId == employee_id: employee = m break acc = Account.objects().get(Employee=employee.objectId) store = SESSION.get_store(request.session) if not employee or not acc: return redirect(reverse('employees_index')+ "?%s" %\ urllib.urlencode({'success': 'Employee does not exist.'})) if request.method == "POST": store.set_access_level(acc, request.POST["ACL"]) store.update() request.session['store'] = store # notify other dashboards of this change payload = { COMET_RECEIVE_KEY_NAME: COMET_RECEIVE_KEY, "updatedStore":store.jsonify() } comet_receive(store.objectId, payload) return redirect(reverse('employees_index')+ "?%s" %\ urllib.urlencode({'success': 'Employee has been updated.'})) form = EmployeeForm(employee.__dict__.copy()) form.data['email'] = acc.get('email') data.update({ 'ACCESS_ADMIN': ACCESS_ADMIN[0], 'ACCESS_PUNCHREDEEM': ACCESS_PUNCHREDEEM[0], 'ACCESS_NONE': ACCESS_NONE[0], 'form': form, 'employee': employee, 'employee_acl': store.get_access_level(acc)[0], }) return render(request, 'manage/employee_edit.djhtml', data)
def edit(request, employee_id): data = {'employees_nav': True, 'employee_id': employee_id} # get from the employees_approved_list in session cache employees_approved_list = SESSION.get_employees_approved_list(\ request.session) employee = None for m in employees_approved_list: if m.objectId == employee_id: employee = m break acc = Account.objects().get(Employee=employee.objectId) store = SESSION.get_store(request.session) if not employee or not acc: return redirect(reverse('employees_index')+ "?%s" %\ urllib.urlencode({'success': 'Employee does not exist.'})) if request.method == "POST": store.set_access_level(acc, request.POST["ACL"]) store.update() request.session['store'] = store # notify other dashboards of this change payload = { COMET_RECEIVE_KEY_NAME: COMET_RECEIVE_KEY, "updatedStore": store.jsonify() } comet_receive(store.objectId, payload) return redirect(reverse('employees_index')+ "?%s" %\ urllib.urlencode({'success': 'Employee has been updated.'})) form = EmployeeForm(employee.__dict__.copy()) form.data['email'] = acc.get('email') data.update({ 'ACCESS_ADMIN': ACCESS_ADMIN[0], 'ACCESS_PUNCHREDEEM': ACCESS_PUNCHREDEEM[0], 'ACCESS_NONE': ACCESS_NONE[0], 'form': form, 'employee': employee, 'employee_acl': store.get_access_level(acc)[0], }) return render(request, 'manage/employee_edit.djhtml', data)
def __init__(self, fetch_user=True, user_include="Patron,Store,Employee"): """ tests has the following format: [ {'test_name': "Test title"}, ... ] """ self.name = UN_CAMEL_RE.sub(r'\1 \2', self.__class__.__name__) self._tests = [] self._results = { "section_name": self.name, "parts": self._tests, } if fetch_user: self.account = Account.objects().get(email=\ self.USER['email'], include=user_include) self.patron = self.account.patron self.store = self.account.store self.employee = self.account.employee
def test_rewards(): # setup account = Account.objects().get(username=TEST_USER['username'], include="Store") store = account.store # start with no rewards store.rewards = [] store.update() test = SeleniumTest() parts = [ {'test_name': "User needs to be logged in to access page"}, {'test_name': "Having no rewards shows a placeholder row"}, {'test_name': "Adding a reward works"}, {'test_name': "The new reward is saved to parse"}, {'test_name': "Redemption count starts at 0"}, {'test_name': "Reward id starts at 0"}, {'test_name': "Updating a reward works"}, {'test_name': "The updated reward is saved to parse"}, {'test_name': "The updated reward retains the reward_id"}, {'test_name': "The updated reward retains the " +\ "redemption_count"}, {'test_name': "Clicking delete brings up a confirmation " +\ "dialog"}, {'test_name': "Deleting a reward works"}, {'test_name': "The deleted reward is deleted from parse"}, {'test_name': "Reward name is required"}, {'test_name': "Punches is required"}, {'test_name': "Description is not required"}, {'test_name': "Clicking cancel redirects user " +\ "back to rewards index"}, {'test_name': "Punches must be a number"}, {'test_name': "Punches must be greater than 0"}, {'test_name': "Rewards are initially sorted by Punches " +\ "from least to greatest"}, {'test_name': "Punches is sortable"}, {'test_name': "Name is sortable"}, {'test_name': "Updating a reward with the reset redemption" +\ " count option resets the redemption count to 0"}, ] section = { "section_name": "Rewards page working properly?", "parts": parts, } test.results.append(section) ########## User needs to be logged in to access page test.open(reverse("rewards_index")) # ACTION! sleep(1) parts[0]['success'] = test.is_current_url(reverse(\ 'manage_login') + "?next=" + reverse("rewards_index")) # login selectors = ( ("#login_username", TEST_USER['username']), ("#login_password", TEST_USER['password']), ("", Keys.RETURN) ) test.action_chain(0, selectors, "send_keys") # ACTION! sleep(7) ########## Having no rewards shows a placeholder row try: parts[1]['success'] =\ test.find("//div[@id='rewards_section']/div[@class=" +\ "'tr reward']/div[@class='td reward_summary']/span[1]", type="xpath").text == "No Rewards" except Exception as e: print e parts[1]['test_message'] = str(e) def add_reward(name, description, punches): test.find("#add_reward").click() sleep(1) selectors = ( ("#id_reward_name", name), ("#id_description", description), ("#id_punches", str(punches)), ) test.action_chain(0, selectors, action="send_keys") test.find("#submit-reward-form").click() sleep(5) reward1_name = "Reward #1" reward1_description = "First reward" reward1_punches = 5 ########## Adding a reward works try: add_reward(reward1_name, reward1_description, reward1_punches) parts[2]['success'] = test.find("//div[@id='0']/a/div[2]" +\ "/span[1]", type="xpath").text == reward1_name except Exception as e: print e parts[2]['test_message'] = str(e) ########## The new reward is saved to parse try: store.rewards = None reward = store.get("rewards")[0] parts[3]['success'] = reward['reward_name'] ==\ reward1_name and reward['description'] ==\ reward1_description and\ reward['punches'] == reward1_punches except Exception as e: print e parts[3]['test_message'] = str(e) ########## Redemption count starts at 0 try: parts[4]['success'] = reward['redemption_count'] == 0 except Exception as e: print e parts[4]['test_message'] = str(e) ########## Redemption count starts at 0 try: parts[5]['success'] = reward['reward_id'] == 0 except Exception as e: print e parts[5]['test_message'] = str(e) # let's add 3 more rewards! reward2_name = "reward dos" reward2_description = "DOS" reward2_punches = 10 reward3_name = "reward tres" reward3_description = "TRES" reward3_punches = 12 reward4_name = "reward quatro" reward4_description = "QUATRO" reward4_punches = 15 add_reward(reward2_name, reward2_description, reward2_punches) add_reward(reward3_name, reward3_description, reward3_punches) add_reward(reward4_name, reward4_description, reward4_punches) ################ reward1_name = "reward uno" reward1_description = "UNO" reward1_punches = 1 ########## Updating a reward works try: test.find("//div[@id='0']/a", type="xpath").click() sleep(1) selectors = ( ("#id_reward_name", reward1_name), ("#id_description", reward1_description), ("#id_punches", str(reward1_punches)), ) test.action_chain(0, selectors, action="clear") test.action_chain(0, selectors, action="send_keys") test.find("#submit-reward-form").click() sleep(4) parts[6]['success'] = test.find("//div[@id='0']/a/div[2]" +\ "/span[1]", type="xpath").text == reward1_name except Exception as e: print e parts[6]['test_message'] = str(e) ########## The updated reward is saved to parse try: store.rewards = None reward = store.get("rewards")[0] # list is sorted by punches parts[7]['success'] = reward['reward_name'] ==\ reward1_name and\ reward['description'] == reward1_description and\ reward['punches'] == reward1_punches except Exception as e: print e parts[7]['test_message'] = str(e) ########## The updated reward retains the reward_id try: parts[8]['success'] = reward['reward_id'] == 0 except Exception as e: print e parts[8]['test_message'] = str(e) ########## The updated reward retains the redemption_count try: parts[9]['success'] = reward['redemption_count'] == 0 except Exception as e: print e parts[9]['test_message'] = str(e) ########## Clicking delete brings up a confirmation dialog try: test.find("//div[@id='0']/a", type="xpath").click() sleep(1) test.find("#delete-link").click() alert = test.switch_to_alert() parts[10]['success'] = alert is not None except Exception as e: print e parts[10]['test_message'] = str(e) ########## Deleting a reward works try: alert.accept() sleep(5) parts[11]['success'] = test.find("//div[@id='1']/a/div[2]" +\ "/span[1]", type="xpath").text == reward2_name try: # first reward should be gone test.find("//div[@id='0']/a/div[2]" +\ "/span[1]", type="xpath").text except Exception: parts[11]['success'] = parts[11]['success'] else: parts[11]['success'] = False except Exception as e: print e parts[11]['test_message'] = str(e) ########## The deleted reward is deleted from parse try: store.rewards = None rewards = store.get("rewards") parts[12]['success'] = reward1_name not in\ [r['reward_name'] for r in rewards] and\ 0 not in [r['reward_id'] for r in rewards] except Exception as e: print e parts[12]['test_message'] = str(e) # field required add_reward(" ", "", "") ########## Reward name is required try: parts[13]['success'] =\ test.find("#reward_name_ic ul li").text ==\ "This field is required." except Exception as e: print e parts[13]['test_message'] = str(e) ########## Punches is required try: parts[14]['success'] =\ test.find("#punches_ic ul li").text ==\ "This field is required." except Exception as e: print e parts[14]['test_message'] = str(e) ########## Description is not required try: parts[15]['success'] =\ not test.element_exists("#description_ic ul li") except Exception as e: print e parts[15]['test_message'] = str(e) ########## Clicking cancel redirects user back to rewards index try: test.find("//div [@id='edit-reward-options']/a[2]", type="xpath").click() sleep(2) parts[16]['success'] =\ test.is_current_url(reverse("rewards_index")) except Exception as e: print e parts[16]['test_message'] = str(e) ########## Punches must be a number try: add_reward("", "", "ba") parts[17]['success'] =\ test.find("#punches_ic ul li").text ==\ "Enter a whole number." test.find("//div [@id='edit-reward-options']/a[2]", type="xpath").click() sleep(2) except Exception as e: print e parts[17]['test_message'] = str(e) ########## Punches must be greater than 0 try: add_reward("", "", "0") parts[18]['success'] =\ test.find("#punches_ic ul li").text ==\ "Ensure this value is greater than or equal to 1." test.find("//div [@id='edit-reward-options']/a[2]", type="xpath").click() sleep(2) except Exception as e: print e parts[18]['test_message'] = str(e) ########## Rewards are initially sorted by Punchess ### from least to greatest try: store.rewards = None rewards = store.get('rewards') punches_map = {r['punches']:r for r in rewards} ascending = [r['punches'] for r in rewards] descending = ascending[:] ascending.sort() descending.sort(reverse=True) success = True for i in range(3): if int(test.find("//div[@id='%s']/a/div[1]" %(str(i+1),), type="xpath").text) != ascending[i]: success = False break parts[19]['success'] = success except Exception as e: print e parts[19]['test_message'] = str(e) ########## Punches is sortable try: test.find("#header-reward_punches").click() rows = test.find("//div[@id='rewards_section']/" +\ "div[contains(@class, 'reward')]", type="xpath", multiple=True) if len(rows) == len(descending): success = True for i in range(len(rows)): # check both the punches and id row = rows[i] if int(row.text.split("\n")[0]) !=\ descending[i] or int(row.get_attribute("id")) !=\ punches_map[descending[i]]['reward_id']: success = False break parts[20]['success'] = success except Exception as e: print e parts[20]['test_message'] = str(e) ########## Name is sortable try: name_map = {r['reward_name']:r for r in rewards} ascending = [r['reward_name'] for r in rewards] descending = ascending[:] ascending.sort() descending.sort(reverse=True) # ascending order test.find("#header-reward_summary").click() rows = test.find("//div[@id='rewards_section']/" +\ "div[contains(@class, 'reward')]", type="xpath", multiple=True) if len(rows) == len(ascending): success1 = True for i in range(len(rows)): # check both the name and id row = rows[i] if row.text.split("\n")[1] !=\ ascending[i] or int(row.get_attribute("id")) !=\ name_map[ascending[i]]['reward_id']: success1 = False break # descending order test.find("#header-reward_summary").click() rows = test.find("//div[@id='rewards_section']/" +\ "div[contains(@class, 'reward')]", type="xpath", multiple=True) if len(rows) == len(descending): success2 = True for i in range(len(rows)): # check both the name and id row = rows[i] if row.text.split("\n")[1] !=\ descending[i] or int(row.get_attribute("id")) !=\ name_map[descending[i]]['reward_id']: success2 = False break parts[21]['success'] = success1 and success2 except Exception as e: print e parts[21]['test_message'] = str(e) ########## Updating a reward with the reset redemption ### count option resets the redemption count to 0 try: # logout test.logout() test.dev_login() sleep(1) # modify store the rewards redemption count store.rewards = None for r in store.get("rewards"): r['redemption_count'] = 99 store.update() # login test.open(reverse("rewards_index")) # ACTION! sleep(1) selectors = ( ("#login_username", TEST_USER['username']), ("#login_password", TEST_USER['password']), ("", Keys.RETURN) ) test.action_chain(0, selectors, "send_keys") # ACTION! sleep(7) # now test test.find("//div[@id='2']/a", type="xpath").click() sleep(1) red_count = int(test.find("#redemption_count").text.split(" ")[1]) test.find("#reset_red_count").click() test.find("#submit-reward-form").click() sleep(5) test.find("//div[@id='2']/a", type="xpath").click() new_red_count =\ int(test.find("#redemption_count").text.split(" ")[1]) parts[22]['success'] = new_red_count == 0 and red_count !=\ new_red_count except Exception as e: print e parts[22]['test_message'] = str(e) # END OF ALL TESTS - cleanup return test.tear_down()
def test_messages(): # TODO test that patrons are getting the messages!!! # setup account = Account.objects().get(username=TEST_USER['username'], include="Store.Subscription") store = account.store subscription = store.subscription # set subscriptionType to free subscription.subscriptionType = 0 subscription.update() # clear the sent messages relation sent_messages = store.get("sentMessages", keys="") if sent_messages: store.remove_relation("SentMessages_", [m.objectId for m in sent_messages]) store.set("sentMessages", None) # we can clear the list locally but just re-pull from parse account = Account.objects().get(username=TEST_USER['username'], include="Store.Subscription") store = account.store subscription = store.subscription test = SeleniumTest() parts = [ {'test_name': "User needs to be logged in to access page"}, # FIRST {'test_name': "Send message. Filter all. No offer"}, {'test_name': "Message is in store's sentMessages relation"}, {'test_name': "Message is visible in page"}, {'test_name': "Message can be view by clicking on row"}, # SECOND {'test_name': "Send message. Filter all. With offer"}, {'test_name': "Message is in store's sentMessages relation"}, {'test_name': "Message is visible in page"}, {'test_name': "Message can be view by clicking on row"}, # THIRD {'test_name': "Send message. Filter idle. No offer. " +\ "Message limit passed (free) dialog appears"}, # LIMIT PASSED {'test_name': "Upgrading account from the dialog sends the " +\ "message and upgrades the account to middle"}, {'test_name': "Email is sent notifying user the upgrade"}, # {'test_name': "Message is in store's sentMessages relation"}, {'test_name': "Message is visible in page"}, {'test_name': "Message can be view by clicking on row"}, # FOURTH {'test_name': "Send message. Filter idle. With offer"}, {'test_name': "Message is in store's sentMessages relation"}, {'test_name': "Message is visible in page"}, {'test_name': "Message can be view by clicking on row"}, # FIFTH {'test_name': "Send message. Filter most_loyal. No offer. " +\ "Message limit passed (middle) dialog appears"}, # LIMIT PASSED {'test_name': "Upgrading account from the dialog sends the" +\ " message and upgrades the account to heavy"}, {'test_name': "Email is sent notifying user the upgrade"}, # {'test_name': "Message is in store's sentMessages relation"}, {'test_name': "Message is visible in page"}, {'test_name': "Message can be view by clicking on row"}, # SIXTH {'test_name': "Send message. Filter most_loyal. With offer"}, {'test_name': "Message is in store's sentMessages relation"}, {'test_name': "Message is visible in page"}, {'test_name': "Message can be view by clicking on row"}, # SEVENTH {'test_name': "Send message. Filter all. With offer"}, {'test_name': "Message is in store's sentMessages relation"}, {'test_name': "Message is visible in page"}, {'test_name': "Message can be view by clicking on row"}, # EIGHTH {'test_name': "Send message. Filter all. With offer"}, {'test_name': "Message is in store's sentMessages relation"}, {'test_name': "Message is visible in page"}, {'test_name': "Message can be view by clicking on row"}, # NINTH {'test_name': "Send message. Filter all. No offer. " +\ "Message limit passed (heavy) dialog appears"}, # LIMIT PASSED {'test_name': "Account can no longer be upgraded." +\ "Message cannot be sent. Clicking okay redirects "+\ "user to messages index."}, # {'test_name': "Subject is required"}, {'test_name': "Body is required"}, {'test_name': "Offer title not required if attach offer off"}, {'test_name': "Expiration not required if attach offer off"}, {'test_name': "Offer title is required if attach offer on"}, {'test_name': "Expiration date required if attach offer on"}, {'test_name': "Expiration date must be at a later date"}, {'test_name': "Expiration date must be at most 1 year later"}, ] section = { "section_name": "Sending messages works?", "parts": parts, } test.results.append(section) ########## User needs to be logged in to access page test.open(reverse("messages_index")) # ACTION! sleep(1) parts[0]['success'] = test.is_current_url(reverse(\ 'manage_login') + "?next=" + reverse("messages_index")) # login selectors = ( ("#login_username", TEST_USER['username']), ("#login_password", TEST_USER['password']), ("", Keys.RETURN) ) test.action_chain(0, selectors, "send_keys") # ACTION! sleep(5) def send_message(filter, subject, body, attach_offer=False, offer_title=None, exp_date=None,): """ Must be called at messages index page """ test.find("#create_message").click() sleep(1) # set the filter test.find("//select[@id='filter']/option[@value='%s']" %\ (filter,), type="xpath").click() # subject test.find("#id_subject").send_keys(subject) # body test.find("#id_body").send_keys(body) # attach_offer if attach_offer: test.find("#id_attach_offer").click() # offer title if offer_title: test.find("#id_offer_title").send_keys(offer_title) # exp_date if exp_date: test.find("#id_date_offer_expiration").send_keys(exp_date) # submit test.find("#send-now").click() sleep(5) def message_in_relation(message_id, test_number): if not message_id: return store.sentMessages = None parts[test_number]['success'] = store.get("sentMessages", objectId=message_id, count=1, limit=0) == 1 def message_in_page(message_id, test_number): if not message_id: return try: rows = test.find("#tab-body-sent div.tr a", multiple=True) for row in rows: if row.get_attribute("href").split("/")[5] ==\ message_id: parts[test_number]['success'] = True except Exception as e: print e parts[test_number]['test_message'] = str(e) def message_viewable(message_id, test_number): if not message_id: return href = reverse("message_details", args=(message_id,)) try: test.find("#tab-body-sent div.tr a[href='%s']" %\ (href,)).click() sleep(2) parts[test_number]['success'] = test.is_current_url(href) except Exception as e: print e parts[test_number]['test_message'] = str(e) finally: # must go back to messages index for the other tests test.open(reverse("messages_index")) # FIRST ########## Send message. Filter all. No offer. message_id = None try: send_message("all", "msg #1", "body #1") parts[1]['success'] = len(test.find(\ "div.notification.success", multiple=True)) > 0 message_id = test.driver.current_url.split("/")[5] except Exception as e: print e parts[1]['test_message'] = str(e) finally: # must go back to messages index test.open(reverse("messages_index")) ########## Message is in store's sentMessages relation. message_in_relation(message_id, 2) ########## Message is visible in page. message_in_page(message_id, 3) ########## Message can be view by clicking on row. message_viewable(message_id, 4) # SECOND ########## Send message. Filter all. With offer. message_id = None try: exp_date = timezone.now() + relativedelta(days=1) send_message("all", "msg #2", "body #2", True, "offer#2", exp_date.strftime(DATE_PICKER_STRFTIME)) parts[5]['success'] = len(test.find(\ "div.notification.success", multiple=True)) > 0 message_id = test.driver.current_url.split("/")[5] except Exception as e: print e parts[5]['test_message'] = str(e) finally: # must go back to messages index test.open(reverse("messages_index")) ########## Message is in store's sentMessages relation. message_in_relation(message_id, 6) ########## Message is visible in page. message_in_page(message_id, 7) ########## Message can be view by clicking on row. message_viewable(message_id, 8) # THIRD ########## Send message. Filter idle. No offer. ### Message limit passed (free) dialog appears. message_id = None try: send_message("idle", "msg #3", "body #3") parts[9]['success'] = test.find("#upgrade") is not None except Exception as e: print e parts[9]['test_message'] = str(e) test.open(reverse("messages_index")) # LIMIT PASSED ########## Upgrading account from the dialog sends the ### message and upgrades the account to middle. try: test.find("#upgrade").click() sleep(2) test.find("#id_cc_cvv").send_keys("123") test.find("#id_recurring").click() test.find("#update-form-submit").click() sleep(5) message_id = test.driver.current_url.split("/")[5] subscription.subscriptionType = None parts[10]['success'] = test.is_current_url(\ reverse("message_details", args=(message_id,))) and\ subscription.get("subscriptionType") == 1 except Exception as e: print e parts[10]['test_message'] = str(e) finally: # must go back to messages index test.open(reverse("messages_index")) # open the mail connection if SeleniumTest.CHECK_SENT_MAIL: mail = Mail() ########## Email is sent notifying user the upgrade. try: parts[11]['success'] = mail.is_mail_sent(\ EMAIL_UPGRADE_SUBJECT) except Exception as e: print e parts[11]['test_message'] = str(e) else: parts[11]['success'] = parts[10]['success'] ########## Message is in store's sentMessages relation. message_in_relation(message_id, 12) ########## Message is visible in page. message_in_page(message_id, 13) ########## Message can be view by clicking on row. message_viewable(message_id, 14) # FOURTH ########## Send message. Filter idle. With offer. message_id = None try: exp_date = timezone.now() + relativedelta(days=1) send_message("idle", "msg #4", "body #4", True, "offer#4", exp_date.strftime(DATE_PICKER_STRFTIME)) parts[15]['success'] = len(test.find(\ "div.notification.success", multiple=True)) > 0 message_id = test.driver.current_url.split("/")[5] except Exception as e: print e parts[15]['test_message'] = str(e) finally: # must go back to messages index test.open(reverse("messages_index")) ########## Message is in store's sentMessages relation. message_in_relation(message_id, 16) ########## Message is visible in page. message_in_page(message_id, 17) ########## Message can be view by clicking on row. message_viewable(message_id, 18) # FIFTH ########## Send message. Filter most_loyal. No offer. ### Message limit passed (free) dialog appears. message_id = None try: send_message("most_loyal", "msg #5", "body #5") parts[19]['success'] = test.find("#upgrade") is not None except Exception as e: print e parts[19]['test_message'] = str(e) test.open(reverse("messages_index")) # LIMIT PASSED ########## Upgrading account from the dialog sends the ### message and upgrades the account to heavy. try: test.find("#upgrade").click() sleep(2) test.find("#id_cc_cvv").send_keys("123") test.find("#id_recurring").click() test.find("#update-form-submit").click() sleep(5) message_id = test.driver.current_url.split("/")[5] subscription.subscriptionType = None parts[20]['success'] = test.is_current_url(\ reverse("message_details", args=(message_id,))) and\ subscription.get("subscriptionType") == 2 except Exception as e: print e parts[20]['test_message'] = str(e) finally: # must go back to messages index test.open(reverse("messages_index")) ########## Email is sent notifying user the upgrade. if SeleniumTest.CHECK_SENT_MAIL: try: parts[21]['success'] = mail.is_mail_sent(\ EMAIL_UPGRADE_SUBJECT) except Exception as e: print e parts[21]['test_message'] = str(e) else: parts[21]['success'] = parts[20]['success'] ########## Message is in store's sentMessages relation. message_in_relation(message_id, 22) ########## Message is visible in page. message_in_page(message_id, 23) ########## Message can be view by clicking on row. message_viewable(message_id, 24) # SIXTH ########## Send message. Filter most_loyal. With offer. message_id = None try: exp_date = timezone.now() + relativedelta(days=1) send_message("most_loyal", "msg #6", "body #6", True, "offer#6", exp_date.strftime(DATE_PICKER_STRFTIME)) parts[25]['success'] = len(test.find(\ "div.notification.success", multiple=True)) > 0 message_id = test.driver.current_url.split("/")[5] except Exception as e: print e parts[25]['test_message'] = str(e) finally: # must go back to messages index test.open(reverse("messages_index")) ########## Message is in store's sentMessages relation. message_in_relation(message_id, 26) ########## Message is visible in page. message_in_page(message_id, 27) ########## Message can be view by clicking on row. message_viewable(message_id, 28) # SEVENTH ########## Send message. Filter all. With offer. message_id = None try: exp_date = timezone.now() + relativedelta(days=1) send_message("all", "msg #7", "body #7", True, "offer#7", exp_date.strftime(DATE_PICKER_STRFTIME)) parts[29]['success'] = len(test.find(\ "div.notification.success", multiple=True)) > 0 message_id = test.driver.current_url.split("/")[5] except Exception as e: print e parts[29]['test_message'] = str(e) finally: # must go back to messages index test.open(reverse("messages_index")) ########## Message is in store's sentMessages relation. message_in_relation(message_id, 30) ########## Message is visible in page. message_in_page(message_id, 31) ########## Message can be view by clicking on row. message_viewable(message_id, 32) # EIGHTH ########## Send message. Filter all. With offer. message_id = None try: exp_date = timezone.now() + relativedelta(days=1) send_message("all", "msg #8", "body #8", True, "offer#8", exp_date.strftime(DATE_PICKER_STRFTIME)) parts[33]['success'] = len(test.find(\ "div.notification.success", multiple=True)) > 0 message_id = test.driver.current_url.split("/")[5] except Exception as e: print e parts[33]['test_message'] = str(e) finally: # must go back to messages index test.open(reverse("messages_index")) ########## Message is in store's sentMessages relation. message_in_relation(message_id, 34) ########## Message is visible in page. message_in_page(message_id, 35) ########## Message can be view by clicking on row. message_viewable(message_id, 36) # NINTH ########## Send message. Filter all. With offer. ### Message limit passed (heavy) dialog appears. message_id = None try: send_message("all", "msg #9", "body #9") parts[37]['success'] = test.element_exists("#maxed_out") except Exception as e: print e parts[37]['test_message'] = str(e) test.open(reverse("messages_index")) # LIMIT PASSED ########## Account can no longer be upgraded. Msg cannot be sent. ### Clicking Okay redirects user to messages index. try: test.find("#maxed_out").click() sleep(1) parts[38]['success'] =\ test.is_current_url(reverse("messages_index")) except Exception as e: print e parts[38]['test_message'] = str(e) test.open(reverse("messages_index")) # # goto edit message page test.find("#create_message").click() sleep(2) selectors = ( ("#id_subject", " "), ("#id_body", " "), ) test.action_chain(0, selectors, action="send_keys") test.find("#send-now").click() sleep(1) ########## Subject is required. try: parts[39]['success'] = test.find("#subject_e ul li").text ==\ "This field is required." except Exception as e: print e parts[39]['test_message']= str(e) ########## Body is required. try: parts[40]['success'] = test.find("#body_e ul li").text ==\ "This field is required." except Exception as e: print e parts[40]['test_message'] = str(e) ########## Offer title not required if attach offer off. try: parts[41]['success'] = not test.element_exists(\ "#offer_title_e ul li") except Exception as e: print e parts[41]['test_message'] = str(e) ########## Expiration not required if attach offer off. try: parts[42]['success'] = not test.element_exists(\ "#date_offer_expiration_e ul li") except Exception as e: print e parts[42]['test_message'] = str(e) test.find("#id_attach_offer").click() test.find("#send-now").click() sleep(1) ########## Offer title is required if attach offer on. try: parts[43]['success'] =\ test.find("#offer_title_e ul li").text ==\ "Please enter a title." except Exception as e: print e parts[43]['test_message'] = str(e) ########## Expiration date required if attach offer on. try: parts[44]['success'] =\ test.find("#date_offer_expiration_e ul li").text ==\ "Please enter an expiration date." except Exception as e: print e parts[44]['test_message'] = str(e) ########## Expiration date must be at a later date. try: # don't click attach offer again! # test.find("#id_attach_offer").click() exp_date = timezone.now() + relativedelta(days=-1) test.find("#id_date_offer_expiration").send_keys(\ exp_date.strftime(DATE_PICKER_STRFTIME)) test.find("#send-now").click() sleep(1) parts[45]['success'] = test.find(\ "#date_offer_expiration_e ul li").text ==\ "Please enter an expiration date that is later than today." except Exception as e: print e parts[45]['test_message'] = str(e) ########## Expiration date must be at most 1 year later. try: # don't click attach offer again! # test.find("#id_attach_offer").click() exp_date = timezone.now() + relativedelta(days=367) date_offer = test.find("#id_date_offer_expiration") date_offer.clear() date_offer.send_keys(\ exp_date.strftime(DATE_PICKER_STRFTIME)) test.find("#send-now").click() sleep(2) parts[46]['success'] = test.find(\ "#date_offer_expiration_e ul li").text ==\ "Please enter an expiration date that is less than a year." except Exception as e: print e parts[46]['test_message'] = str(e) # END OF ALL TESTS - cleanup if SeleniumTest.CHECK_SENT_MAIL: mail.logout() return test.tear_down()
def create_random_stores(self, amount): for i in range(amount): print "Creating store %s" % (str(i), ) # create the store street, city, state, zip, country, phone_number =\ self.addrs[i].split(", ") first_name, last_name = self.owners[i].split(" ") neighborhood = self.neighborhoods[i] store_name = self.stores[i] store_i = STORE.copy() store_location_i = STORE_LOCATION.copy() # create the thumbnaiil and cover (same image different size) self.get_store_location_image(i) thumbnail = create_png(TMP_IMG_PATH, IMAGE_THUMBNAIL_SIZE) while "error" in image: print "Retrying create_png" thumbnail = create_png(TMP_IMG_PATH) cover = create_png(TMP_IMG_PATH) while "error" in cover: print "Retrying create_png" cover = create_png(TMP_IMG_PATH) store_i.update({ "store_name": store_name, "first_name": first_name, "last_name": last_name, "thumbnail_image": thumbnail.get("name"), "cover_image": cover.get("name"), }) store_location_i.update({ "street": street, "city": city, "state": state, "zip": zip, "neighborhood": neighborhood, "country": country, "phone_number": phone_number, "coordinates": self.get_random_coordinates(), }) # create the store store = Store.objects().create(**store_i) # create the store location store_location = StoreLocation(**store_location_i) store_location.Store = store.objectId store_location.update() # create the settings settings = Settings.objects().create(Store=store.objectId) # create the subscription subscription =\ Subscription.objects().create(Store=store.objectId, date_last_billed=timezone.now()) # create the user email = first_name + str(randint(0, 99)) + USER_EMAIL_POSTFIX email = email.lower() acc = Account.objects().create(\ username=email, email=email, password=USER_PASSWORD, Store=store.objectId) if not acc.objectId: raise Exception("Account creation failed.") # link the store store.Settings = settings.objectId store.Subscription = subscription.objectId store.owner_id = acc.objectId store.ACL[acc.objectId] = {"read": True, "write": True} store.store_locations = [store_location] store.update()
def handle(self, *args, **options): """ get all of the subscriptions whose stores are active & who have been last billed 30+ days ago. IMPORTANT! This includes accounts of type FREE, which are not charged or included in the email notifications """ # for logging when ran by CRON print "Running monthly_billing: " + str(timezone.now()) date_30_ago = timezone.now() + relativedelta(days=-30) date_now = timezone.now() sub_count = pointer_query("Subscription", {"date_last_billed__lte":date_30_ago}, "Store", "Store", {"active":True}, count=True) asiss = [] # get 500 subscriptions at a time LIMIT, skip = 500, 0 while sub_count > 0: for each in pointer_query("Subscription", {"date_last_billed__lte":date_30_ago}, "Store", "Store", {"active":True}, limit=LIMIT, skip=skip, order="createdAt")['results']: subscription = Subscription(**each) sub_cost = sub_type[subscription.get(\ "subscriptionType")]["monthly_cost"] store = None # prevent UnboundLocalError update_store = False if sub_cost == 0: # FREE account subscription.date_last_billed =\ subscription.date_last_billed +\ relativedelta(days=30) subscription.update() else: # PAID account account = Account.objects().get(Store=\ subscription.get("Store"), include="Store") store = account.get("store") invoice = subscription.charge_cc(\ sub_cost, "Repunch Inc. Recurring " +\ "monthly subscription charge", MONTHLY) send_user_email = True if invoice: subscription.date_last_billed =\ subscription.date_last_billed +\ relativedelta(days=30) subscription.date_charge_failed = None subscription.update() else: subscription.date_charge_failed = date_now # force entering new credit card! subscription.cc_number = None subscription.date_cc_expiration = None subscription.update() # notify user via email- payment is done via # dashboard to also validate cc realtime # 1st day time range day0_end = date_30_ago.replace() day0_start = day0_end + relativedelta(hours=-24) # 4th day time range day4_end = date_30_ago + relativedelta(days=-4) day4_start = day4_end + relativedelta(hours=-24) # 8th day time range day8_end = date_30_ago + relativedelta(days=-8) day8_start = day8_end + relativedelta(hours=-24) # 14th day time range day14_end = date_30_ago + relativedelta(days=-14) day14_start = day14_end + relativedelta(hours=-24) # only send email after 1, 4, and 8 days last_billed = subscription.date_last_billed if (last_billed >= day0_start and\ last_billed <= day0_end) or\ (last_billed >= day4_start and\ last_billed <= day4_end) or\ (last_billed >= day8_start and\ last_billed <= day8_end): send_email_receipt_monthly_failed(account, store, subscription) elif last_billed >= day14_start and\ last_billed <= day14_end: # send final notification send_email_receipt_monthly_failed(account, store, subscription, account_disabled=True) else: send_user_email = False # make sure that the store is deactivated after the 14th day if last_billed <= day14_end: store.active = False store.update() update_store = True # do not send email after account deactivation if send_user_email: asiss.append((account, store, invoice, subscription)) # update the logged in users' subscription and store payload = { COMET_RECEIVE_KEY_NAME: COMET_RECEIVE_KEY, "updatedSubscription":subscription.jsonify() } if store and update_store: payload.update({"updatedStore":store.jsonify()}) comet_receive(subscription.Store, payload) # end of while loop sub_count -= LIMIT skip += LIMIT # everything is done - send the emails send_email_receipt_monthly_batch(asiss)
def register(request): """ Adds a new employee to the currently logged in Store. This automatically sets this employee to approved. """ data = {'employees_nav': True} settings = SESSION.get_settings(request.session) store = SESSION.get_store(request.session) if request.method == "POST": from_associated_account = False # check if this post is from the associated account dialog # if it is then skip form validations aaf_nonce_id = request.POST.get('aaf-nonce') aaf_account_id = request.POST.get('aaf-account_id') if len(aaf_nonce_id) > 0 and len(aaf_account_id) > 0: aa_nonce = AssociatedAccountNonce.objects.filter(\ id=aaf_nonce_id, account_id=aaf_account_id) if len(aa_nonce) > 0 and aa_nonce[0].verified: aa_nonce[0].delete() from_associated_account = True account_form = EmployeeAccountSignUpForm(request.POST) employee_form = EmployeeForm(request.POST) if not from_associated_account: all_forms_valid = account_form.is_valid() and\ employee_form.is_valid() else: all_forms_valid = True if all_forms_valid: postDict = request.POST.dict() # make the cloud call # see cloud param for possible access level values acl = postDict['acl'] if acl == ACCESS_ADMIN[0]: access_level = "admin" elif acl == ACCESS_PUNCHREDEEM[0]: access_level = "punch_redeem" else: access_level = None params = { "retailer_pin": settings.get("retailer_pin"), "username": postDict['email'].strip().lower(), "first_name": postDict['first_name'].capitalize(), "last_name": postDict['last_name'].capitalize(), "email": postDict['email'].strip().lower(), "status": APPROVED, "access_level": access_level, } if from_associated_account: res = cloud_call("link_employee", params) else: params["password"] = postDict['password'] res = cloud_call("register_employee", params) # don't forget to retrieve the latest session request.session.clear() request.session.update(SessionStore(request.session.session_key)) # check if email already taken here to handle the case where # the user already has a patron/employee account # but also want to sign up for a Store account if "error" in res and res['error'] in ("EMAIL_TAKEN_AVAILABLE", "USERNAME_TAKEN_AVAILABLE"): aa = Account.objects().get( email=postDict['email'].strip().lower()) aan = AssociatedAccountNonce.objects.create(\ account_id=aa.objectId) return HttpResponse(json.dumps({"associated_account":\ aa.objectId, "associated_account_nonce":aan.id, "email": aa.email, "code": 0}), content_type="application/json") elif "error" in res and res['error'] in ("EMAIL_TAKEN", "USERNAME_TAKEN"): account_form._errors.setdefault( "email", ErrorList()).append(u"Email is already being used.") elif "error" not in res: # add the employee to the approved list employees_approved_list =\ SESSION.get_employees_approved_list(request.session) employees_approved_ids =\ [ emp.objectId for emp in employees_approved_list ] new_employee = Employee(**res["result"]) if new_employee.objectId not in employees_approved_ids: employees_approved_list.insert(0, new_employee) request.session['employees_approved_list'] =\ employees_approved_list # update our local store's acl - don't wait for # the cloud post store = SESSION.get_store(request.session) store.set_access_level(Account.objects().get(\ email=postDict['email'].strip().lower()), acl) request.session['store'] = store # notify other dashboards of this change payload = { COMET_RECEIVE_KEY_NAME: COMET_RECEIVE_KEY, "updatedStore": store.jsonify() } comet_receive(store.objectId, payload) return HttpResponse(json.dumps({"code": 2}), content_type="application/json") else: return HttpResponse(json.dumps({"code": 3}), content_type="application/json") else: employee_form = EmployeeForm(initial={"acl":\ ACCESS_PUNCHREDEEM[0]}) account_form = EmployeeAccountSignUpForm() data["employee_form"] = employee_form data["account_form"] = account_form return render(request, 'manage/employee_register.djhtml', data)
def test_settings(): # setup account = Account.objects().get(username=TEST_USER['username'], include="Store.Settings") store = account.store settings = store.settings # set punches facebook and employees to 1 store.punches_facebook = 1 settings.punches_employee = 1 store.update() settings.update() test = SeleniumTest() parts = [ {'test_name': "User needs to be logged in to access page"}, {'test_name': "Changes to Punches employee are visible"}, {'test_name': "Changes to Punches facebook are visible"}, {'test_name': "Changes to Punches employee are saved to Parse"}, {'test_name': "Changes to Punches facebook are saved to Parse"}, {'test_name': "Punches employee is required"}, {'test_name': "Punches facebook is required"}, {'test_name': "Punches employee must be a number"}, {'test_name': "Punches facebook must be a number."}, {'test_name': "Punches employee must be greater than 0"}, {'test_name': "Punches facebook must be greater than or = 0"}, {'test_name': "Retailer PIN is refreshable"}, {'test_name': "Changes to Retailer PIN is immediately " +\ "commited to Parse without having to save settings."}, {'test_name': "Clicking cancle redirects user back to " +\ "settings page"}, {'test_name': "Clicking cancel changes will not undo the " +\ "change made to Retailer PIN"}, {'test_name': "Clicking cancel changes will not save " +\ "changes to punches facebook and punches employee"}, ] section = { "section_name": "Settings page working properly?", "parts": parts, } test.results.append(section) ########## User needs to be logged in to access page test.open(reverse("store_settings")) # ACTION! sleep(1) parts[0]['success'] = test.is_current_url(reverse(\ 'manage_login') + "?next=" + reverse("store_settings")) # login selectors = (("#login_username", TEST_USER['username']), ("#login_password", TEST_USER['password']), ("", Keys.RETURN)) test.action_chain(0, selectors, "send_keys") # ACTION! sleep(5) try: selectors = ( ("#id_punches_employee", "5"), ("#id_punches_facebook", "5"), ) test.action_chain(0, selectors, action="clear") test.action_chain(0, selectors, action="send_keys") test.find("#settings-form-submit").click() sleep(5) except Exception as e: print e ########## Changes to Punches employee are visible try: parts[1]['success'] = test.find(\ "#id_punches_employee").get_attribute("value") == "5" except Exception as e: print e parts[1]['test_message'] = str(e) ########## Changes to Punches facebook are visible try: parts[2]['success'] = test.find(\ "#id_punches_facebook").get_attribute("value") == "5" except Exception as e: print e parts[2]['test_message'] = str(e) ########## Changes to Punches employee are saved to Parse try: settings.punches_employee = None parts[3]['success'] = settings.get("punches_employee") ==\ int(test.find("#id_punches_employee").get_attribute("value")) except Exception as e: print e parts[3]['test_message'] = str(e) ########## Changes to Punches facebook are saved to Parse try: store.punches_facebook = None parts[4]['success'] = store.get("punches_facebook") ==\ int(test.find("#id_punches_facebook").get_attribute("value")) except Exception as e: print e parts[4]['test_message'] = str(e) try: selectors = ("#id_punches_employee", "#id_punches_facebook") test.action_chain(0, selectors, action="clear") test.find("#settings-form-submit").click() sleep(1) except Exception: pass ########## Punches employee is required try: parts[5]['success'] =\ test.find("#punches_employee_e ul li").text ==\ "This field is required." except Exception as e: print e parts[5]['test_message'] = str(e) ########## Punches facebook is required try: parts[6]['success'] =\ test.find("#punches_facebook_e ul li").text ==\ "This field is required." except Exception as e: print e parts[6]['test_mesage'] = str(e) try: selectors = ( ("#id_punches_employee", "a"), ("#id_punches_facebook", "b"), ) test.action_chain(0, selectors, action="clear") test.action_chain(0, selectors, action="send_keys") test.find("#settings-form-submit").click() sleep(1) except Exception: pass ########## Punches employee must be a number try: parts[7]['success'] =\ test.find("#punches_employee_e ul li").text==\ "Enter a whole number." except Exception as e: print e parts[7]['test_message'] = str(e) ########## Punches facebook must be a number try: parts[8]['success'] =\ test.find("#punches_facebook_e ul li").text==\ "Enter a whole number." except Exception as e: print e parts[8]['test_message'] = str(e) try: selectors = ( ("#id_punches_employee", "-1"), ("#id_punches_facebook", "-1"), ) test.action_chain(0, selectors, action="clear") test.action_chain(0, selectors, action="send_keys") test.find("#settings-form-submit").click() sleep(1) except Exception: pass ########## Punches employee must be greater than 0 try: parts[9]['success'] =\ test.find("#punches_employee_e ul li").text ==\ "Ensure this value is greater than or equal to 1." except Exception as e: print e parts[9]['test_message'] = str(e) ########## Punches facebook must be greater than or equal to 0 try: parts[10]['success'] =\ test.find("#punches_facebook_e ul li").text ==\ "Ensure this value is greater than or equal to 0." except Exception as e: print e parts[10]['test_message'] = str(e) ########## Retailer PIN is refreshable try: prev_pin = test.find("#retailer_pin").text test.find("#link_refresh_retailer_pin").click() sleep(10) # yea this takes a while new_pin = test.find("#retailer_pin").text parts[11]['success'] = prev_pin != new_pin except Exception as e: print e parts[11]['test_message'] = str(e) ########## Changes to Retailer PIN is immediately ### commited to Parse without having to save settings try: settings.retailer_pin = None parts[12]['success'] = settings.get("retailer_pin") == new_pin except Exception as e: print e parts[12]['test_message'] = str(e) ########## Clicking cancel redirects user back to settings page try: current_pin = test.find("#retailer_pin").text test.find("//div[@id='settings-options']/a[2]", type="xpath").click() sleep(1) parts[13]['success'] =\ test.is_current_url(reverse("store_settings")) except Exception as e: print e parts[13]['test_message'] = str(e) ########## Clicking cancel changes will not undo the ### change made to Retailer PIN try: parts[14]['success'] = current_pin ==\ test.find("#retailer_pin").text except Exception as e: print e parts[14]['test_message'] = str(e) ########## Clicking cancel changes will not save ### changes to punches facebook and punches employee try: current_ep =\ test.find("#id_punches_employee").get_attribute("value") current_fbp =\ test.find("#id_punches_facebook").get_attribute("value") test.find("//div[@id='settings-options']/a[2]", type="xpath").click() sleep(1) parts[15]['success'] = current_ep == test.find(\ "#id_punches_employee").get_attribute("value") and\ current_fbp == test.find(\ "#id_punches_facebook").get_attribute("value") except Exception as e: print e parts[15]['test_message'] = str(e) # END OF ALL TESTS - cleanup return test.tear_down()
def employee_is_owner(session, employee_id): account = Account.objects().get(Employee=employee_id) return SESSION.get_store(session).is_owner(account)
def handle(self, *args, **options): # for logging when ran by CRON print "Running passed_user_limit: " + str(timezone.now()) now = timezone.now() b4_now = now + relativedelta(hours=-1) # get 500 subscriptions at a time LIMIT = 500 # first scan though all the stores and set their # date_passed_user_limit if so # TODO optimize with a relational query? possible with Parse? #### SUB_TYPE 0 skip = 0 sub_count = Subscription.objects().count(\ date_passed_user_limit=None, subscriptionType=0) max_users = sub_type[0]['max_users'] while sub_count > 0: for sub in Subscription.objects().filter(\ subscriptionType=0, include="Store", date_passed_user_limit=None, god_mode=False, limit=LIMIT, skip=skip, order="createdAt"): store = sub.store if store.get("patronStores", count=1, limit=0) >\ max_users: sub.date_passed_user_limit = b4_now sub.update() # notify the dashboards of these changes payload = { COMET_RECEIVE_KEY_NAME: COMET_RECEIVE_KEY, "updatedSubscription": sub.jsonify() } comet_receive(sub.Store, payload) # end of while loop sub_count -= LIMIT skip += LIMIT # TODO optimize with a relational query? possible with Parse? #### SUB_TYPE 1 skip = 0 sub_count = Subscription.objects().count(\ date_passed_user_limit=None, subscriptionType=1) max_users = sub_type[1]['max_users'] while sub_count > 0: for sub in Subscription.objects().filter(\ subscriptionType=1, include="Store", date_passed_user_limit=None, god_mode=False, limit=LIMIT, skip=skip, order="createdAt"): store = sub.store if store.get("patronStores", count=1, limit=0) >\ max_users: sub.date_passed_user_limit = b4_now sub.update() # notify the dashboards of these changes payload = { COMET_RECEIVE_KEY_NAME: COMET_RECEIVE_KEY, "updatedSubscription": sub.jsonify() } comet_receive(sub.Store, payload) # end of while loop sub_count -= LIMIT skip += LIMIT ################ conn = mail.get_connection(fail_silently=(not DEBUG)) conn.open() # 1st day time range day1_end = now.replace() day1_start = day1_end + relativedelta(hours=-24) # 4th day time range day4_end = now + relativedelta(days=-4) day4_start = day4_end + relativedelta(hours=-24) # 8th day time range day8_end = now + relativedelta(days=-8) day8_start = day8_end + relativedelta(hours=-24) # 14th day time range day14_end = now + relativedelta(days=-14) day14_start = day14_end + relativedelta(hours=-24) #### SUB_TYPE 0 ## 1st day skip = 0 sub_count = Subscription.objects().count(\ subscriptionType=0, date_passed_user_limit__lte=day1_end, date_passed_user_limit__gte=day1_start) while sub_count > 0: for sub in Subscription.objects().filter(\ subscriptionType=0, include="Store", date_passed_user_limit__lte=day1_end, date_passed_user_limit__gte=day1_start, limit=LIMIT, skip=skip, order="createdAt"): # with pp_cc_id if sub.pp_cc_id and len(sub.pp_cc_id) > 0: sub.subscriptionType = 1 sub.date_passed_user_limit = None sub.update() # notify the dashboards of these changes payload = { COMET_RECEIVE_KEY_NAME: COMET_RECEIVE_KEY, "updatedSubscription": sub.jsonify() } comet_receive(sub.Store, payload) package = { "status": "upgraded", "sub_type": sub_type[0]["name"], "new_sub_type": sub_type[1]["name"], "new_sub_type_cost": sub_type[1]["monthly_cost"], "new_max_patronStore_count":\ sub_type[1]["max_users"], "patronStore_count": sub.store.get(\ "patronStores", limit=0, count=1), } # no pp_cc_id else: package = { "sub_type": sub_type[0]["name"], "max_patronStore_count": sub_type[0]["max_users"], "patronStore_count": sub.store.get(\ "patronStores", limit=0, count=1), "disable_date": sub.date_passed_user_limit + relativedelta(days=\ USER_LIMIT_PASSED_DISABLE_DAYS), } try: send_email_passed_user_limit(Account.objects().get(\ Store=sub.Store), sub.store, package, conn) except SMTPServerDisconnected: conn = mail.get_connection(fail_silently=(not DEBUG)) conn.open() send_email_passed_user_limit(Account.objects().get(\ Store=sub.Store), sub.store, package, conn) # end of while loop sub_count -= LIMIT skip += LIMIT ## 4th day skip = 0 sub_count = Subscription.objects().count(\ subscriptionType=0, date_passed_user_limit__lte=day4_end, date_passed_user_limit__gte=day4_start) while sub_count > 0: for sub in Subscription.objects().filter(\ subscriptionType=0, include="Store", date_passed_user_limit__lte=day4_end, date_passed_user_limit__gte=day4_start, limit=LIMIT, skip=skip, order="createdAt"): package = { "sub_type": sub_type[0]["name"], "max_patronStore_count": sub_type[0]["max_users"], "patronStore_count": sub.store.get(\ "patronStores", limit=0, count=1), "disable_date": sub.date_passed_user_limit + relativedelta(days=\ USER_LIMIT_PASSED_DISABLE_DAYS), } try: send_email_passed_user_limit(Account.objects().get(\ Store=sub.Store), sub.store, package, conn) except SMTPServerDisconnected: conn = mail.get_connection(fail_silently=(not DEBUG)) conn.open() send_email_passed_user_limit(Account.objects().get(\ Store=sub.Store), sub.store, package, conn) # end of while loop sub_count -= LIMIT skip += LIMIT ## 8th day skip = 0 sub_count = Subscription.objects().count(\ subscriptionType=0, date_passed_user_limit__lte=day8_end, date_passed_user_limit__gte=day8_start) while sub_count > 0: for sub in Subscription.objects().filter(\ subscriptionType=0, include="Store", date_passed_user_limit__lte=day8_end, date_passed_user_limit__gte=day8_start, limit=LIMIT, skip=skip, order="createdAt"): package = { "sub_type": sub_type[0]["name"], "max_patronStore_count": sub_type[0]["max_users"], "patronStore_count": sub.store.get(\ "patronStores", limit=0, count=1), "disable_date": sub.date_passed_user_limit + relativedelta(days=\ USER_LIMIT_PASSED_DISABLE_DAYS), } try: send_email_passed_user_limit(Account.objects().get(\ Store=sub.Store), sub.store, package, conn) except SMTPServerDisconnected: conn = mail.get_connection(fail_silently=(not DEBUG)) conn.open() send_email_passed_user_limit(Account.objects().get(\ Store=sub.Store), sub.store, package, conn) # end of while loop sub_count -= LIMIT skip += LIMIT ## 14th day skip = 0 sub_count = Subscription.objects().count(\ subscriptionType=0, date_passed_user_limit__lte=day14_end, date_passed_user_limit__gte=day14_start) while sub_count > 0: for sub in Subscription.objects().filter(\ subscriptionType=0, include="Store", date_passed_user_limit__lte=day14_end, date_passed_user_limit__gte=day14_start, limit=LIMIT, skip=skip, order="createdAt"): package = {"status": "disabled"} # deactivate the store sub.store.active = False sub.store.update() payload = { COMET_RECEIVE_KEY_NAME: COMET_RECEIVE_KEY, "updatedStore": sub.store.jsonify(), } comet_receive(sub.Store, payload) try: send_email_passed_user_limit(Account.objects().get(\ Store=sub.Store), sub.store, package, conn) except SMTPServerDisconnected: conn = mail.get_connection(fail_silently=(not DEBUG)) conn.open() send_email_passed_user_limit(Account.objects().get(\ Store=sub.Store), sub.store, package, conn) # end of while loop sub_count -= LIMIT skip += LIMIT #### SUB_TYPE 1 ## 1st day skip = 0 sub_count = Subscription.objects().count(\ subscriptionType=1, date_passed_user_limit__lte=day1_end, date_passed_user_limit__gte=day1_start) while sub_count > 0: for sub in Subscription.objects().filter(\ subscriptionType=1, include="Store", date_passed_user_limit__lte=day1_end, date_passed_user_limit__gte=day1_start, limit=LIMIT, skip=skip, order="createdAt"): # with pp_cc_id if sub.pp_cc_id and len(sub.pp_cc_id) > 0: sub.subscriptionType = 2 sub.date_passed_user_limit = None sub.update() # notify the dashboards of these changes payload = { COMET_RECEIVE_KEY_NAME: COMET_RECEIVE_KEY, "updatedSubscription": sub.jsonify() } comet_receive(sub.Store, payload) package = { "status": "upgraded", "sub_type": sub_type[1]["name"], "new_sub_type": sub_type[2]["name"], "new_sub_type_cost": sub_type[2]["monthly_cost"], "new_max_patronStore_count": "Unlimited", "patronStore_count": sub.store.get(\ "patronStores", limit=0, count=1), } # no pp_cc_id else: package = { "sub_type": sub_type[1]["name"], "max_patronStore_count": sub_type[1]["max_users"], "patronStore_count": sub.store.get(\ "patronStores", limit=0, count=1), "disable_date": sub.date_passed_user_limit + relativedelta(days=\ USER_LIMIT_PASSED_DISABLE_DAYS), } try: send_email_passed_user_limit(Account.objects().get(\ Store=sub.Store), sub.store, package, conn) except SMTPServerDisconnected: conn = mail.get_connection(fail_silently=(not DEBUG)) conn.open() send_email_passed_user_limit(Account.objects().get(\ Store=sub.Store), sub.store, package, conn) # end of while loop sub_count -= LIMIT skip += LIMIT ## 4th day skip = 0 sub_count = Subscription.objects().count(\ subscriptionType=1, date_passed_user_limit__lte=day4_end, date_passed_user_limit__gte=day4_start) while sub_count > 0: for sub in Subscription.objects().filter(\ subscriptionType=1, include="Store", date_passed_user_limit__lte=day4_end, date_passed_user_limit__gte=day4_start, limit=LIMIT, skip=skip, order="createdAt"): package = { "sub_type": sub_type[1]["name"], "max_patronStore_count": sub_type[1]["max_users"], "patronStore_count": sub.store.get(\ "patronStores", limit=0, count=1), "disable_date": sub.date_passed_user_limit + relativedelta(days=\ USER_LIMIT_PASSED_DISABLE_DAYS), } try: send_email_passed_user_limit(Account.objects().get(\ Store=sub.Store), sub.store, package, conn) except SMTPServerDisconnected: conn = mail.get_connection(fail_silently=(not DEBUG)) conn.open() send_email_passed_user_limit(Account.objects().get(\ Store=sub.Store), sub.store, package, conn) # end of while loop sub_count -= LIMIT skip += LIMIT ## 8th day skip = 0 sub_count = Subscription.objects().count(\ subscriptionType=1, date_passed_user_limit__lte=day8_end, date_passed_user_limit__gte=day8_start) while sub_count > 0: for sub in Subscription.objects().filter(\ subscriptionType=1, include="Store", date_passed_user_limit__lte=day8_end, date_passed_user_limit__gte=day8_start, limit=LIMIT, skip=skip, order="createdAt"): package = { "sub_type": sub_type[1]["name"], "max_patronStore_count": sub_type[1]["max_users"], "patronStore_count": sub.store.get(\ "patronStores", limit=0, count=1), "disable_date": sub.date_passed_user_limit + relativedelta(days=\ USER_LIMIT_PASSED_DISABLE_DAYS), } try: send_email_passed_user_limit(Account.objects().get(\ Store=sub.Store), sub.store, package, conn) except SMTPServerDisconnected: conn = mail.get_connection(fail_silently=(not DEBUG)) conn.open() send_email_passed_user_limit(Account.objects().get(\ Store=sub.Store), sub.store, package, conn) # end of while loop sub_count -= LIMIT skip += LIMIT ## 14th day skip = 0 sub_count = Subscription.objects().count(\ subscriptionType=1, date_passed_user_limit__lte=day14_end, date_passed_user_limit__gte=day14_start) while sub_count > 0: for sub in Subscription.objects().filter(\ subscriptionType=1, include="Store", date_passed_user_limit__lte=day14_end, date_passed_user_limit__gte=day14_start, limit=LIMIT, skip=skip, order="createdAt"): package = {"status": "disabled"} # deactivate the store sub.store.active = False sub.store.update() payload = { COMET_RECEIVE_KEY_NAME: COMET_RECEIVE_KEY, "updatedStore": sub.store.jsonify(), } comet_receive(sub.Store, payload) try: send_email_passed_user_limit(Account.objects().get(\ Store=sub.Store), sub.store, package, conn) except SMTPServerDisconnected: conn = mail.get_connection(fail_silently=(not DEBUG)) conn.open() send_email_passed_user_limit(Account.objects().get(\ Store=sub.Store), sub.store, package, conn) # end of while loop sub_count -= LIMIT skip += LIMIT try: conn.close() except Exception: pass
def test_update_subscription(): # TODO test place_order account = Account.objects().get(username=TEST_USER['username'], include="Store.Subscription") store = account.store subscription = store.subscription subscription.update_locally(SUBSCRIPTION_INFO, False) subscription.update() test = SeleniumTest() parts = [ {'test_name': "User needs to be logged in to access page"}, {'test_name': "Update account page reachable"}, {'test_name': "Changes to first name are visible"}, {'test_name': "Changes to first name are saved to parse"}, {'test_name': "Changes to last name are visible"}, {'test_name': "Changes to last name are saved to parse"}, {'test_name': "Changes to card number are visible"}, {'test_name': "Changes to card number are saved to parse"}, {'test_name': "A paypal credit card id is generated & saved"}, {'test_name': "Changes to cc expiration are visible"}, {'test_name': "Changes to cc expiration are saved to parse"}, {'test_name': "Changes to address are visible"}, {'test_name': "Changes to address are saved to parse"}, {'test_name': "Changes to city are visible"}, {'test_name': "Changes to city are saved to parse"}, {'test_name': "Changes to state are visible"}, {'test_name': "Changes to state are saved to parse"}, {'test_name': "Changes to zip are visible"}, {'test_name': "Changes to zip are saved to parse"}, {'test_name': "First name is required"}, {'test_name': "Last name is required"}, {'test_name': "Card number is required"}, {'test_name': "Security code (cvc) is required"}, {'test_name': "Address is required"}, {'test_name': "City is required"}, {'test_name': "State is required"}, {'test_name': "Zip is required"}, {'test_name': "ToS checked is required"}, {'test_name': "Invalid credit card number shows error"}, {'test_name': "Past expiration date is invalid"}, {'test_name': "Only the last 4 digits of the card number" +\ " are shown"}, {'test_name': "Not changing the card number does not " +\ "generate new paypal credit card id"}, ] section = { "section_name": "Edit account/subscription working properly?", "parts": parts, } self.results.append(section) ########## User needs to be logged in to access page self.open(reverse("store_index")) sleep(1) parts[0]['success'] = self.is_current_url(reverse(\ 'manage_login') + "?next=" + reverse("store_index")) # login selectors = ( ("#login_username", TEST_USER['username']), ("#login_password", TEST_USER['password']), ("", Keys.RETURN) ) self.action_chain(0, selectors, "send_keys") sleep(7) ########## Update account page reachable try: self.find("//div[@id='account-options']/a[1]", type="xpath").click() sleep(3) parts[1]['success'] =\ self.is_current_url(reverse("subscription_update")) except Exception as e: print e parts[1]['test_message'] = str(e) ## Make changes # first clear all inputs for el in self.find("input[type='text']", multiple=True): el.clear() selectors = ( ("#id_first_name", TEST_SUBSCRIPTION_INFO['first_name']), ("#id_last_name", TEST_SUBSCRIPTION_INFO['last_name']), ("#id_cc_number", TEST_SUBSCRIPTION_INFO['cc_number']), ("#id_cc_cvv", "905"), ("#id_address", TEST_SUBSCRIPTION_INFO['address']), ("#id_city", TEST_SUBSCRIPTION_INFO['city']), ("#id_state", TEST_SUBSCRIPTION_INFO['state']), ("#id_zip", TEST_SUBSCRIPTION_INFO['zip']), ) self.action_chain(0, selectors, action="send_keys") month_el =\ self.find("//select[@id='id_date_cc_expiration_month']/" +\ "option[@value='%s']" % (str(TEST_SUBSCRIPTION_INFO[\ 'date_cc_expiration'].month),), type="xpath") year_el =\ self.find("//select[@id='id_date_cc_expiration_year']/" +\ "option[@value='%s']" % (str(TEST_SUBSCRIPTION_INFO[\ 'date_cc_expiration'].year),), type="xpath") month = month_el.get_attribute("value") year = year_el.get_attribute("value") month_el.click() year_el.click() self.find("#id_recurring").click() self.find("#update-form-submit").click() sleep(5) # back to update account page self.find("//div[@id='account-options']/a[1]", type="xpath").click() sleep(3) ########## Changes to first name are visible parts[2]['success'] =\ self.find("#id_first_name").get_attribute("value") ==\ TEST_SUBSCRIPTION_INFO['first_name'] ########## Changes to first name are saved to parse subscription.first_name = None parts[3]['success'] = subscription.get("first_name") ==\ TEST_SUBSCRIPTION_INFO['first_name'] ########## Changes to last name are visible parts[4]['success'] =\ self.find("#id_last_name").get_attribute("value") ==\ TEST_SUBSCRIPTION_INFO['last_name'] ########## Changes to last name are saved to parse subscription.last_name = None parts[5]['success'] = subscription.get("last_name") ==\ TEST_SUBSCRIPTION_INFO['last_name'] ########## Changes to card number are visible parts[6]['success'] =\ self.find("#id_cc_number").get_attribute("value")[-4:] ==\ TEST_SUBSCRIPTION_INFO['cc_number'][-4:] ########## Changes to card number are saved to parse subscription.cc_number = None parts[7]['success'] = subscription.get("cc_number") ==\ TEST_SUBSCRIPTION_INFO['cc_number'][-4:] ########## A paypal credit card id is generated & saved # CARD-97223025G70599255KHHCCVQ subscription.pp_cc_id = None parts[8]['success'] = subscription.get("pp_cc_id").__contains__(\ "CARD") and len(subscription.get("pp_cc_id")) == 29 ########## Changes to cc expiration are visible parts[9]['success'] = month == self.get_selected("//select" +\ "[@id='id_date_cc_expiration_month']/option", type="xpath").get_attribute("value") and\ year == self.get_selected(\ "//select[@id='id_date_cc_expiration_year']/option", type="xpath").get_attribute("value") ########## Changes to cc expiration are saved to parse subscription.date_cc_expiration = None exp = subscription.get("date_cc_expiration") parts[10]['success'] = exp.month == TEST_SUBSCRIPTION_INFO[\ 'date_cc_expiration'].month and exp.year ==\ TEST_SUBSCRIPTION_INFO['date_cc_expiration'].year ########## Changes to address are visible parts[11]['success'] =\ self.find("#id_address").get_attribute("value") ==\ TEST_SUBSCRIPTION_INFO['address'] ########## Changes to address are saved to parse subscription.address = None parts[12]['success'] = subscription.get("address") ==\ TEST_SUBSCRIPTION_INFO['address'] ########## Changes to city are visible parts[13]['success'] =\ self.find("#id_city").get_attribute("value") ==\ TEST_SUBSCRIPTION_INFO['city'] ########## Changes to city are saved to parse subscription.city = None parts[14]['success'] = subscription.get("city") ==\ TEST_SUBSCRIPTION_INFO['city'] ########## Changes to state are visible self.find("//select[@id='id_date_cc_expiration_year']/" +\ "option[@value='%s']" % (str(TEST_SUBSCRIPTION_INFO[\ 'date_cc_expiration'].year)), type="xpath") parts[15]['success'] =\ self.find("#id_state").get_attribute("value") ==\ TEST_SUBSCRIPTION_INFO['state'] ########## Changes to state are saved to parse subscription.state = None parts[16]['success'] = subscription.get("state") ==\ TEST_SUBSCRIPTION_INFO['state'] ########## Changes to zip are visible parts[17]['success'] =\ self.find("#id_zip").get_attribute("value") ==\ TEST_SUBSCRIPTION_INFO['zip'] ########## Changes to zip are saved to parse subscription.zip = None parts[18]['success'] = subscription.get("zip") ==\ TEST_SUBSCRIPTION_INFO['zip'] ## Make changes selectors = [ "#id_first_name", "#id_last_name", "#id_cc_number", "#id_cc_cvv", "#id_address", "#id_city", "#id_state", "#id_zip", ] self.action_chain(0, selectors, action="clear") for i in range(len(selectors)): selectors[i] = (selectors[i], " ") self.action_chain(0, selectors, action="send_keys") self.find("#update-form-submit").click() sleep(3) ########## First name is required ########## Last name is required ########## Card number is required ########## Security code (cvc) is required ########## Address is required ########## City is required ########## State is required ########## Zip is required ########## ToS checked is required def field_is_required(part, selector, message="This field is required."): try: parts[part]['success'] = self.find(selector).text==message except Exception as e: print e parts[part]['test_message'] = str(e) selectors = ( (19, "#first_name_ic ul.errorlist li"), (20, "#last_name_ic ul.errorlist li"), (21, "#card_number_container ul.errorlist li", "Enter a valid credit card number."), (22, "#cc_cvv_ic ul.errorlist li"), (23, "#address_ic ul.errorlist li"), (24, "#city_ic ul.errorlist li"), (25, "#state_ic ul.errorlist li"), (26, "#zip_ic ul.errorlist li"), (27, "#recurring_charge_container ul.errorlist li", "You must accept the Terms & Conditions to continue."), ) for selector in selectors: field_is_required(*selector) ########## Invalid credit card number shows error try: cc_number = self.find("#id_cc_number") cc_number.clear() cc_number.send_keys("8769233313929990") self.find("#update-form-submit").click() sleep(3) parts[28]['success'] = self.find("#card_number_container " +\ "ul.errorlist li").text ==\ "Enter a valid credit card number." except Exception as e: print e parts[28]['test_message'] = str(e) ########## Past expiration date is invalid if timezone.now().month == 1: # note that if this test is being run on a January, this will # fail so to prevent that just skip the test if it is January parts[29]['test_message'] = "READ ME. This test has been " +\ "skipped because the month is January, which means " +\ "that this test will always fail due to limited " +\ "select options." else: try: # select january of this year. self.find("//select[@id='id_date_cc_expiration_month']/" +\ "option[@value='1']", type="xpath").click() self.find("//select[@id='id_date_cc_expiration_year']/" +\ "option[@value='%s']" %\ (str(timezone.now().year),), type="xpath").click() self.find("#update-form-submit").click() sleep(3) parts[29]['success'] =\ self.find("#date_cc_expiration_ic ul.errorlist " +\ "li").text == "Your credit card has expired!" except Exception as e: print e parts[29]['test_message'] = str(e) ########## Only the last 4 digits of the card number are shown try: self.find("//div[@class='form-options']/a[2]", type="xpath").click() sleep(1) self.find("//div[@id='account-options']/a[1]", type="xpath").click() sleep(3) masked_number =\ self.find("#id_cc_number").get_attribute("value") parts[30]['success'] = masked_number[:-4] ==\ "************" and str(masked_number[-4:]).isdigit() except Exception as e: print e parts[30]['test_message'] = str(e) ########## Not changing the card number does not ###### generate new paypal credit card id try: subscription.pp_cc_id = None cc_id = subscription.get("pp_cc_id") self.find("#id_cc_cvv").send_keys("123") self.find("#id_recurring").click() self.find("#update-form-submit").click() sleep(5) subscription.pp_cc_id = None parts[31]['success'] = cc_id == subscription.get("pp_cc_id") except Exception as e: print e parts[31]['test_message'] = str(e) # END OF ALL TESTS - cleanup return self.tear_down()
def test_feedbacks(): """ This test also tests cloud code send_feedback. """ # we can clear the list locally but just re-pull from parse account = Account.objects().get(username=TEST_USER['username'], include="Store.Subscription") store = account.store subscription = store.subscription account = Account.objects().get(username=TEST_PATRON['username'], include="Patron") patron = account.patron # make sure that the account is not used for these tests account = None # clear the received messages relation received_messages = store.get("receivedMessages", keys="") if received_messages: store.remove_relation("ReceivedMessages_", [m.objectId for m in received_messages]) store.set("receivedMessages", None) test = SeleniumTest() parts = [ {'test_name': "User needs to be logged in to access page"}, {'test_name': "Cloud code send_feedback works"}, {'test_name': "Notification badge appears if there are" +\ " unread feedbacks"}, {'test_name': "A new row appears when feedback is received"}, {'test_name': "Feedback is initially unread (dashboard)"}, {'test_name': "Feedback is in store's ReceivedMessages " +\ "relation and is initially unread"}, {'test_name': "Clicking the row redirects user to the " +\ "feedback detail page"}, {'test_name': "Clicking back to feedback inbox redirects " +\ "user back to messages index with feedback tab active"}, {'test_name': "Feedback is now read (dashboard)"}, {'test_name': "Feedback is now read (Parse)"}, {'test_name': "Clicking reply redirects user to feedback " +\ "reply page"}, {'test_name': "Reply body is required."}, {'test_name': "Replying redirects user back to feedback " +\ "details"}, {'test_name': "The reply is visible"}, {'test_name': "The reply message is saved in the store's " +\ "sent messages relation with message_type of feedback"}, {'test_name': "The reply message is saved in the Patron's " +\ "received messages relation wrapped in a Message Status"}, {'test_name': "A feedback with a reply does not have a " +\ "reply button"}, {'test_name': "Clicking delete message prompts the user " +\ "to confirm the deletion"}, {'test_name': "The user is redirected to messages index " +\ "with feedback tab active"}, {'test_name': "Deleting the reply only removes the message" +\ " from the store's sent messages relation"}, {'test_name': "The deleted feedback is no longer in " +\ "the table"}, {'test_name': "Multiple feedbacks (testing 3 here) " +\ "can appear at the same time"}, ] section = { "section_name": "Receiving messages works?", "parts": parts, } test.results.append(section) ########## User needs to be logged in to access page test.open(reverse("messages_index")) # ACTION! sleep(1) parts[0]['success'] = test.is_current_url(reverse(\ 'manage_login') + "?next=" + reverse("messages_index")) # login selectors = (("#login_username", TEST_USER['username']), ("#login_password", TEST_USER['password']), ("", Keys.RETURN)) test.action_chain(0, selectors, "send_keys") # ACTION! sleep(5) def send_feedback(subject, body): """ This is a consumer action - not dashboard """ return cloud_call( "send_feedback", { "store_id": store.objectId, "patron_id": patron.objectId, "sender_name": patron.get_fullname(), "subject": subject, "body": body, }) def feedback_id(feedback_tr): return feedback_tr.find_element_by_css_selector(\ "a").get_attribute("href").split("/")[-1] def feedback_unread(feedback_tr): """ check parse if the fb is unread """ fb_id = feedback_id(feedback_tr) store.set("receivedMessages", None) msg = store.get("receivedMessages", objectId=fb_id) if msg and len(msg) > 0: return not msg[0].is_read return False ########## Cloud code send_feedback works try: parts[1]['success'] = send_feedback( "feedback #1", "body #1").get("result") == "success" except Exception as e: print e parts[1]['test_message'] = str(e) ########## Notification badge appears if there are ### unread feedbacks try: sleep(COMET_PULL_RATE * 2 + 2) parts[2]['success'] = test.element_exists("#messages-nav " +\ "a div.nav-item-badge") except Exception as e: print e parts[2]['test_message'] = str(e) ########## A new row appears when feedback is received try: test.find("#tab-feedback").click() feedbacks =\ test.find("#tab-body-feedback div.tr", multiple=True) parts[3]['success'] = len(feedbacks) > 0 except Exception as e: print e parts[3]['test_message'] = str(e) ########## Feedback is initially unread (dashboard) try: parts[4]['success'] =\ feedbacks[0].get_attribute("class").__contains__("unread") except Exception as e: print e parts[4]['test_message'] = str(e) ########## Feedback is in store's ReceivedMessages ### relation and is initially unread try: parts[5]['success'] = feedback_unread(feedbacks[0]) except Exception as e: print e parts[5]['test_message'] = str(e) ########## Clicking the row redirects user to the ### feedback detail page try: fb_id = feedback_id(feedbacks[0]) feedbacks[0].find_element_by_css_selector("a").click() sleep(2) parts[6]['success'] = test.is_current_url(reverse(\ "feedback_details", args=(fb_id,))) except Exception as e: print e parts[6]['test_message'] = str(e) ########## Clicking back to feedback inbox redirects ### user back to messages index with feedback tab active try: test.find("#back_to_feedback").click() sleep(1) parts[7]['success'] =\ test.find("#tab-feedback").get_attribute(\ "class").__contains__("active") except Exception as e: print e parts[7]['test_message'] = str(e) ########## Feedback is now read (dashboard) try: feedbacks =\ test.find("#tab-body-feedback div.tr", multiple=True) parts[8]['success'] = not feedbacks[0].get_attribute(\ "class").__contains__("unread") except Exception as e: print e parts[8]['test_message'] = str(e) ########## Feedback is now read (Parse) try: parts[9]['success'] = not feedback_unread(feedbacks[0]) except Exception as e: print e parts[9]['test_message'] = str(e) ########## Clicking reply redirects user to feedback reply page try: fb_id = feedback_id(feedbacks[0]) feedbacks[0].find_element_by_css_selector("a").click() sleep(2) test.find("#reply-button").click() sleep(1) parts[10]['success'] = test.is_current_url(reverse(\ "feedback_reply", args=(fb_id,))) except Exception as e: print e parts[10]['test_message'] = str(e) ########## Reply body is required. try: test.find("#body").send_keys(" ") test.find("#reply-form-submit").click() sleep(2) parts[11]['success'] = test.find("div.notification.hide " +\ "div").text == "Please enter a message." except Exception as e: print e parts[11]['test_message'] = str(e) ########## Replying redirects user back to feedback details try: test.find("#body").send_keys("Hey") test.find("#reply-form-submit").click() parts[12]['success'] = test.is_current_url(reverse(\ "feedback_details", args=(fb_id,)) +\ "?%s" % urlencode({'success':\ 'Reply has been sent.'})) sleep(5) except Exception as e: print e parts[12]['test_message'] = str(e) ########## The reply is visible try: parts[13]['success'] = test.find("#reply-box " +\ "div.sect.body").text == "Hey" except Exception as e: print e parts[13]['test_message'] = str(e) ########## The reply message is saved in the store's ### sent messages relation with message_type of feedback try: test.find("#back_to_feedback").click() sleep(1) store.set("sentMessages", None) store.set("receivedMessages", None) parts[14]['success'] = len( store.get("sentMessages", objectId=store.get("receivedMessages", objectId=fb_id)[0].Reply, message_type=FEEDBACK)) > 0 except Exception as e: print e parts[14]['test_message'] = str(e) ########## The reply message is saved in the Patron's ### received messages relation wrapped in a Message Status try: patron.set("receivedMessages", None) parts[15]['success'] = len( patron.get("receivedMessages", Message=fb_id)) > 0 except Exception as e: print e parts[15]['test_message'] = str(e) ########## A feedback with a reply does not have a reply button try: feedbacks =\ test.find("#tab-body-feedback div.tr", multiple=True) feedbacks[0].find_element_by_css_selector("a").click() sleep(2) parts[16]['success'] = not test.element_exists(\ "#reply-form-submit") except Exception as e: print e parts[16]['test_message'] = str(e) ########## Clicking delete message prompts the user ### to confirm the deletion try: test.find("#delete-button").click() sleep(1) alert = test.switch_to_alert() parts[17]['success'] = alert.text ==\ "Are you sure you want to delete this feedback thread?" except Exception as e: print e parts[17]['test_message'] = str(e) ########## The user is redirected to messages index ### with feedback tab active try: alert.accept() sleep(4) parts[18]['success'] =\ test.find("#tab-feedback").get_attribute(\ "class").__contains__("active") except Exception as e: print e parts[18]['test_message'] = str(e) ########## Deleting the reply only removes the message ### from the store's sent messages relation try: store.set("receivedMessages", None) parts[19]['success'] = not store.get( "receivedMessages", objectId=fb_id, message_type=FEEDBACK) except Exception as e: print e parts[19]['test_message'] = str(e) ########## The deleted feedback is no longer in the table try: test.find("#tab-feedback").click() sleep(1) feedbacks =\ test.find("#tab-body-feedback div.tr a[href='%s']" %\ (fb_id,), multiple=True) parts[20]['success'] = len(feedbacks) == 0 except Exception as e: print e parts[20]['test_message'] = str(e) ########## Multiple feedbacks (testing 3 here) ### can appear at the same time try: send_feedback("feedback #2", "body #2") send_feedback("feedback #3", "body #3") send_feedback("feedback #4", "body #4") sleep(COMET_PULL_RATE * 3 + 4) feedbacks =\ test.find("#tab-body-feedback div.tr a", multiple=True) parts[21]['success'] = len(feedbacks) == 3 except Exception as e: print e parts[21]['test_message'] = str(e) # END OF ALL TESTS - cleanup return test.tear_down()
def login(request, requestDict, no_recaptcha=False): """ This combines Django's authenticate and login functions. The request.POST must contain a username and password. If authentication is successful, account is updated_locally and adds the appropriate data to the request so that the user/ account is recognized to be logged in. This will also cache some of the store's info in the session. Also checks if the store is active or not. Returns an Account object if the account subscription is active and username and passwords are good. Otherwise, 0 if bad login credentials (wrong pass or pointer to store does not exist) and 1 if subscription is not active, 2 if employee but no access, 3 if employee is still pending, 4 if RECAPTCHA_TOKEN in session and recaptcha response fails. """ # first check if the request is already logged in if request.session.get('account'): return request.session.get('account') # now check for recaptcha if not no_recaptcha and RECAPTCHA_TOKEN in request.session and\ request.session[RECAPTCHA_TOKEN] >= RECAPTCHA_ATTEMPTS: is_valid = recaptcha.submit( request, requestDict['recaptcha_challenge_field'], requestDict['recaptcha_response_field']).is_valid if not is_valid: return 4 # note that email is the same as username # email needs to be stripped and lowered res = account_login(requestDict['username'].strip().lower(), requestDict['password']) if res and "error" not in res: # correct login credentials - remove recaptcha token if exist if not no_recaptcha: recaptcha.login_success(request.session, requestDict['username']) account = Account(**res) account.fetch_all() # If the Account has both a Store and Employee object, # log the user in as the Store owner - not employee store = None if account.Store: store = account.store account_type = "store" elif account.Employee: store = account.employee.get("store") employee = account.employee account_type = "employee" # if the User object has a store then we are good to go if store: store.fetch_all() # check if employee with no access level or still pending if account_type == "employee": if employee.status == PENDING: return 3 elif not store.has_access(account): return 2 settings = store.get("settings") subscription = store.get("subscription") if store.get('active'): request.session[SESSION_KEY] = res.get('sessionToken') # load all cache data! # IMPORTANT for parse.comet.receive to work properly # reason is that if user stays in 1 page, not all # cache data is loaded and things go wrong- # e.g. sending message from window 1 does not update # the message count in window 2 request.session['subscription'] = subscription request.session['settings'] = settings request.session['store'] = store request.session['account'] = account if account_type == "employee": request.session['employee'] = employee SESSION.load_all(request.session) # If value is None, the session reverts to using # the global session expiry policy. if "stay_in" in requestDict and PRODUCTION_SERVER: request.session.set_expiry(None) # If value is 0, the user's session cookie will # expire when the user's Web browser is closed. else: request.session.set_expiry(0) return account else: return 1 else: if not no_recaptcha: recaptcha.login_fail(request.session, requestDict['username']) return 0 else: if not no_recaptcha: recaptcha.login_fail(request.session, requestDict['username']) return 0
def test_messages(): # TODO test that patrons are getting the messages!!! # setup account = Account.objects().get(username=TEST_USER['username'], include="Store.Subscription") store = account.store subscription = store.subscription # set subscriptionType to free subscription.subscriptionType = 0 subscription.update() # clear the sent messages relation sent_messages = store.get("sentMessages", keys="") if sent_messages: store.remove_relation("SentMessages_", [m.objectId for m in sent_messages]) store.set("sentMessages", None) # we can clear the list locally but just re-pull from parse account = Account.objects().get(username=TEST_USER['username'], include="Store.Subscription") store = account.store subscription = store.subscription test = SeleniumTest() parts = [ {'test_name': "User needs to be logged in to access page"}, # FIRST {'test_name': "Send message. Filter all. No offer"}, {'test_name': "Message is in store's sentMessages relation"}, {'test_name': "Message is visible in page"}, {'test_name': "Message can be view by clicking on row"}, # SECOND {'test_name': "Send message. Filter all. With offer"}, {'test_name': "Message is in store's sentMessages relation"}, {'test_name': "Message is visible in page"}, {'test_name': "Message can be view by clicking on row"}, # THIRD {'test_name': "Send message. Filter idle. No offer. " +\ "Message limit passed (free) dialog appears"}, # LIMIT PASSED {'test_name': "Upgrading account from the dialog sends the " +\ "message and upgrades the account to middle"}, {'test_name': "Email is sent notifying user the upgrade"}, # {'test_name': "Message is in store's sentMessages relation"}, {'test_name': "Message is visible in page"}, {'test_name': "Message can be view by clicking on row"}, # FOURTH {'test_name': "Send message. Filter idle. With offer"}, {'test_name': "Message is in store's sentMessages relation"}, {'test_name': "Message is visible in page"}, {'test_name': "Message can be view by clicking on row"}, # FIFTH {'test_name': "Send message. Filter most_loyal. No offer. " +\ "Message limit passed (middle) dialog appears"}, # LIMIT PASSED {'test_name': "Upgrading account from the dialog sends the" +\ " message and upgrades the account to heavy"}, {'test_name': "Email is sent notifying user the upgrade"}, # {'test_name': "Message is in store's sentMessages relation"}, {'test_name': "Message is visible in page"}, {'test_name': "Message can be view by clicking on row"}, # SIXTH {'test_name': "Send message. Filter most_loyal. With offer"}, {'test_name': "Message is in store's sentMessages relation"}, {'test_name': "Message is visible in page"}, {'test_name': "Message can be view by clicking on row"}, # SEVENTH {'test_name': "Send message. Filter all. With offer"}, {'test_name': "Message is in store's sentMessages relation"}, {'test_name': "Message is visible in page"}, {'test_name': "Message can be view by clicking on row"}, # EIGHTH {'test_name': "Send message. Filter all. With offer"}, {'test_name': "Message is in store's sentMessages relation"}, {'test_name': "Message is visible in page"}, {'test_name': "Message can be view by clicking on row"}, # NINTH {'test_name': "Send message. Filter all. No offer. " +\ "Message limit passed (heavy) dialog appears"}, # LIMIT PASSED {'test_name': "Account can no longer be upgraded." +\ "Message cannot be sent. Clicking okay redirects "+\ "user to messages index."}, # {'test_name': "Subject is required"}, {'test_name': "Body is required"}, {'test_name': "Offer title not required if attach offer off"}, {'test_name': "Expiration not required if attach offer off"}, {'test_name': "Offer title is required if attach offer on"}, {'test_name': "Expiration date required if attach offer on"}, {'test_name': "Expiration date must be at a later date"}, {'test_name': "Expiration date must be at most 1 year later"}, ] section = { "section_name": "Sending messages works?", "parts": parts, } test.results.append(section) ########## User needs to be logged in to access page test.open(reverse("messages_index")) # ACTION! sleep(1) parts[0]['success'] = test.is_current_url(reverse(\ 'manage_login') + "?next=" + reverse("messages_index")) # login selectors = (("#login_username", TEST_USER['username']), ("#login_password", TEST_USER['password']), ("", Keys.RETURN)) test.action_chain(0, selectors, "send_keys") # ACTION! sleep(5) def send_message( filter, subject, body, attach_offer=False, offer_title=None, exp_date=None, ): """ Must be called at messages index page """ test.find("#create_message").click() sleep(1) # set the filter test.find("//select[@id='filter']/option[@value='%s']" %\ (filter,), type="xpath").click() # subject test.find("#id_subject").send_keys(subject) # body test.find("#id_body").send_keys(body) # attach_offer if attach_offer: test.find("#id_attach_offer").click() # offer title if offer_title: test.find("#id_offer_title").send_keys(offer_title) # exp_date if exp_date: test.find("#id_date_offer_expiration").send_keys(exp_date) # submit test.find("#send-now").click() sleep(5) def message_in_relation(message_id, test_number): if not message_id: return store.sentMessages = None parts[test_number]['success'] = store.get("sentMessages", objectId=message_id, count=1, limit=0) == 1 def message_in_page(message_id, test_number): if not message_id: return try: rows = test.find("#tab-body-sent div.tr a", multiple=True) for row in rows: if row.get_attribute("href").split("/")[5] ==\ message_id: parts[test_number]['success'] = True except Exception as e: print e parts[test_number]['test_message'] = str(e) def message_viewable(message_id, test_number): if not message_id: return href = reverse("message_details", args=(message_id, )) try: test.find("#tab-body-sent div.tr a[href='%s']" %\ (href,)).click() sleep(2) parts[test_number]['success'] = test.is_current_url(href) except Exception as e: print e parts[test_number]['test_message'] = str(e) finally: # must go back to messages index for the other tests test.open(reverse("messages_index")) # FIRST ########## Send message. Filter all. No offer. message_id = None try: send_message("all", "msg #1", "body #1") parts[1]['success'] = len(test.find(\ "div.notification.success", multiple=True)) > 0 message_id = test.driver.current_url.split("/")[5] except Exception as e: print e parts[1]['test_message'] = str(e) finally: # must go back to messages index test.open(reverse("messages_index")) ########## Message is in store's sentMessages relation. message_in_relation(message_id, 2) ########## Message is visible in page. message_in_page(message_id, 3) ########## Message can be view by clicking on row. message_viewable(message_id, 4) # SECOND ########## Send message. Filter all. With offer. message_id = None try: exp_date = timezone.now() + relativedelta(days=1) send_message("all", "msg #2", "body #2", True, "offer#2", exp_date.strftime(DATE_PICKER_STRFTIME)) parts[5]['success'] = len(test.find(\ "div.notification.success", multiple=True)) > 0 message_id = test.driver.current_url.split("/")[5] except Exception as e: print e parts[5]['test_message'] = str(e) finally: # must go back to messages index test.open(reverse("messages_index")) ########## Message is in store's sentMessages relation. message_in_relation(message_id, 6) ########## Message is visible in page. message_in_page(message_id, 7) ########## Message can be view by clicking on row. message_viewable(message_id, 8) # THIRD ########## Send message. Filter idle. No offer. ### Message limit passed (free) dialog appears. message_id = None try: send_message("idle", "msg #3", "body #3") parts[9]['success'] = test.find("#upgrade") is not None except Exception as e: print e parts[9]['test_message'] = str(e) test.open(reverse("messages_index")) # LIMIT PASSED ########## Upgrading account from the dialog sends the ### message and upgrades the account to middle. try: test.find("#upgrade").click() sleep(2) test.find("#id_cc_cvv").send_keys("123") test.find("#id_recurring").click() test.find("#update-form-submit").click() sleep(5) message_id = test.driver.current_url.split("/")[5] subscription.subscriptionType = None parts[10]['success'] = test.is_current_url(\ reverse("message_details", args=(message_id,))) and\ subscription.get("subscriptionType") == 1 except Exception as e: print e parts[10]['test_message'] = str(e) finally: # must go back to messages index test.open(reverse("messages_index")) # open the mail connection if SeleniumTest.CHECK_SENT_MAIL: mail = Mail() ########## Email is sent notifying user the upgrade. try: parts[11]['success'] = mail.is_mail_sent(\ EMAIL_UPGRADE_SUBJECT) except Exception as e: print e parts[11]['test_message'] = str(e) else: parts[11]['success'] = parts[10]['success'] ########## Message is in store's sentMessages relation. message_in_relation(message_id, 12) ########## Message is visible in page. message_in_page(message_id, 13) ########## Message can be view by clicking on row. message_viewable(message_id, 14) # FOURTH ########## Send message. Filter idle. With offer. message_id = None try: exp_date = timezone.now() + relativedelta(days=1) send_message("idle", "msg #4", "body #4", True, "offer#4", exp_date.strftime(DATE_PICKER_STRFTIME)) parts[15]['success'] = len(test.find(\ "div.notification.success", multiple=True)) > 0 message_id = test.driver.current_url.split("/")[5] except Exception as e: print e parts[15]['test_message'] = str(e) finally: # must go back to messages index test.open(reverse("messages_index")) ########## Message is in store's sentMessages relation. message_in_relation(message_id, 16) ########## Message is visible in page. message_in_page(message_id, 17) ########## Message can be view by clicking on row. message_viewable(message_id, 18) # FIFTH ########## Send message. Filter most_loyal. No offer. ### Message limit passed (free) dialog appears. message_id = None try: send_message("most_loyal", "msg #5", "body #5") parts[19]['success'] = test.find("#upgrade") is not None except Exception as e: print e parts[19]['test_message'] = str(e) test.open(reverse("messages_index")) # LIMIT PASSED ########## Upgrading account from the dialog sends the ### message and upgrades the account to heavy. try: test.find("#upgrade").click() sleep(2) test.find("#id_cc_cvv").send_keys("123") test.find("#id_recurring").click() test.find("#update-form-submit").click() sleep(5) message_id = test.driver.current_url.split("/")[5] subscription.subscriptionType = None parts[20]['success'] = test.is_current_url(\ reverse("message_details", args=(message_id,))) and\ subscription.get("subscriptionType") == 2 except Exception as e: print e parts[20]['test_message'] = str(e) finally: # must go back to messages index test.open(reverse("messages_index")) ########## Email is sent notifying user the upgrade. if SeleniumTest.CHECK_SENT_MAIL: try: parts[21]['success'] = mail.is_mail_sent(\ EMAIL_UPGRADE_SUBJECT) except Exception as e: print e parts[21]['test_message'] = str(e) else: parts[21]['success'] = parts[20]['success'] ########## Message is in store's sentMessages relation. message_in_relation(message_id, 22) ########## Message is visible in page. message_in_page(message_id, 23) ########## Message can be view by clicking on row. message_viewable(message_id, 24) # SIXTH ########## Send message. Filter most_loyal. With offer. message_id = None try: exp_date = timezone.now() + relativedelta(days=1) send_message("most_loyal", "msg #6", "body #6", True, "offer#6", exp_date.strftime(DATE_PICKER_STRFTIME)) parts[25]['success'] = len(test.find(\ "div.notification.success", multiple=True)) > 0 message_id = test.driver.current_url.split("/")[5] except Exception as e: print e parts[25]['test_message'] = str(e) finally: # must go back to messages index test.open(reverse("messages_index")) ########## Message is in store's sentMessages relation. message_in_relation(message_id, 26) ########## Message is visible in page. message_in_page(message_id, 27) ########## Message can be view by clicking on row. message_viewable(message_id, 28) # SEVENTH ########## Send message. Filter all. With offer. message_id = None try: exp_date = timezone.now() + relativedelta(days=1) send_message("all", "msg #7", "body #7", True, "offer#7", exp_date.strftime(DATE_PICKER_STRFTIME)) parts[29]['success'] = len(test.find(\ "div.notification.success", multiple=True)) > 0 message_id = test.driver.current_url.split("/")[5] except Exception as e: print e parts[29]['test_message'] = str(e) finally: # must go back to messages index test.open(reverse("messages_index")) ########## Message is in store's sentMessages relation. message_in_relation(message_id, 30) ########## Message is visible in page. message_in_page(message_id, 31) ########## Message can be view by clicking on row. message_viewable(message_id, 32) # EIGHTH ########## Send message. Filter all. With offer. message_id = None try: exp_date = timezone.now() + relativedelta(days=1) send_message("all", "msg #8", "body #8", True, "offer#8", exp_date.strftime(DATE_PICKER_STRFTIME)) parts[33]['success'] = len(test.find(\ "div.notification.success", multiple=True)) > 0 message_id = test.driver.current_url.split("/")[5] except Exception as e: print e parts[33]['test_message'] = str(e) finally: # must go back to messages index test.open(reverse("messages_index")) ########## Message is in store's sentMessages relation. message_in_relation(message_id, 34) ########## Message is visible in page. message_in_page(message_id, 35) ########## Message can be view by clicking on row. message_viewable(message_id, 36) # NINTH ########## Send message. Filter all. With offer. ### Message limit passed (heavy) dialog appears. message_id = None try: send_message("all", "msg #9", "body #9") parts[37]['success'] = test.element_exists("#maxed_out") except Exception as e: print e parts[37]['test_message'] = str(e) test.open(reverse("messages_index")) # LIMIT PASSED ########## Account can no longer be upgraded. Msg cannot be sent. ### Clicking Okay redirects user to messages index. try: test.find("#maxed_out").click() sleep(1) parts[38]['success'] =\ test.is_current_url(reverse("messages_index")) except Exception as e: print e parts[38]['test_message'] = str(e) test.open(reverse("messages_index")) # # goto edit message page test.find("#create_message").click() sleep(2) selectors = ( ("#id_subject", " "), ("#id_body", " "), ) test.action_chain(0, selectors, action="send_keys") test.find("#send-now").click() sleep(1) ########## Subject is required. try: parts[39]['success'] = test.find("#subject_e ul li").text ==\ "This field is required." except Exception as e: print e parts[39]['test_message'] = str(e) ########## Body is required. try: parts[40]['success'] = test.find("#body_e ul li").text ==\ "This field is required." except Exception as e: print e parts[40]['test_message'] = str(e) ########## Offer title not required if attach offer off. try: parts[41]['success'] = not test.element_exists(\ "#offer_title_e ul li") except Exception as e: print e parts[41]['test_message'] = str(e) ########## Expiration not required if attach offer off. try: parts[42]['success'] = not test.element_exists(\ "#date_offer_expiration_e ul li") except Exception as e: print e parts[42]['test_message'] = str(e) test.find("#id_attach_offer").click() test.find("#send-now").click() sleep(1) ########## Offer title is required if attach offer on. try: parts[43]['success'] =\ test.find("#offer_title_e ul li").text ==\ "Please enter a title." except Exception as e: print e parts[43]['test_message'] = str(e) ########## Expiration date required if attach offer on. try: parts[44]['success'] =\ test.find("#date_offer_expiration_e ul li").text ==\ "Please enter an expiration date." except Exception as e: print e parts[44]['test_message'] = str(e) ########## Expiration date must be at a later date. try: # don't click attach offer again! # test.find("#id_attach_offer").click() exp_date = timezone.now() + relativedelta(days=-1) test.find("#id_date_offer_expiration").send_keys(\ exp_date.strftime(DATE_PICKER_STRFTIME)) test.find("#send-now").click() sleep(1) parts[45]['success'] = test.find(\ "#date_offer_expiration_e ul li").text ==\ "Please enter an expiration date that is later than today." except Exception as e: print e parts[45]['test_message'] = str(e) ########## Expiration date must be at most 1 year later. try: # don't click attach offer again! # test.find("#id_attach_offer").click() exp_date = timezone.now() + relativedelta(days=367) date_offer = test.find("#id_date_offer_expiration") date_offer.clear() date_offer.send_keys(\ exp_date.strftime(DATE_PICKER_STRFTIME)) test.find("#send-now").click() sleep(2) parts[46]['success'] = test.find(\ "#date_offer_expiration_e ul li").text ==\ "Please enter an expiration date that is less than a year." except Exception as e: print e parts[46]['test_message'] = str(e) # END OF ALL TESTS - cleanup if SeleniumTest.CHECK_SENT_MAIL: mail.logout() return test.tear_down()
def test_rewards(): # setup account = Account.objects().get(username=TEST_USER['username'], include="Store") store = account.store # start with no rewards store.rewards = [] store.update() test = SeleniumTest() parts = [ {'test_name': "User needs to be logged in to access page"}, {'test_name': "Having no rewards shows a placeholder row"}, {'test_name': "Adding a reward works"}, {'test_name': "The new reward is saved to parse"}, {'test_name': "Redemption count starts at 0"}, {'test_name': "Reward id starts at 0"}, {'test_name': "Updating a reward works"}, {'test_name': "The updated reward is saved to parse"}, {'test_name': "The updated reward retains the reward_id"}, {'test_name': "The updated reward retains the " +\ "redemption_count"}, {'test_name': "Clicking delete brings up a confirmation " +\ "dialog"}, {'test_name': "Deleting a reward works"}, {'test_name': "The deleted reward is deleted from parse"}, {'test_name': "Reward name is required"}, {'test_name': "Punches is required"}, {'test_name': "Description is not required"}, {'test_name': "Clicking cancel redirects user " +\ "back to rewards index"}, {'test_name': "Punches must be a number"}, {'test_name': "Punches must be greater than 0"}, {'test_name': "Rewards are initially sorted by Punches " +\ "from least to greatest"}, {'test_name': "Punches is sortable"}, {'test_name': "Name is sortable"}, {'test_name': "Updating a reward with the reset redemption" +\ " count option resets the redemption count to 0"}, ] section = { "section_name": "Rewards page working properly?", "parts": parts, } test.results.append(section) ########## User needs to be logged in to access page test.open(reverse("rewards_index")) # ACTION! sleep(1) parts[0]['success'] = test.is_current_url(reverse(\ 'manage_login') + "?next=" + reverse("rewards_index")) # login selectors = (("#login_username", TEST_USER['username']), ("#login_password", TEST_USER['password']), ("", Keys.RETURN)) test.action_chain(0, selectors, "send_keys") # ACTION! sleep(7) ########## Having no rewards shows a placeholder row try: parts[1]['success'] =\ test.find("//div[@id='rewards_section']/div[@class=" +\ "'tr reward']/div[@class='td reward_summary']/span[1]", type="xpath").text == "No Rewards" except Exception as e: print e parts[1]['test_message'] = str(e) def add_reward(name, description, punches): test.find("#add_reward").click() sleep(1) selectors = ( ("#id_reward_name", name), ("#id_description", description), ("#id_punches", str(punches)), ) test.action_chain(0, selectors, action="send_keys") test.find("#submit-reward-form").click() sleep(5) reward1_name = "Reward #1" reward1_description = "First reward" reward1_punches = 5 ########## Adding a reward works try: add_reward(reward1_name, reward1_description, reward1_punches) parts[2]['success'] = test.find("//div[@id='0']/a/div[2]" +\ "/span[1]", type="xpath").text == reward1_name except Exception as e: print e parts[2]['test_message'] = str(e) ########## The new reward is saved to parse try: store.rewards = None reward = store.get("rewards")[0] parts[3]['success'] = reward['reward_name'] ==\ reward1_name and reward['description'] ==\ reward1_description and\ reward['punches'] == reward1_punches except Exception as e: print e parts[3]['test_message'] = str(e) ########## Redemption count starts at 0 try: parts[4]['success'] = reward['redemption_count'] == 0 except Exception as e: print e parts[4]['test_message'] = str(e) ########## Redemption count starts at 0 try: parts[5]['success'] = reward['reward_id'] == 0 except Exception as e: print e parts[5]['test_message'] = str(e) # let's add 3 more rewards! reward2_name = "reward dos" reward2_description = "DOS" reward2_punches = 10 reward3_name = "reward tres" reward3_description = "TRES" reward3_punches = 12 reward4_name = "reward quatro" reward4_description = "QUATRO" reward4_punches = 15 add_reward(reward2_name, reward2_description, reward2_punches) add_reward(reward3_name, reward3_description, reward3_punches) add_reward(reward4_name, reward4_description, reward4_punches) ################ reward1_name = "reward uno" reward1_description = "UNO" reward1_punches = 1 ########## Updating a reward works try: test.find("//div[@id='0']/a", type="xpath").click() sleep(1) selectors = ( ("#id_reward_name", reward1_name), ("#id_description", reward1_description), ("#id_punches", str(reward1_punches)), ) test.action_chain(0, selectors, action="clear") test.action_chain(0, selectors, action="send_keys") test.find("#submit-reward-form").click() sleep(4) parts[6]['success'] = test.find("//div[@id='0']/a/div[2]" +\ "/span[1]", type="xpath").text == reward1_name except Exception as e: print e parts[6]['test_message'] = str(e) ########## The updated reward is saved to parse try: store.rewards = None reward = store.get("rewards")[0] # list is sorted by punches parts[7]['success'] = reward['reward_name'] ==\ reward1_name and\ reward['description'] == reward1_description and\ reward['punches'] == reward1_punches except Exception as e: print e parts[7]['test_message'] = str(e) ########## The updated reward retains the reward_id try: parts[8]['success'] = reward['reward_id'] == 0 except Exception as e: print e parts[8]['test_message'] = str(e) ########## The updated reward retains the redemption_count try: parts[9]['success'] = reward['redemption_count'] == 0 except Exception as e: print e parts[9]['test_message'] = str(e) ########## Clicking delete brings up a confirmation dialog try: test.find("//div[@id='0']/a", type="xpath").click() sleep(1) test.find("#delete-link").click() alert = test.switch_to_alert() parts[10]['success'] = alert is not None except Exception as e: print e parts[10]['test_message'] = str(e) ########## Deleting a reward works try: alert.accept() sleep(5) parts[11]['success'] = test.find("//div[@id='1']/a/div[2]" +\ "/span[1]", type="xpath").text == reward2_name try: # first reward should be gone test.find("//div[@id='0']/a/div[2]" +\ "/span[1]", type="xpath").text except Exception: parts[11]['success'] = parts[11]['success'] else: parts[11]['success'] = False except Exception as e: print e parts[11]['test_message'] = str(e) ########## The deleted reward is deleted from parse try: store.rewards = None rewards = store.get("rewards") parts[12]['success'] = reward1_name not in\ [r['reward_name'] for r in rewards] and\ 0 not in [r['reward_id'] for r in rewards] except Exception as e: print e parts[12]['test_message'] = str(e) # field required add_reward(" ", "", "") ########## Reward name is required try: parts[13]['success'] =\ test.find("#reward_name_ic ul li").text ==\ "This field is required." except Exception as e: print e parts[13]['test_message'] = str(e) ########## Punches is required try: parts[14]['success'] =\ test.find("#punches_ic ul li").text ==\ "This field is required." except Exception as e: print e parts[14]['test_message'] = str(e) ########## Description is not required try: parts[15]['success'] =\ not test.element_exists("#description_ic ul li") except Exception as e: print e parts[15]['test_message'] = str(e) ########## Clicking cancel redirects user back to rewards index try: test.find("//div [@id='edit-reward-options']/a[2]", type="xpath").click() sleep(2) parts[16]['success'] =\ test.is_current_url(reverse("rewards_index")) except Exception as e: print e parts[16]['test_message'] = str(e) ########## Punches must be a number try: add_reward("", "", "ba") parts[17]['success'] =\ test.find("#punches_ic ul li").text ==\ "Enter a whole number." test.find("//div [@id='edit-reward-options']/a[2]", type="xpath").click() sleep(2) except Exception as e: print e parts[17]['test_message'] = str(e) ########## Punches must be greater than 0 try: add_reward("", "", "0") parts[18]['success'] =\ test.find("#punches_ic ul li").text ==\ "Ensure this value is greater than or equal to 1." test.find("//div [@id='edit-reward-options']/a[2]", type="xpath").click() sleep(2) except Exception as e: print e parts[18]['test_message'] = str(e) ########## Rewards are initially sorted by Punchess ### from least to greatest try: store.rewards = None rewards = store.get('rewards') punches_map = {r['punches']: r for r in rewards} ascending = [r['punches'] for r in rewards] descending = ascending[:] ascending.sort() descending.sort(reverse=True) success = True for i in range(3): if int( test.find("//div[@id='%s']/a/div[1]" % (str(i + 1), ), type="xpath").text) != ascending[i]: success = False break parts[19]['success'] = success except Exception as e: print e parts[19]['test_message'] = str(e) ########## Punches is sortable try: test.find("#header-reward_punches").click() rows = test.find("//div[@id='rewards_section']/" +\ "div[contains(@class, 'reward')]", type="xpath", multiple=True) if len(rows) == len(descending): success = True for i in range(len(rows)): # check both the punches and id row = rows[i] if int(row.text.split("\n")[0]) !=\ descending[i] or int(row.get_attribute("id")) !=\ punches_map[descending[i]]['reward_id']: success = False break parts[20]['success'] = success except Exception as e: print e parts[20]['test_message'] = str(e) ########## Name is sortable try: name_map = {r['reward_name']: r for r in rewards} ascending = [r['reward_name'] for r in rewards] descending = ascending[:] ascending.sort() descending.sort(reverse=True) # ascending order test.find("#header-reward_summary").click() rows = test.find("//div[@id='rewards_section']/" +\ "div[contains(@class, 'reward')]", type="xpath", multiple=True) if len(rows) == len(ascending): success1 = True for i in range(len(rows)): # check both the name and id row = rows[i] if row.text.split("\n")[1] !=\ ascending[i] or int(row.get_attribute("id")) !=\ name_map[ascending[i]]['reward_id']: success1 = False break # descending order test.find("#header-reward_summary").click() rows = test.find("//div[@id='rewards_section']/" +\ "div[contains(@class, 'reward')]", type="xpath", multiple=True) if len(rows) == len(descending): success2 = True for i in range(len(rows)): # check both the name and id row = rows[i] if row.text.split("\n")[1] !=\ descending[i] or int(row.get_attribute("id")) !=\ name_map[descending[i]]['reward_id']: success2 = False break parts[21]['success'] = success1 and success2 except Exception as e: print e parts[21]['test_message'] = str(e) ########## Updating a reward with the reset redemption ### count option resets the redemption count to 0 try: # logout test.logout() test.dev_login() sleep(1) # modify store the rewards redemption count store.rewards = None for r in store.get("rewards"): r['redemption_count'] = 99 store.update() # login test.open(reverse("rewards_index")) # ACTION! sleep(1) selectors = (("#login_username", TEST_USER['username']), ("#login_password", TEST_USER['password']), ("", Keys.RETURN)) test.action_chain(0, selectors, "send_keys") # ACTION! sleep(7) # now test test.find("//div[@id='2']/a", type="xpath").click() sleep(1) red_count = int(test.find("#redemption_count").text.split(" ")[1]) test.find("#reset_red_count").click() test.find("#submit-reward-form").click() sleep(5) test.find("//div[@id='2']/a", type="xpath").click() new_red_count =\ int(test.find("#redemption_count").text.split(" ")[1]) parts[22]['success'] = new_red_count == 0 and red_count !=\ new_red_count except Exception as e: print e parts[22]['test_message'] = str(e) # END OF ALL TESTS - cleanup return test.tear_down()
def create_random_stores(self, amount): for i in range(amount): print "Creating store %s" % (str(i),) # create the store street, city, state, zip, country, phone_number =\ self.addrs[i].split(", ") first_name, last_name = self.owners[i].split(" ") neighborhood = self.neighborhoods[i] store_name = self.stores[i] store_i = STORE.copy() store_location_i = STORE_LOCATION.copy() # create the thumbnaiil and cover (same image different size) self.get_store_location_image(i) thumbnail = create_png(TMP_IMG_PATH, IMAGE_THUMBNAIL_SIZE) while "error" in image: print "Retrying create_png" thumbnail = create_png(TMP_IMG_PATH) cover = create_png(TMP_IMG_PATH) while "error" in cover: print "Retrying create_png" cover = create_png(TMP_IMG_PATH) store_i.update({ "store_name": store_name, "first_name": first_name, "last_name": last_name, "thumbnail_image": thumbnail.get("name"), "cover_image": cover.get("name"), }) store_location_i.update({ "street": street, "city": city, "state": state, "zip": zip, "neighborhood": neighborhood, "country": country, "phone_number": phone_number, "coordinates": self.get_random_coordinates(), }) # create the store store = Store.objects().create(**store_i) # create the store location store_location = StoreLocation(**store_location_i) store_location.Store = store.objectId store_location.update() # create the settings settings = Settings.objects().create(Store=store.objectId) # create the subscription subscription =\ Subscription.objects().create(Store=store.objectId, date_last_billed=timezone.now()) # create the user email = first_name+str(randint(0, 99))+USER_EMAIL_POSTFIX email = email.lower() acc = Account.objects().create(\ username=email, email=email, password=USER_PASSWORD, Store=store.objectId) if not acc.objectId: raise Exception("Account creation failed.") # link the store store.Settings = settings.objectId store.Subscription = subscription.objectId store.owner_id = acc.objectId store.ACL[acc.objectId] = {"read": True,"write": True} store.store_locations = [store_location] store.update()
def test_employees(): """ Tests for employee approve, deny, remove, details. etc. """ # TODO test employee graph # TODO test employee punches history # delete the employees and associated User objects in the relation account = Account.objects().get(username=TEST_USER['username'], include="Store.Settings") store = account.store settings = store.settings emps = store.get("employees") if emps: for emp in emps: Account.objects().get(Employee=emp.objectId).delete() emp.delete() store.set("employees", None) test = SeleniumTest() parts = [ {'test_name': "User needs to be logged in to access page"}, {'test_name': "Cloud code register_employee works"}, {'test_name': "Employee is saved in store's Employees " +\ "relation"}, {'test_name': "Employee is initially pending (Parse)"}, {'test_name': "Employee is initially pending (Dashboard)"}, {'test_name': "Email must be valid (cloud code)"}, {'test_name': "Email must be unique (cloud code)"}, {'test_name': "Username must be unique (cloud code)"}, {'test_name': "Retailer PIN must exist (cloud code)"}, {'test_name': "Clicking deny prompts the user to confirm"}, {'test_name': "The user is redirected to employee index"}, {'test_name': "The denied employee is removed from the " +\ "pending table"}, {'test_name': "The employee is deleted from parse"}, {'test_name': "The account/user is deleted from parse"}, {'test_name': "Approving the employee moves it from " +\ "pending to approved"}, {'test_name': "Employee status is set to approved in Parse"}, {'test_name': "Employee initially has 0 punches."}, {'test_name': "Clicking on the approved employee row " +\ " redirects user to employee edit page"}, {'test_name': "Clicking delete prompts the user to confirm"}, {'test_name': "The user is redirected to employee index"}, {'test_name': "The deleted employee is removed from the " +\ "pending table"}, {'test_name': "The employee is deleted from parse"}, {'test_name': "The account/user is deleted from parse"}, {'test_name': "Multiple employees (3) registering at once" +\ " shows up in dashboard"}, ] section = { "section_name": "Employees page working properly?", "parts": parts, } test.results.append(section) ########## User needs to be logged in to access page" test.open(reverse("employees_index")) # ACTION! sleep(1) parts[0]['success'] = test.is_current_url(reverse(\ 'manage_login') + "?next=" + reverse("employees_index")) # login selectors = ( ("#login_username", TEST_USER['username']), ("#login_password", TEST_USER['password']), ("", Keys.RETURN) ) test.action_chain(0, selectors, "send_keys") # ACTION! sleep(7) def register_employee(first_name, last_name, username=None, password=None, email=None, retailer_pin=None): if username is None: username = first_name if password is None: password = first_name if email is None: email = first_name + "@" + last_name + ".com" if retailer_pin is None: retailer_pin = settings.retailer_pin return cloud_call("register_employee", { "first_name": first_name, "last_name": last_name, "username": username, "password": password, "email": email, "retailer_pin": retailer_pin, }) first_name, last_name, username, email =\ "vandolf1", "estrellado", "*****@*****.**", "*****@*****.**" ########## Cloud code register_employee works" try: res = register_employee(first_name, last_name, username, email=email) parts[1]['success'] = "error" not in res except Exception as e: print e parts[1]['test_message'] = str(e) ########## Employee is saved in store's Employees relation" try: emp = store.get("employees")[0] parts[2]['success'] = emp is not None except Exception as e: print e parts[2]['test_message'] = str(e) ########## Employee is initially pending (Parse)" try: parts[3]['success'] = emp.status == PENDING except Exception as e: print e parts[3]['test_message'] = str(e) ########## Employee is initially pending (Dashboard)" try: sleep(COMET_PULL_RATE*2 + 1) # wait for dashboard to receive test.find("#tab-pending-employees").click() parts[4]['success'] = test.element_exists(\ "#tab-body-pending-employees div.tr div.td.approve") except Exception as e: print e parts[4]['test_message'] = str(e) ########## Email must be valid (cloud code)" try: res = register_employee("vman", "vman", email="vmahs@vman") parts[5]['success'] = res['error'] == 'EMAIL_INVALID' except Exception as e: print e parts[5]['test_message'] = str(e) ########## Email must be unique (cloud code) " try: res = register_employee("vman", "vman", email=email) parts[6]['success'] = res['error'] == 'EMAIL_TAKEN' except Exception as e: print e parts[6]['test_message'] = str(e) ########## Username must be unique (cloud code) " try: res = register_employee("vman", "vman", username=username) parts[7]['success'] = res['error'] == 'USERNAME_TAKEN' except Exception as e: print e parts[7]['test_message'] = str(e) ########## Retailer PIN must exist (cloud code) " try: res = register_employee("vman", "vman", retailer_pin="sdgdgs") parts[8]['success'] = res['error'] == "RETAILER_PIN_INVALID" except Exception as e: print e parts[8]['test_message'] = str(e) ########## Clicking deny prompts the user to confirm" try: test.find("#tab-pending-employees").click() test.find("#tab-body-pending-employees div.tr " +\ "div.td.approve a.deny").click() alert = test.switch_to_alert() parts[9]['success'] = alert.text == "Deny employee?" except Exception as e: print e parts[9]['test_message'] = str(e) ########## The user is redirected to employee index" try: sleep(1) alert.accept() sleep(2) parts[10]['success'] = test.is_current_url(reverse(\ "employees_index") +\ "?show_pending&success=Employee+has+been+denied.") except Exception as e: print e parts[10]['test_message'] = str(e) ########## The denied employee is removed from the pending table" try: parts[11]['success'] = not test.element_exists(\ "#tab-body-pending-employees div.tr " +\ "div.td.approve a.approve") except Exception as e: print e parts[11]['test_message'] = str(e) ########## The employee is deleted from parse" try: store.set("employees", None) parts[12]['success'] = store.get("employees",\ first_name=first_name, last_name=last_name, count=1) == 0 except Exception as e: print e parts[12]['test_message'] = str(e) ########## The account/user is deleted from parse" try: parts[13]['success'] = Account.objects().count(\ username=username, email=email) == 0 except Exception as e: print e parts[13]['test_message'] = str(e) ########## Approving the employee moves it from pending to approved" try: register_employee(first_name, last_name, username, email=email) sleep(COMET_PULL_RATE*2 + 1) # wait for dashboard to receive test.find("#tab-pending-employees").click() approveRow = test.find("#tab-body-pending-employees " +\ "div.tr") approveId = approveRow.get_attribute("id") approveRow.find_element_by_css_selector(\ "div.td.approve a.approve").click() sleep(1) test.switch_to_alert().accept() sleep(2) test.find("#tab-approved-employees").click() parts[14]['success'] = test.element_exists(\ "#tab-body-approved-employees " +\ "div.tr div.td.remove a") except Exception as e: print e parts[14]['test_message'] = str(e) ########## Employee status is set to approved in Parse" try: store.set("employees", None) emp = store.get("employees", first_name=first_name, last_name=last_name)[0] parts[15]['success'] = emp.status == APPROVED except Exception as e: print e parts[15]['test_message'] = str(e) ########## Employee initially has 0 punches" try: parts[16]['success'] = emp.lifetime_punches == 0 except Exception as e: print e parts[16]['test_message'] = str(e) ########## Clicking on the approved employee row ### redirects user to employee edit page try: test.find("#tab-approved-employees").click() test.find("#tab-body-approved-employees div#%s a" %\ (emp.objectId,)).click() sleep(1) parts[17]['success'] = test.is_current_url(reverse(\ "employee_edit", args=(emp.objectId,))) except Exception as e: print e parts[17]['test_message'] = str(e) ########## Clicking delete prompts the user to confirm" try: test.find("#delete-button").click() alert = test.switch_to_alert() parts[18]['success'] = alert.text ==\ "Are you sure you want to delete this employee?" except Exception as e: print e parts[18]['test_message'] = str(e) ########## The user is redirected to employee index" try: sleep(1) alert.accept() sleep(2) parts[19]['success'] = test.is_current_url(reverse(\ "employees_index") +\ "?success=Employee+has+been+deleted.") except Exception as e: print e parts[19]['test_message'] = str(e) ########## The deleted employee is removed from the pending table" try: parts[20]['success'] = not test.element_exists(\ "#tab-body-approved-employees div#%s a" %(emp.objectId,)) except Exception as e: print e parts[20]['test_message'] = str(e) ########## The employee is deleted from parse" try: store.set("employees", None) parts[21]['success'] = store.get("employees", objectId=emp.objectId, count=1) == 0 except Exception as e: print e parts[21]['test_message'] = str(e) ########## The account/user is deleted from parse" try: parts[22]['success'] = Account.objects().count(\ username=username, email=email) == 0 except Exception as e: print e parts[22]['test_message'] = str(e) ########## Multiple employees (4) registering at once ### shows up in dashboard" try: for i in range(3): register_employee(first_name + str(i), last_name + str(i)) sleep(COMET_PULL_RATE*2 + 3) test.find("#tab-pending-employees").click() parts[23]['success'] = len(test.find(\ "#tab-body-pending-employees div.tr " +\ "div.td.approve a.approve", multiple=True)) == 3 except Exception as e: print e parts[23]['test_message'] = str(e) # END OF ALL TESTS - cleanup return test.tear_down()
def handle(self, *args, **options): # for logging when ran by CRON print "Running detect_suspicious_activity: " + str(timezone.now()) # first count the number of active stores store_count = Store.objects().count(active=True) # store_count = Store.objects().count(objectId="o72LmDy0YK") end = timezone.now() start = end + relativedelta(hours=-24) conn = mail.get_connection(fail_silently=(not DEBUG)) conn.open() # to send to the admins admin_chunks = [] # get 500 stores at a time LIMIT, skip = 500, 0 while store_count > 0: for store in Store.objects().filter(active=True, include="store_locations", limit=LIMIT, skip=skip, order="createdAt"): # for store in Store.objects().filter(\ # objectId="o72LmDy0YK", include="store_locations"): ### CHUNK1 #################################### chunk1, account_patron, patron_punch = {}, {}, {} total_punches = [] # check approved EMPLOYEES employees = store.get("employees", status=APPROVED, limit=900) employee_punches = [] def add_to_patron_punch(punch, employee=None): if punch.Patron not in patron_punch: patron_punch[punch.Patron] =\ [{"punch":punch, "employee": employee}] else: patron_punch[punch.Patron].append({"punch":\ punch, "employee":employee}) def get_location(location_id): for loc in store.store_locations: if loc.objectId == location_id: return loc if employees and len(employees) > 0: # check all the punches of each employee for employee in employees: # get all the punches for today punches = employee.get("punches", limit=900, createdAt__lte=end, createdAt__gte=start) if not punches: continue # for querying the dashboard punches employee_punches.extend([p.objectId for p in\ punches]) # group the punches by patron for punch in punches: add_to_patron_punch(punch, employee) # now check DASHBOARD punches = store.get("punches", limit=900, createdAt__lte=end, createdAt__gte=start, objectId__nin=employee_punches) # group the punches by patron if punches: for punch in punches: add_to_patron_punch(punch, None) # check for a group with a list >= 6 for key, val in patron_punch.iteritems(): suspicious_punches = [] if val and len(val) >= 6: for punch in val: suspicious_punches.append({ "store_location":\ get_location(punch["punch"].store_location_id), "punch": punch["punch"], "employee": punch["employee"] }) # cache the account and patron if key not in account_patron: acc = Account.objects().get(Patron=key, include="Patron") account_patron[key] = { "account": acc, "patron": acc.patron, } if key not in chunk1: chunk1[key] = { "account":\ account_patron[key]['account'], "patron":\ account_patron[key]['patron'], "punches": suspicious_punches } else: chunk1[key]['punches'].extend(suspicious_punches) ### CHUNK2 #################################### # hours per location # punches are still grouped per patron chunk2 = {} for loc in store.store_locations: if loc.hours and len(loc.hours) > 0 and\ loc.hours[0]['day'] != 0: # 24/7 # check for punches out of hours tz = pytz.timezone(loc.store_timezone) start = timezone.localtime(start, tz) end = timezone.localtime(end, tz) # isoweekday is from 1-7 monday to sunday # convert to 1-7 sunday to saturday day1_weekday = (start.isoweekday()) % 7 + 1 day2_weekday = (end.isoweekday()) % 7 + 1 # get the hours for day1 and day2 def get_hours_range(weekday, d): for hr in loc.hours: if hr["day"] == weekday: hr_start_hour =\ int(hr["open_time"][:2]) hr_start_minute =\ int(hr["open_time"][2:]) hr_end_hour =\ int(hr["close_time"][:2]) hr_end_minute =\ int(hr["close_time"][2:]) return d.replace(hour=hr_start_hour, minute=hr_start_minute),\ d.replace(hour=hr_end_hour, minute=hr_end_minute) return None, None (hours1_start, hours1_end) =\ get_hours_range(day1_weekday, start) (hours2_start, hours2_end) =\ get_hours_range(day2_weekday, end) # now convert to utc since punch times are in utc if hours1_start: hours1_start =\ timezone.localtime(hours1_start, tzutc()) hours1_end =\ timezone.localtime(hours1_end, tzutc()) if hours2_start: hours2_start =\ timezone.localtime(hours2_start, tzutc()) hours2_end =\ timezone.localtime(hours2_end, tzutc()) for key, val in patron_punch.iteritems(): if not val: continue suspicious_punches = [] # process only those punches that are in this location for p in [ x for x in val if x["punch"].store_location_id == loc.objectId ]: punch = p["punch"] # suspicious if not in hours1 and 2 if not (hours1_start and\ punch.createdAt>hours1_start and\ punch.createdAt<hours1_end) and\ not (hours2_start and\ punch.createdAt>hours2_start and\ punch.createdAt<hours2_end): # not in hours1 or 2 so suspicious! suspicious_punches.append({ "store_location": loc, "punch": punch, "employee": p["employee"], }) if len(suspicious_punches) == 0: continue # cache the account and patron if key not in account_patron: acc = Account.objects().get(Patron=key, include="Patron") account_patron[key] = { "account": acc, "patron": acc.patron, } if key not in chunk2: chunk2[key] = { "account":\ account_patron[key]['account'], "patron":\ account_patron[key]['patron'], "punches": suspicious_punches } else: chunk2[key]['punches'].extend( suspicious_punches) # all tasks are done for this store - send email if len(chunk1) > 0 or len(chunk2) > 0: store_acc = Account.objects().get(Store=store.objectId) admin_chunks.append({ "store_acc": store_acc, "store": store, "data": (chunk1, chunk2), }) try: send_email_suspicious_activity(store_acc, store, chunk1, chunk2, conn) except SMTPServerDisconnected: conn = mail.get_connection(fail_silently=(not DEBUG)) conn.open() send_email_suspicious_activity(store_acc, store, chunk1, chunk2, conn) # end of while loop store_count -= LIMIT skip += LIMIT if len(admin_chunks) > 0: send_email_suspicious_activity_admin(admin_chunks, start, end, conn) # everything is done. close the connection try: conn.close() except Exception: pass
def test_employee_access(): """ Tests for employee dashboard access. This tests any user with ACL of ACCESS_PUNCHREDEEM and NO_ACCESS. Tests for ACCESS_ADMIN are not necessary since all other tests involving the store owner covers it. """ # delete the employees and associated User objects in the relation account = Account.objects().get(email=TEST_USER['username'], include="Store.Settings") store = account.store settings = store.settings emps = store.get("employees") if emps: for emp in emps: Account.objects().get(Employee=emp.objectId).delete() emp.delete() store.set("employees", None) test = SeleniumTest() parts = [ {"test_name" : "Pending employee has no access"}, {"test_name" : "Approved employee initially not in store ACL"}, {"test_name" : "Employee with ACCESS_NONE cannot login " +\ "using the login dialog"}, {"test_name" : "Employee with ACCESS_NONE cannot login " +\ "using the dedicated login page"}, {"test_name" : "Employee with ACCESS_PUNCHREDEEM can " +\ "login to the dashboard through the login dialog"}, {"test_name" : "Employee with ACCESS_PUNCHREDEEM can " +\ "login to the dashboard through the dedicated dialog pg"}, {"test_name" : "Account settings accessible"}, {"test_name" : "No store edit button"}, {"test_name" : "Requesting edit store detail through url " +\ "redirects user to store index"}, {"test_name" : "No update subscription button"}, {"test_name" : "Requesting update subscription through url " +\ "redirects user to store index"}, {"test_name" : "No deactivate my store button"}, {"test_name" : "Requesting store deactivation through url " +\ "redirects user to store index"}, {"test_name" : "Rewards accessible"}, {"test_name" : "No create new reward button"}, {"test_name" : "Requesting create new reward through url " +\ "redirects user to rewards index"}, {"test_name" : "Rewards are not clickable"}, {"test_name" : "Requesting edit reward through url " +\ "redirects user to rewards index"}, {"test_name" : "Messages accessible"}, {"test_name" : "No create new message button"}, {"test_name" : "Requesting create new message through url " +\ "redirects user to messages index"}, {"test_name" : "Sent messages are viewable"}, {"test_name" : "Feedbacks are viewable"}, {"test_name" : "No reply button"}, {"test_name" : "Requesting reply through url " +\ "redirects user to messages index"}, {"test_name" : "No delete message button"}, {"test_name" : "Requesting delete message through url " +\ "redirects user to messages index"}, {"test_name" : "Analysis accessible"}, {"test_name" : "Employees accessible"}, {"test_name" : "No register new employee button"}, {"test_name" : "Requesting new employee registration"+\ "redirects user to employees_index"}, {"test_name" : "Approved employees are not clickable"}, {"test_name" : "Requesting edit employee through url " +\ "redirects user to employees index"}, {"test_name" : "No remove button in approved employees"}, {"test_name" : "Requesting remove employee through url " +\ "redirects user to employees index"}, {"test_name" : "No deny button in pending employees"}, {"test_name" : "Requesting deny employee through url " +\ "redirects user to employees index"}, {"test_name" : "No approve button in pending employees"}, {"test_name" : "Requesting approve employee through url " +\ "redirects user to employees index"}, {"test_name" : "Settings accessible"}, {"test_name" : "No refresh button for retailer pin"}, {"test_name" : "Requesting refresh through url returns " +\ "a json object with error Permission denied"}, {"test_name" : "Punches employee is readonly"}, {"test_name" : "Punches facebook is readonly"}, {"test_name" : "No save button"}, {"test_name" : "No cancel changes button"}, {"test_name" : "Workbench accessible"}, {"test_name" : "Employee can punch"}, {"test_name" : "Employee can reject redeem"}, {"test_name" : "Employee can validate redeem"}, ] section = { "section_name":\ "Employee dashboard access working as expected?", "parts": parts, } test.results.append(section) try: # register the test employee register_employee("employee", "ex", TEST_EMPLOYEE['username'], TEST_EMPLOYEE['username'], TEST_EMPLOYEE['password'], settings.retailer_pin) sleep(3) employee_acc = Account.objects().get(username=TEST_EMPLOYEE[\ 'username'], include="Employee") employee = employee_acc.employee except Exception as e: print e ########## Pending employee has no access try: test.login(TEST_EMPLOYEE['username'], TEST_EMPLOYEE['password'], reverse("employees_index")) parts[0]['success'] =\ test.find("#dialog-login-message").text ==\ "You are not yet approved." except Exception as e: print e parts[0]['test_message'] = str(e) try: # login to approve test.login(TEST_USER['username'], TEST_USER['password'], reverse("employees_index")) # approve test.find("#tab-pending-employees").click() approveRow = test.find("#tab-body-pending-employees " +\ "div.tr") approveRow.find_element_by_css_selector(\ "div.td.approve a.approve").click() sleep(1) test.switch_to_alert().accept() sleep(2) test.logout() except Exception as e: print e ########## Approved employee initially not in store ACL try: # store.ACL = None cannot retrieve Parse built-ins! account = Account.objects().get(email=TEST_USER['username'], include="Store.Settings") store = account.store parts[1]['success'] = employee_acc.objectId not in store.ACL except Exception as e: print e parts[1]['test_message'] = str(e) ########## Employee with ACCESS_NONE cannot login ### using the login dialog try: test.dev_login() test.login(TEST_EMPLOYEE['username'], TEST_EMPLOYEE['password']) parts[2]['success'] =\ test.find("#dialog-login-message").text ==\ "You do not have permission to access the dashboard." except Exception as e: print e parts[2]['test_message'] = str(e) ########## Employee with ACCESS_NONE cannot login ### using the dedicated login page try: test.login(TEST_EMPLOYEE['username'], TEST_EMPLOYEE['password'], reverse("employees_index")) parts[3]['success'] =\ test.find("#dialog-login-message").text ==\ "You do not have permission to access the dashboard." except Exception as e: print e parts[3]['test_message'] = str(e) ### Update the store's ACL store.ACL = {"*": {"read": True, "write": True}} store.set_access_level(employee_acc, ACCESS_PUNCHREDEEM[0]) store.update() test.new_driver(False) ########## Employee with ACCESS_PUNCHREDEEM can ### login to the dashboard through the login dialog try: test.login(TEST_EMPLOYEE['username'], TEST_EMPLOYEE['password'], final_sleep=6) parts[4]['success'] = test.is_current_url(reverse("store_index")) test.logout() except Exception as e: print e parts[4]['test_message'] = str(e) ########## Employee with ACCESS_PUNCHREDEEM can ### login to the dashboard through the dedicated dialog page try: test.dev_login() test.login(TEST_EMPLOYEE['username'], TEST_EMPLOYEE['password'], reverse("employees_index"), final_sleep=6) parts[5]['success'] = test.is_current_url(reverse("employees_index")) sleep(4) except Exception as e: print e parts[5]['test_message'] = str(e) ########## Account settings accessible try: test.find("#header-right a").click() sleep(2) parts[6]['success'] = test.is_current_url(reverse("account_edit")) except Exception as e: print e parts[6]['test_message'] = str(e) ########## No edit store detail button try: test.open(reverse("store_index")) sleep(2) test.set_to_implicit_wait(False) try: test.find("#store-details a[href='%s']" % (reverse("store_edit"),)) except NoSuchElementException: parts[7]['success'] = True except Exception as e: print e parts[7]['test_message'] = str(e) finally: test.set_to_implicit_wait(True) ########## Requesting edit store detail through url ### redirects user to store details try: test.open(reverse("store_edit")) sleep(2) parts[8]['success'] = test.is_current_url(reverse("store_index")+\ "?" + urlencode({'error': "Permission denied"})) except Exception as e: print e parts[8]['test_message'] = str(e) ########## No update subscription button try: test.set_to_implicit_wait(False) try: test.find("#account-options a[href='%s']" %\ (reverse("subscription_update"),)) except NoSuchElementException: parts[9]['success'] = True except Exception as e: print e parts[9]['test_message'] = str(e) finally: test.set_to_implicit_wait(True) ########## Requesting update subscription through url ### redirects user to store index try: test.open(reverse("subscription_update")) sleep(2) parts[10]['success'] = test.is_current_url(reverse(\ "store_index")+ "?" + urlencode({'error':\ "Permission denied"})) except Exception as e: print e parts[10]['test_message'] = str(e) ########## No deactivate my store button try: test.set_to_implicit_wait(False) try: test.find("#deactivate_account") except NoSuchElementException: parts[11]['success'] = True except Exception as e: print e parts[11]['test_message'] = str(e) finally: test.set_to_implicit_wait(True) ########## Requesting store deactivation through url ### redirects user to store index try: test.open(reverse("store_deactivate")) sleep(2) parts[12]['success'] = test.is_current_url(reverse(\ "store_index")+ "?" + urlencode({'error':\ "Permission denied"})) except Exception as e: print e parts[12]['test_message'] = str(e) ########## Rewards accessible try: test.open(reverse("rewards_index")) sleep(2) parts[13]['success'] = test.is_current_url(reverse("rewards_index")) except Exception as e: print e parts[13]['test_message'] = str(e) ########## No create new reward button try: test.set_to_implicit_wait(False) try: test.find("#add_reward") except NoSuchElementException: parts[14]['success'] = True except Exception as e: print e parts[14]['test_message'] = str(e) finally: test.set_to_implicit_wait(True) ########## Requesting create new reward through url ### redirects user to rewards index try: test.open(reverse("reward_edit", args=(-1,))) sleep(2) parts[15]['success'] = test.is_current_url(reverse(\ "rewards_index")+ "?" + urlencode({'error':\ "Permission denied"})) except Exception as e: print e parts[15]['test_message'] = str(e) ########## Rewards are not clickable try: test.set_to_implicit_wait(False) try: test.find("div.tr.reward a") except NoSuchElementException: parts[16]['success'] = True except Exception as e: print e parts[16]['test_message'] = str(e) finally: test.set_to_implicit_wait(True) ########## Requesting edit reward through url ### redirects user to rewards index try: test.open(reverse("reward_edit", args=\ (int(test.find("div.tr.reward").get_attribute("id")),))) sleep(3) parts[17]['success'] = test.is_current_url(reverse(\ "rewards_index")+ "?" + urlencode({'error':\ "Permission denied"})) except Exception as e: print e parts[17]['test_message'] = str(e) ########## Messages accessible try: test.open(reverse("messages_index")) sleep(3) parts[18]['success'] = test.is_current_url(reverse(\ "messages_index")) except Exception as e: print e parts[18]['test_message'] = str(e) ########## No create new message button try: test.set_to_implicit_wait(False) try: test.find("#create_message") except NoSuchElementException: parts[19]['success'] = True except Exception as e: print e parts[19]['test_message'] = str(e) finally: test.set_to_implicit_wait(True) ########## Requesting create new message through url ### redirects user to messages index try: test.open(reverse("message_edit", args=("0",))) sleep(3) parts[20]['success'] = test.is_current_url(reverse(\ "messages_index")+ "?" + urlencode({'error':\ "Permission denied"})) except Exception as e: print e parts[20]['test_message'] = str(e) ########## Sent messages are viewable try: row = test.find("#tab-body-sent div.tr a") # get id from /manage/messages/H1llFritVJ/details message_id = MESSAGE_DETAIL_RE.search(\ row.get_attribute("href")).group(1) row.click() sleep(3) parts[21]['success'] = test.is_current_url(reverse(\ "message_details", args=(message_id,))) except Exception as e: print e parts[21]['test_message'] = str(e) ########## Feedbacks are viewable try: test.open(reverse("messages_index")) sleep(2) test.find("#tab-feedback").click() sleep(1) row = test.find("#tab-body-feedback div.tr a") # get id from /manage/messages/feedback/H1llFritVJ feedback_id = row.get_attribute("href").split("/")[-1] row.click() sleep(3) parts[22]['success'] = test.is_current_url(reverse(\ "feedback_details", args=(feedback_id,))) except Exception as e: print e parts[22]['test_message'] = str(e) ########## No reply button try: test.set_to_implicit_wait(False) try: test.find("#reply-button") except NoSuchElementException: parts[23]['success'] = True except Exception as e: print e parts[23]['test_message'] = str(e) finally: test.set_to_implicit_wait(True) ########## Requesting reply through url ### redirects user to messages index try: test.open(reverse("feedback_reply", args=(feedback_id,))) sleep(3) parts[24]['success'] = test.is_current_url(reverse(\ "messages_index")+ "?" + urlencode({'error':\ "Permission denied"}) + "&tab_feedback=1") except Exception as e: print e parts[24]['test_message'] = str(e) ########## No delete message button try: test.open(reverse("feedback_details", args=(feedback_id,))) sleep(3) test.set_to_implicit_wait(False) try: test.find("#delete-button") except NoSuchElementException: parts[25]['success'] = True except Exception as e: print e parts[25]['test_message'] = str(e) finally: test.set_to_implicit_wait(True) ########## Requesting delete message through url ### redirects user to messages index try: test.open(reverse("feedback_delete", args=(feedback_id,))) sleep(2) parts[26]['success'] = test.is_current_url(reverse(\ "messages_index")+ "?" + urlencode({'error':\ "Permission denied"}) + "&tab_feedback=1") except Exception as e: print e parts[26]['test_message'] = str(e) ########## Analysis accessible try: test.open(reverse("analysis_index")) sleep(4) parts[27]['success'] = test.is_current_url(reverse(\ "analysis_index")) except Exception as e: print e parts[27]['test_message'] = str(e) ########## Employees accessible try: test.open(reverse("employees_index")) sleep(4) parts[28]['success'] = test.is_current_url(reverse(\ "employees_index")) except Exception as e: print e parts[28]['test_message'] = str(e) ########## No register new employee button try: test.set_to_implicit_wait(False) try: test.find("#register_employee") except NoSuchElementException: parts[29]['success'] = True except Exception as e: print e parts[29]['test_message'] = str(e) finally: test.set_to_implicit_wait(True) ########## Requesting new employee registration ### redirects user to employees_index try: test.open(reverse("employee_register")) sleep(3) parts[30]['success'] = test.is_current_url(reverse(\ "employees_index")+ "?" + urlencode({'error':\ "Permission denied"})) except Exception as e: print e parts[30]['test_message'] = str(e) ########## Approved employees are not clickable try: test.set_to_implicit_wait(False) try: test.find("#tab-body-approved-employees div.tr a") except NoSuchElementException: parts[31]['success'] = True except Exception as e: print e parts[31]['test_message'] = str(e) finally: test.set_to_implicit_wait(True) ########## Requesting edit employee through url ### redirects user to employees index try: row = test.find("#tab-body-approved-employees div.tr") employee_id = row.get_attribute("id") test.open(reverse("employee_edit", args=(employee_id,))) sleep(3) parts[32]['success'] = test.is_current_url(reverse(\ "employees_index")+ "?" + urlencode({'error':\ "Permission denied"})) except Exception as e: print e parts[32]['test_message'] = str(e) ########## No remove button in approved employees try: test.set_to_implicit_wait(False) try: test.find("#tab-body-approved-employees div.tr "+\ "div.remove a") except NoSuchElementException: parts[33]['success'] = True except Exception as e: print e parts[33]['test_message'] = str(e) finally: test.set_to_implicit_wait(True) ########## Requesting remove employee through url ### redirects user to employees index try: test.open(reverse("employee_delete", args=(employee_id,))) sleep(3) parts[34]['success'] = test.is_current_url(reverse(\ "employees_index")+ "?" + urlencode({'error':\ "Permission denied"})) except Exception as e: print e parts[34]['test_message'] = str(e) # create a pending register_rand_employee(store.objectId) test.new_driver(False) test.login(TEST_EMPLOYEE['username'], TEST_EMPLOYEE['password'], reverse("employees_index"), final_sleep=6) ########## No deny button in pending employees try: test.find("#tab-pending-employees").click() sleep(1) test.set_to_implicit_wait(False) try: test.find("#tab-body-pending-employees div.tr "+\ "div.deny a") except NoSuchElementException: parts[35]['success'] = True except Exception as e: print e parts[35]['test_message'] = str(e) finally: test.set_to_implicit_wait(True) ########## Requesting deny employee through url ### redirects user to employees index try: test.find("#tab-pending-employees").click() row = test.find("#tab-body-pending-employees div.tr") employee_id = row.get_attribute("id") test.open(reverse("employee_deny", args=(employee_id,))) sleep(3) parts[36]['success'] = test.is_current_url(reverse(\ "employees_index") + "?" + urlencode({'error':\ "Permission denied"})) except Exception as e: print e parts[36]['test_message'] = str(e) ########## No approve button in pending employees try: test.find("#tab-pending-employees").click() sleep(1) test.set_to_implicit_wait(False) try: test.find("#tab-body-pending-employees div.tr "+\ "div.approve a") except NoSuchElementException: parts[37]['success'] = True except Exception as e: print e parts[37]['test_message'] = str(e) finally: test.set_to_implicit_wait(True) ########## Requesting approve employee through url ### redirects user to employees index try: test.open(reverse("employee_approve", args=(employee_id,))) sleep(3) parts[38]['success'] = test.is_current_url(reverse(\ "employees_index") + "?" + urlencode({'error':\ "Permission denied"})) except Exception as e: print e parts[38]['test_message'] = str(e) ########## Settings accessible try: test.open(reverse("store_settings")) sleep(3) parts[39]['success'] = test.is_current_url(reverse(\ "store_settings")) except Exception as e: print e parts[39]['test_message'] = str(e) ########## No refresh button for retailer pin try: test.set_to_implicit_wait(False) try: test.find("#link_refresh_retailer_pin") except NoSuchElementException: parts[40]['success'] = True except Exception as e: print e parts[40]['test_message'] = str(e) finally: test.set_to_implicit_wait(True) ########## Requesting refresh through url returns ### a json object with error Permission denied try: test.open(reverse("refresh_retailer_pin")) sleep(3) parts[41]['success'] = "Permission denied" in\ test.driver.page_source except Exception as e: print e parts[41]['test_message'] = str(e) ########## Punches employee is readonly try: test.open(reverse("store_settings")) sleep(3) parts[42]['success'] =\ test.find("#id_punches_employee").get_attribute(\ "readonly") == "true" except Exception as e: print e parts[42]['test_message'] = str(e) ########## Punches facebook is readonly try: parts[43]['success'] =\ test.find("#id_punches_facebook").get_attribute(\ "readonly") == "true" except Exception as e: print e parts[43]['test_message'] = str(e) ########## No save button try: test.set_to_implicit_wait(False) try: test.find("#settings-form-submit") except NoSuchElementException: parts[44]['success'] = True except Exception as e: print e parts[44]['test_message'] = str(e) finally: test.set_to_implicit_wait(True) ########## No cancel changes button try: test.set_to_implicit_wait(False) try: test.find("#settings-options a.red") except NoSuchElementException: parts[45]['success'] = True except Exception as e: print e parts[45]['test_message'] = str(e) finally: test.set_to_implicit_wait(True) ########## Workbench accessible try: test.open(reverse("workbench_index")) sleep(3) parts[46]['success'] = test.is_current_url(\ reverse("workbench_index")) except Exception as e: print e parts[46]['test_message'] = str(e) ########## Employee can punch try: test.find("#punch_code").send_keys("00010") test.find("#punch_amount").send_keys("1") test.find("#punch-form a.button.blue").click() sleep(2) parts[47]['success'] =\ test.find(".notification.hide") is not None except Exception as e: print e parts[47]['test_message'] = str(e) ########## Employee can reject redeem try: request_redeem_ps("eiZa6Mzu7f") sleep(COMET_PULL_RATE*2+4) row = test.find("#tab-body-pending-redemptions div.tr") test.find("//div[@id='%s']/" % (row.get_attribute("id"),) +\ "div[contains(@class, 'redemption_redeem')]/a[2]", type="xpath").click() sleep(3) parts[48]['success'] = row.text.__contains__("Successfully") except Exception as e: print e parts[48]['test_message'] = str(e) ########## Employee can validate redeem try: request_redeem_ps("eiZa6Mzu7f") sleep(COMET_PULL_RATE*2+4) row = test.find("#tab-body-pending-redemptions div.tr") test.find("//div[@id='%s']/" % (row.get_attribute("id"),) +\ "div[contains(@class, 'redemption_redeem')]/a[1]", type="xpath").click() sleep(3) parts[49]['success'] = row.text.__contains__("Successfully") except Exception as e: print e parts[49]['test_message'] = str(e) # END OF ALL TESTS - cleanup return test.tear_down()
def handle(self, *args, **options): # for logging when ran by CRON print "Running passed_user_limit: " + str(timezone.now()) now = timezone.now() b4_now = now + relativedelta(hours=-1) # get 500 subscriptions at a time LIMIT = 500 # first scan though all the stores and set their # date_passed_user_limit if so # TODO optimize with a relational query? possible with Parse? #### SUB_TYPE 0 skip = 0 sub_count = Subscription.objects().count(\ date_passed_user_limit=None, subscriptionType=0) max_users = sub_type[0]['max_users'] while sub_count > 0: for sub in Subscription.objects().filter(\ subscriptionType=0, include="Store", date_passed_user_limit=None, god_mode=False, limit=LIMIT, skip=skip, order="createdAt"): store = sub.store if store.get("patronStores", count=1, limit=0) >\ max_users: sub.date_passed_user_limit = b4_now sub.update() # notify the dashboards of these changes payload={ COMET_RECEIVE_KEY_NAME: COMET_RECEIVE_KEY, "updatedSubscription": sub.jsonify() } comet_receive(sub.Store, payload) # end of while loop sub_count -= LIMIT skip += LIMIT # TODO optimize with a relational query? possible with Parse? #### SUB_TYPE 1 skip = 0 sub_count = Subscription.objects().count(\ date_passed_user_limit=None, subscriptionType=1) max_users = sub_type[1]['max_users'] while sub_count > 0: for sub in Subscription.objects().filter(\ subscriptionType=1, include="Store", date_passed_user_limit=None, god_mode=False, limit=LIMIT, skip=skip, order="createdAt"): store = sub.store if store.get("patronStores", count=1, limit=0) >\ max_users: sub.date_passed_user_limit = b4_now sub.update() # notify the dashboards of these changes payload={ COMET_RECEIVE_KEY_NAME: COMET_RECEIVE_KEY, "updatedSubscription": sub.jsonify() } comet_receive(sub.Store, payload) # end of while loop sub_count -= LIMIT skip += LIMIT ################ conn = mail.get_connection(fail_silently=(not DEBUG)) conn.open() # 1st day time range day1_end = now.replace() day1_start = day1_end + relativedelta(hours=-24) # 4th day time range day4_end = now + relativedelta(days=-4) day4_start = day4_end + relativedelta(hours=-24) # 8th day time range day8_end = now + relativedelta(days=-8) day8_start = day8_end + relativedelta(hours=-24) # 14th day time range day14_end = now + relativedelta(days=-14) day14_start = day14_end + relativedelta(hours=-24) #### SUB_TYPE 0 ## 1st day skip = 0 sub_count = Subscription.objects().count(\ subscriptionType=0, date_passed_user_limit__lte=day1_end, date_passed_user_limit__gte=day1_start) while sub_count > 0: for sub in Subscription.objects().filter(\ subscriptionType=0, include="Store", date_passed_user_limit__lte=day1_end, date_passed_user_limit__gte=day1_start, limit=LIMIT, skip=skip, order="createdAt"): # with pp_cc_id if sub.pp_cc_id and len(sub.pp_cc_id) > 0: sub.subscriptionType = 1 sub.date_passed_user_limit = None sub.update() # notify the dashboards of these changes payload={ COMET_RECEIVE_KEY_NAME: COMET_RECEIVE_KEY, "updatedSubscription": sub.jsonify() } comet_receive(sub.Store, payload) package = { "status": "upgraded", "sub_type": sub_type[0]["name"], "new_sub_type": sub_type[1]["name"], "new_sub_type_cost": sub_type[1]["monthly_cost"], "new_max_patronStore_count":\ sub_type[1]["max_users"], "patronStore_count": sub.store.get(\ "patronStores", limit=0, count=1), } # no pp_cc_id else: package = { "sub_type": sub_type[0]["name"], "max_patronStore_count": sub_type[0]["max_users"], "patronStore_count": sub.store.get(\ "patronStores", limit=0, count=1), "disable_date": sub.date_passed_user_limit + relativedelta(days=\ USER_LIMIT_PASSED_DISABLE_DAYS), } try: send_email_passed_user_limit(Account.objects().get(\ Store=sub.Store), sub.store, package, conn) except SMTPServerDisconnected: conn = mail.get_connection(fail_silently=(not DEBUG)) conn.open() send_email_passed_user_limit(Account.objects().get(\ Store=sub.Store), sub.store, package, conn) # end of while loop sub_count -= LIMIT skip += LIMIT ## 4th day skip = 0 sub_count = Subscription.objects().count(\ subscriptionType=0, date_passed_user_limit__lte=day4_end, date_passed_user_limit__gte=day4_start) while sub_count > 0: for sub in Subscription.objects().filter(\ subscriptionType=0, include="Store", date_passed_user_limit__lte=day4_end, date_passed_user_limit__gte=day4_start, limit=LIMIT, skip=skip, order="createdAt"): package = { "sub_type": sub_type[0]["name"], "max_patronStore_count": sub_type[0]["max_users"], "patronStore_count": sub.store.get(\ "patronStores", limit=0, count=1), "disable_date": sub.date_passed_user_limit + relativedelta(days=\ USER_LIMIT_PASSED_DISABLE_DAYS), } try: send_email_passed_user_limit(Account.objects().get(\ Store=sub.Store), sub.store, package, conn) except SMTPServerDisconnected: conn = mail.get_connection(fail_silently=(not DEBUG)) conn.open() send_email_passed_user_limit(Account.objects().get(\ Store=sub.Store), sub.store, package, conn) # end of while loop sub_count -= LIMIT skip += LIMIT ## 8th day skip = 0 sub_count = Subscription.objects().count(\ subscriptionType=0, date_passed_user_limit__lte=day8_end, date_passed_user_limit__gte=day8_start) while sub_count > 0: for sub in Subscription.objects().filter(\ subscriptionType=0, include="Store", date_passed_user_limit__lte=day8_end, date_passed_user_limit__gte=day8_start, limit=LIMIT, skip=skip, order="createdAt"): package = { "sub_type": sub_type[0]["name"], "max_patronStore_count": sub_type[0]["max_users"], "patronStore_count": sub.store.get(\ "patronStores", limit=0, count=1), "disable_date": sub.date_passed_user_limit + relativedelta(days=\ USER_LIMIT_PASSED_DISABLE_DAYS), } try: send_email_passed_user_limit(Account.objects().get(\ Store=sub.Store), sub.store, package, conn) except SMTPServerDisconnected: conn = mail.get_connection(fail_silently=(not DEBUG)) conn.open() send_email_passed_user_limit(Account.objects().get(\ Store=sub.Store), sub.store, package, conn) # end of while loop sub_count -= LIMIT skip += LIMIT ## 14th day skip = 0 sub_count = Subscription.objects().count(\ subscriptionType=0, date_passed_user_limit__lte=day14_end, date_passed_user_limit__gte=day14_start) while sub_count > 0: for sub in Subscription.objects().filter(\ subscriptionType=0, include="Store", date_passed_user_limit__lte=day14_end, date_passed_user_limit__gte=day14_start, limit=LIMIT, skip=skip, order="createdAt"): package = { "status": "disabled" } # deactivate the store sub.store.active = False sub.store.update() payload = { COMET_RECEIVE_KEY_NAME: COMET_RECEIVE_KEY, "updatedStore":sub.store.jsonify(), } comet_receive(sub.Store, payload) try: send_email_passed_user_limit(Account.objects().get(\ Store=sub.Store), sub.store, package, conn) except SMTPServerDisconnected: conn = mail.get_connection(fail_silently=(not DEBUG)) conn.open() send_email_passed_user_limit(Account.objects().get(\ Store=sub.Store), sub.store, package, conn) # end of while loop sub_count -= LIMIT skip += LIMIT #### SUB_TYPE 1 ## 1st day skip = 0 sub_count = Subscription.objects().count(\ subscriptionType=1, date_passed_user_limit__lte=day1_end, date_passed_user_limit__gte=day1_start) while sub_count > 0: for sub in Subscription.objects().filter(\ subscriptionType=1, include="Store", date_passed_user_limit__lte=day1_end, date_passed_user_limit__gte=day1_start, limit=LIMIT, skip=skip, order="createdAt"): # with pp_cc_id if sub.pp_cc_id and len(sub.pp_cc_id) > 0: sub.subscriptionType = 2 sub.date_passed_user_limit = None sub.update() # notify the dashboards of these changes payload={ COMET_RECEIVE_KEY_NAME: COMET_RECEIVE_KEY, "updatedSubscription": sub.jsonify() } comet_receive(sub.Store, payload) package = { "status": "upgraded", "sub_type": sub_type[1]["name"], "new_sub_type": sub_type[2]["name"], "new_sub_type_cost": sub_type[2]["monthly_cost"], "new_max_patronStore_count": "Unlimited", "patronStore_count": sub.store.get(\ "patronStores", limit=0, count=1), } # no pp_cc_id else: package = { "sub_type": sub_type[1]["name"], "max_patronStore_count": sub_type[1]["max_users"], "patronStore_count": sub.store.get(\ "patronStores", limit=0, count=1), "disable_date": sub.date_passed_user_limit + relativedelta(days=\ USER_LIMIT_PASSED_DISABLE_DAYS), } try: send_email_passed_user_limit(Account.objects().get(\ Store=sub.Store), sub.store, package, conn) except SMTPServerDisconnected: conn = mail.get_connection(fail_silently=(not DEBUG)) conn.open() send_email_passed_user_limit(Account.objects().get(\ Store=sub.Store), sub.store, package, conn) # end of while loop sub_count -= LIMIT skip += LIMIT ## 4th day skip = 0 sub_count = Subscription.objects().count(\ subscriptionType=1, date_passed_user_limit__lte=day4_end, date_passed_user_limit__gte=day4_start) while sub_count > 0: for sub in Subscription.objects().filter(\ subscriptionType=1, include="Store", date_passed_user_limit__lte=day4_end, date_passed_user_limit__gte=day4_start, limit=LIMIT, skip=skip, order="createdAt"): package = { "sub_type": sub_type[1]["name"], "max_patronStore_count": sub_type[1]["max_users"], "patronStore_count": sub.store.get(\ "patronStores", limit=0, count=1), "disable_date": sub.date_passed_user_limit + relativedelta(days=\ USER_LIMIT_PASSED_DISABLE_DAYS), } try: send_email_passed_user_limit(Account.objects().get(\ Store=sub.Store), sub.store, package, conn) except SMTPServerDisconnected: conn = mail.get_connection(fail_silently=(not DEBUG)) conn.open() send_email_passed_user_limit(Account.objects().get(\ Store=sub.Store), sub.store, package, conn) # end of while loop sub_count -= LIMIT skip += LIMIT ## 8th day skip = 0 sub_count = Subscription.objects().count(\ subscriptionType=1, date_passed_user_limit__lte=day8_end, date_passed_user_limit__gte=day8_start) while sub_count > 0: for sub in Subscription.objects().filter(\ subscriptionType=1, include="Store", date_passed_user_limit__lte=day8_end, date_passed_user_limit__gte=day8_start, limit=LIMIT, skip=skip, order="createdAt"): package = { "sub_type": sub_type[1]["name"], "max_patronStore_count": sub_type[1]["max_users"], "patronStore_count": sub.store.get(\ "patronStores", limit=0, count=1), "disable_date": sub.date_passed_user_limit + relativedelta(days=\ USER_LIMIT_PASSED_DISABLE_DAYS), } try: send_email_passed_user_limit(Account.objects().get(\ Store=sub.Store), sub.store, package, conn) except SMTPServerDisconnected: conn = mail.get_connection(fail_silently=(not DEBUG)) conn.open() send_email_passed_user_limit(Account.objects().get(\ Store=sub.Store), sub.store, package, conn) # end of while loop sub_count -= LIMIT skip += LIMIT ## 14th day skip = 0 sub_count = Subscription.objects().count(\ subscriptionType=1, date_passed_user_limit__lte=day14_end, date_passed_user_limit__gte=day14_start) while sub_count > 0: for sub in Subscription.objects().filter(\ subscriptionType=1, include="Store", date_passed_user_limit__lte=day14_end, date_passed_user_limit__gte=day14_start, limit=LIMIT, skip=skip, order="createdAt"): package = { "status": "disabled" } # deactivate the store sub.store.active = False sub.store.update() payload = { COMET_RECEIVE_KEY_NAME: COMET_RECEIVE_KEY, "updatedStore":sub.store.jsonify(), } comet_receive(sub.Store, payload) try: send_email_passed_user_limit(Account.objects().get(\ Store=sub.Store), sub.store, package, conn) except SMTPServerDisconnected: conn = mail.get_connection(fail_silently=(not DEBUG)) conn.open() send_email_passed_user_limit(Account.objects().get(\ Store=sub.Store), sub.store, package, conn) # end of while loop sub_count -= LIMIT skip += LIMIT try: conn.close() except Exception: pass
def test_cancel_account(): """ A test just for the cancel account link. """ account = Account.objects().get(username=TEST_USER['username'], include="Store") store = account.store test = SeleniumTest() parts = [ {'test_name': "User needs to be logged in to access page"}, {'test_name': "Clicking the cancel button brings up a " +\ "confirmation dialog"}, {'test_name': "Clicking cancel on the dialog dimisses the " +\ "dialog and the account remains active"}, {'test_name': "Clicking OK logs the user out"}, {'test_name': "Clicking OK sets the store's active field " +\ "to false on Parse"}, ] section = { "section_name": "Deactivate store link functional?", "parts": parts, } self.results.append(section) ########## User needs to be logged in to access page self.open(reverse("store_index")) sleep(1) parts[0]['success'] = self.is_current_url(reverse(\ 'manage_login') + "?next=" + reverse("store_index")) # login selectors = ( ("#login_username", TEST_USER['username']), ("#login_password", TEST_USER['password']), ("", Keys.RETURN) ) self.action_chain(0, selectors, "send_keys") sleep(7) ########## Clicking the cancel button brings up a confrmtn dialog try: self.find("#deactivate_account").click() sleep(1) alert = self.switch_to_alert() parts[1]['success'] = alert is not None except Exception as e: print e parts[1]['test_message'] = str(e) ########## Clicking cancel on the dialog dimisses the ### dialog and the account remains active try: alert.dismiss() try: alert.text except NoAlertPresentException: store.active = None parts[2]['success'] = store.get("active") except Exception as e: print e parts[2]['test_message'] = str(e) ########## Clicking OK logs the user out try: self.find("#deactivate_account").click() sleep(1) alert = self.switch_to_alert() alert.accept() sleep(4) if SeleniumTest.DEV_LOGIN: parts[3]["success"] =\ self.is_current_url(reverse("manage_dev_login")+"?next=/") else: parts[3]["success"] =\ self.is_current_url(reverse("public_home")) except Exception as e: print e parts[3]['test_message'] = str(e) ########## Clicking OK sets the store's active ### field to false on Parse store.active = None parts[4]['success'] = not store.get("active") # undo store.active = True store.update() # END OF ALL TESTS - cleanup return self.tear_down()
def sign_up(request): """ Creates User, store, subscription, and settings objects. """ # renders the signup page on GET and returns a json object on POST. data = {'sign_up_nav': True} if request.method == 'POST': # this conversion to a regular dictionay is important postDict = request.POST.dict() from_associated_account = False # check if this post is from the associated account dialog # if it is then skip form validations aaf_nonce_id = postDict.get('aaf-nonce') aaf_account_id = postDict.get('aaf-account_id') if len(aaf_nonce_id) > 0 and len(aaf_account_id) > 0: aa_nonce = AssociatedAccountNonce.objects.filter(\ id=aaf_nonce_id, account_id=aaf_account_id) if len(aa_nonce) > 0 and aa_nonce[0].verified: aa_nonce[0].delete() from_associated_account = True # some keys are repeated so must catch this at init store_form = StoreSignUpForm(request.POST) store_location_form = StoreLocationForm(request.POST) account_form = AccountSignUpForm(request.POST) cats = postDict.get("categories") category_names = None if cats and len(cats) > 0: category_names = cats.split("|")[:-1] # make sure that there are only up to 2 categories while len(category_names) > 2: category_names.pop() data["category_names"] = category_names if not from_associated_account: all_forms_valid = store_form.is_valid() and\ store_location_form.is_valid() and account_form.is_valid() else: all_forms_valid = True if all_forms_valid: # check if email already taken here to handle the case where # the user already has a patron/employee account # but also want to sign up for a Store account if hasattr(account_form, "associated_account"): aa = account_form.associated_account aan = AssociatedAccountNonce.objects.create(\ account_id=aa.objectId) return HttpResponse(json.dumps({"associated_account":\ aa.objectId, "associated_account_nonce":aan.id, "email": aa.email, "code": 0}), content_type="application/json") ######################################################### # create store store = Store(**postDict) # set defaults for these guys to prevent # ParseObjects from making parse calls repeatedly store.punches_facebook = 1 store.set("rewards", []) store.set("categories", []) if category_names: for name in category_names: alias = Category.objects.filter(name__iexact=name) if len(alias) > 0: store.categories.append({ "alias": alias[0].alias, "name": name }) # create settings settings = Settings(Store=store.objectId) store.set('settings', settings) # create account if not from_associated_account: account = Account(**postDict) # username = email # we should be doing this in the form but ehh account.set("username", postDict['email'].strip().lower()) account.set("email", postDict['email'].strip().lower()) account.set_password(postDict.get('password')) else: account =\ Account.objects().get(objectId=aaf_account_id) account.set("store", store) # create subscription subscription = Subscription() subscription.subscriptionType = 0 subscription.date_last_billed = timezone.now() subscription.create() # create settings settings.create() # create store store.Settings = settings.objectId store.Subscription = subscription.objectId store.create() # add the pointer to the created store settings.Store = store.objectId settings.update() subscription.Store = store.objectId subscription.update() # create the store location store_location = StoreLocation(**postDict) # format the phone number store_location.store_timezone =\ rputils.get_timezone(postDict.get("zip")).zone store_location.set("hours", []) # coordinates and neighborhood # the call to get map data is actually also in the clean full_address = " ".join(\ store_location.get_full_address().split(", ")) map_data = rputils.get_map_data(full_address) store_location.set("coordinates", map_data.get("coordinates")) store_location.set("neighborhood", store_location.get_best_fit_neighborhood(\ map_data.get("neighborhood"))) store_location.phone_number =\ format_phone_number(postDict["phone_number"]) store_location.Store = store.objectId store_location.create() # add the StoreLocation to the relation store.store_locations = [store_location] store.update() # create account account.Store = store.objectId if not from_associated_account: account.create() else: account.update() # create the store ACL with the account having r/w access store.ACL = { "*": { "read": True, "write": True }, account.objectId: { "read": True, "write": True }, } store.owner_id = account.objectId store.update() # note that username has been fed the email # this shouldn't change anything though shouldn't matter # need to put username and pass in request postDict['username'] = account.username postDict['password'] = account.password # send matt and new user a pretty email. send_email_signup(account) # auto login user_login = login(request, postDict, no_recaptcha=True) if user_login != None: data = {"code": -1} # response to signup.js - not login returns # 0 - Associated account already exists # 2 - subscription is not active # 3 - success (login now) if type(user_login) is int: # subscription not active data['code'] = 2 else: # required for datetime awareness! rputils.set_timezone(request, tz) data['code'] = 3 return HttpResponse(json.dumps(data), content_type="application/json") else: store_form = StoreSignUpForm() store_location_form = StoreLocationForm() account_form = AccountSignUpForm() data['store_form'] = store_form data['store_location_form'] = store_location_form data['account_form'] = account_form return render(request, 'public/signup.djhtml', data)
def login(request, requestDict, no_recaptcha=False): """ This combines Django's authenticate and login functions. The request.POST must contain a username and password. If authentication is successful, account is updated_locally and adds the appropriate data to the request so that the user/ account is recognized to be logged in. This will also cache some of the store's info in the session. Also checks if the store is active or not. Returns an Account object if the account subscription is active and username and passwords are good. Otherwise, 0 if bad login credentials (wrong pass or pointer to store does not exist) and 1 if subscription is not active, 2 if employee but no access, 3 if employee is still pending, 4 if RECAPTCHA_TOKEN in session and recaptcha response fails. """ # first check if the request is already logged in if request.session.get('account'): return request.session.get('account') # now check for recaptcha if not no_recaptcha and RECAPTCHA_TOKEN in request.session and\ request.session[RECAPTCHA_TOKEN] >= RECAPTCHA_ATTEMPTS: is_valid = recaptcha.submit(request, requestDict['recaptcha_challenge_field'], requestDict['recaptcha_response_field']).is_valid if not is_valid: return 4 # note that email is the same as username # email needs to be stripped and lowered res = account_login(requestDict['username'].strip().lower(), requestDict['password']) if res and "error" not in res: # correct login credentials - remove recaptcha token if exist if not no_recaptcha: recaptcha.login_success(request.session, requestDict['username']) account = Account(**res) account.fetch_all() # If the Account has both a Store and Employee object, # log the user in as the Store owner - not employee store = None if account.Store: store = account.store account_type = "store" elif account.Employee: store = account.employee.get("store") employee = account.employee account_type = "employee" # if the User object has a store then we are good to go if store: store.fetch_all() # check if employee with no access level or still pending if account_type == "employee": if employee.status == PENDING: return 3 elif not store.has_access(account): return 2 settings = store.get("settings") subscription = store.get("subscription") if store.get('active'): request.session[SESSION_KEY] = res.get('sessionToken') # load all cache data! # IMPORTANT for parse.comet.receive to work properly # reason is that if user stays in 1 page, not all # cache data is loaded and things go wrong- # e.g. sending message from window 1 does not update # the message count in window 2 request.session['subscription'] = subscription request.session['settings'] = settings request.session['store'] = store request.session['account'] = account if account_type == "employee": request.session['employee'] = employee SESSION.load_all(request.session) # If value is None, the session reverts to using # the global session expiry policy. if "stay_in" in requestDict and PRODUCTION_SERVER: request.session.set_expiry(None) # If value is 0, the user's session cookie will # expire when the user's Web browser is closed. else: request.session.set_expiry(0) return account else: return 1 else: if not no_recaptcha: recaptcha.login_fail(request.session, requestDict['username']) return 0 else: if not no_recaptcha: recaptcha.login_fail(request.session, requestDict['username']) return 0
def test_employees(): """ Tests for employee approve, deny, remove, details. etc. """ # TODO test employee graph # TODO test employee punches history # delete the employees and associated User objects in the relation account = Account.objects().get(username=TEST_USER['username'], include="Store.Settings") store = account.store settings = store.settings emps = store.get("employees") if emps: for emp in emps: Account.objects().get(Employee=emp.objectId).delete() emp.delete() store.set("employees", None) test = SeleniumTest() parts = [ {'test_name': "User needs to be logged in to access page"}, {'test_name': "Cloud code register_employee works"}, {'test_name': "Employee is saved in store's Employees " +\ "relation"}, {'test_name': "Employee is initially pending (Parse)"}, {'test_name': "Employee is initially pending (Dashboard)"}, {'test_name': "Email must be valid (cloud code)"}, {'test_name': "Email must be unique (cloud code)"}, {'test_name': "Username must be unique (cloud code)"}, {'test_name': "Retailer PIN must exist (cloud code)"}, {'test_name': "Clicking deny prompts the user to confirm"}, {'test_name': "The user is redirected to employee index"}, {'test_name': "The denied employee is removed from the " +\ "pending table"}, {'test_name': "The employee is deleted from parse"}, {'test_name': "The account/user is deleted from parse"}, {'test_name': "Approving the employee moves it from " +\ "pending to approved"}, {'test_name': "Employee status is set to approved in Parse"}, {'test_name': "Employee initially has 0 punches."}, {'test_name': "Clicking on the approved employee row " +\ " redirects user to employee edit page"}, {'test_name': "Clicking delete prompts the user to confirm"}, {'test_name': "The user is redirected to employee index"}, {'test_name': "The deleted employee is removed from the " +\ "pending table"}, {'test_name': "The employee is deleted from parse"}, {'test_name': "The account/user is deleted from parse"}, {'test_name': "Multiple employees (3) registering at once" +\ " shows up in dashboard"}, ] section = { "section_name": "Employees page working properly?", "parts": parts, } test.results.append(section) ########## User needs to be logged in to access page" test.open(reverse("employees_index")) # ACTION! sleep(1) parts[0]['success'] = test.is_current_url(reverse(\ 'manage_login') + "?next=" + reverse("employees_index")) # login selectors = (("#login_username", TEST_USER['username']), ("#login_password", TEST_USER['password']), ("", Keys.RETURN)) test.action_chain(0, selectors, "send_keys") # ACTION! sleep(7) def register_employee(first_name, last_name, username=None, password=None, email=None, retailer_pin=None): if username is None: username = first_name if password is None: password = first_name if email is None: email = first_name + "@" + last_name + ".com" if retailer_pin is None: retailer_pin = settings.retailer_pin return cloud_call( "register_employee", { "first_name": first_name, "last_name": last_name, "username": username, "password": password, "email": email, "retailer_pin": retailer_pin, }) first_name, last_name, username, email =\ "vandolf1", "estrellado", "*****@*****.**", "*****@*****.**" ########## Cloud code register_employee works" try: res = register_employee(first_name, last_name, username, email=email) parts[1]['success'] = "error" not in res except Exception as e: print e parts[1]['test_message'] = str(e) ########## Employee is saved in store's Employees relation" try: emp = store.get("employees")[0] parts[2]['success'] = emp is not None except Exception as e: print e parts[2]['test_message'] = str(e) ########## Employee is initially pending (Parse)" try: parts[3]['success'] = emp.status == PENDING except Exception as e: print e parts[3]['test_message'] = str(e) ########## Employee is initially pending (Dashboard)" try: sleep(COMET_PULL_RATE * 2 + 1) # wait for dashboard to receive test.find("#tab-pending-employees").click() parts[4]['success'] = test.element_exists(\ "#tab-body-pending-employees div.tr div.td.approve") except Exception as e: print e parts[4]['test_message'] = str(e) ########## Email must be valid (cloud code)" try: res = register_employee("vman", "vman", email="vmahs@vman") parts[5]['success'] = res['error'] == 'EMAIL_INVALID' except Exception as e: print e parts[5]['test_message'] = str(e) ########## Email must be unique (cloud code) " try: res = register_employee("vman", "vman", email=email) parts[6]['success'] = res['error'] == 'EMAIL_TAKEN' except Exception as e: print e parts[6]['test_message'] = str(e) ########## Username must be unique (cloud code) " try: res = register_employee("vman", "vman", username=username) parts[7]['success'] = res['error'] == 'USERNAME_TAKEN' except Exception as e: print e parts[7]['test_message'] = str(e) ########## Retailer PIN must exist (cloud code) " try: res = register_employee("vman", "vman", retailer_pin="sdgdgs") parts[8]['success'] = res['error'] == "RETAILER_PIN_INVALID" except Exception as e: print e parts[8]['test_message'] = str(e) ########## Clicking deny prompts the user to confirm" try: test.find("#tab-pending-employees").click() test.find("#tab-body-pending-employees div.tr " +\ "div.td.approve a.deny").click() alert = test.switch_to_alert() parts[9]['success'] = alert.text == "Deny employee?" except Exception as e: print e parts[9]['test_message'] = str(e) ########## The user is redirected to employee index" try: sleep(1) alert.accept() sleep(2) parts[10]['success'] = test.is_current_url(reverse(\ "employees_index") +\ "?show_pending&success=Employee+has+been+denied.") except Exception as e: print e parts[10]['test_message'] = str(e) ########## The denied employee is removed from the pending table" try: parts[11]['success'] = not test.element_exists(\ "#tab-body-pending-employees div.tr " +\ "div.td.approve a.approve") except Exception as e: print e parts[11]['test_message'] = str(e) ########## The employee is deleted from parse" try: store.set("employees", None) parts[12]['success'] = store.get("employees",\ first_name=first_name, last_name=last_name, count=1) == 0 except Exception as e: print e parts[12]['test_message'] = str(e) ########## The account/user is deleted from parse" try: parts[13]['success'] = Account.objects().count(\ username=username, email=email) == 0 except Exception as e: print e parts[13]['test_message'] = str(e) ########## Approving the employee moves it from pending to approved" try: register_employee(first_name, last_name, username, email=email) sleep(COMET_PULL_RATE * 2 + 1) # wait for dashboard to receive test.find("#tab-pending-employees").click() approveRow = test.find("#tab-body-pending-employees " +\ "div.tr") approveId = approveRow.get_attribute("id") approveRow.find_element_by_css_selector(\ "div.td.approve a.approve").click() sleep(1) test.switch_to_alert().accept() sleep(2) test.find("#tab-approved-employees").click() parts[14]['success'] = test.element_exists(\ "#tab-body-approved-employees " +\ "div.tr div.td.remove a") except Exception as e: print e parts[14]['test_message'] = str(e) ########## Employee status is set to approved in Parse" try: store.set("employees", None) emp = store.get("employees", first_name=first_name, last_name=last_name)[0] parts[15]['success'] = emp.status == APPROVED except Exception as e: print e parts[15]['test_message'] = str(e) ########## Employee initially has 0 punches" try: parts[16]['success'] = emp.lifetime_punches == 0 except Exception as e: print e parts[16]['test_message'] = str(e) ########## Clicking on the approved employee row ### redirects user to employee edit page try: test.find("#tab-approved-employees").click() test.find("#tab-body-approved-employees div#%s a" %\ (emp.objectId,)).click() sleep(1) parts[17]['success'] = test.is_current_url(reverse(\ "employee_edit", args=(emp.objectId,))) except Exception as e: print e parts[17]['test_message'] = str(e) ########## Clicking delete prompts the user to confirm" try: test.find("#delete-button").click() alert = test.switch_to_alert() parts[18]['success'] = alert.text ==\ "Are you sure you want to delete this employee?" except Exception as e: print e parts[18]['test_message'] = str(e) ########## The user is redirected to employee index" try: sleep(1) alert.accept() sleep(2) parts[19]['success'] = test.is_current_url(reverse(\ "employees_index") +\ "?success=Employee+has+been+deleted.") except Exception as e: print e parts[19]['test_message'] = str(e) ########## The deleted employee is removed from the pending table" try: parts[20]['success'] = not test.element_exists(\ "#tab-body-approved-employees div#%s a" %(emp.objectId,)) except Exception as e: print e parts[20]['test_message'] = str(e) ########## The employee is deleted from parse" try: store.set("employees", None) parts[21]['success'] = store.get("employees", objectId=emp.objectId, count=1) == 0 except Exception as e: print e parts[21]['test_message'] = str(e) ########## The account/user is deleted from parse" try: parts[22]['success'] = Account.objects().count(\ username=username, email=email) == 0 except Exception as e: print e parts[22]['test_message'] = str(e) ########## Multiple employees (4) registering at once ### shows up in dashboard" try: for i in range(3): register_employee(first_name + str(i), last_name + str(i)) sleep(COMET_PULL_RATE * 2 + 3) test.find("#tab-pending-employees").click() parts[23]['success'] = len(test.find(\ "#tab-body-pending-employees div.tr " +\ "div.td.approve a.approve", multiple=True)) == 3 except Exception as e: print e parts[23]['test_message'] = str(e) # END OF ALL TESTS - cleanup return test.tear_down()
def sign_up(request): """ Creates User, store, subscription, and settings objects. """ # renders the signup page on GET and returns a json object on POST. data = {'sign_up_nav': True} if request.method == 'POST': # this conversion to a regular dictionay is important postDict = request.POST.dict() from_associated_account = False # check if this post is from the associated account dialog # if it is then skip form validations aaf_nonce_id = postDict.get('aaf-nonce') aaf_account_id = postDict.get('aaf-account_id') if len(aaf_nonce_id) > 0 and len(aaf_account_id) > 0: aa_nonce = AssociatedAccountNonce.objects.filter(\ id=aaf_nonce_id, account_id=aaf_account_id) if len(aa_nonce) > 0 and aa_nonce[0].verified: aa_nonce[0].delete() from_associated_account = True # some keys are repeated so must catch this at init store_form = StoreSignUpForm(request.POST) store_location_form = StoreLocationForm(request.POST) account_form = AccountSignUpForm(request.POST) cats = postDict.get("categories") category_names = None if cats and len(cats) > 0: category_names = cats.split("|")[:-1] # make sure that there are only up to 2 categories while len(category_names) > 2: category_names.pop() data["category_names"] = category_names if not from_associated_account: all_forms_valid = store_form.is_valid() and\ store_location_form.is_valid() and account_form.is_valid() else: all_forms_valid = True if all_forms_valid: # check if email already taken here to handle the case where # the user already has a patron/employee account # but also want to sign up for a Store account if hasattr(account_form, "associated_account"): aa = account_form.associated_account aan = AssociatedAccountNonce.objects.create(\ account_id=aa.objectId) return HttpResponse(json.dumps({"associated_account":\ aa.objectId, "associated_account_nonce":aan.id, "email": aa.email, "code": 0}), content_type="application/json") ######################################################### # create store store = Store(**postDict) # set defaults for these guys to prevent # ParseObjects from making parse calls repeatedly store.punches_facebook = 1 store.set("rewards", []) store.set("categories", []) if category_names: for name in category_names: alias = Category.objects.filter(name__iexact=name) if len(alias) > 0: store.categories.append({ "alias":alias[0].alias, "name":name }) # create settings settings = Settings(Store=store.objectId) store.set('settings', settings) # create account if not from_associated_account: account = Account(**postDict) # username = email # we should be doing this in the form but ehh account.set("username", postDict['email'].strip().lower()) account.set("email", postDict['email'].strip().lower()) account.set_password(postDict.get('password')) else: account =\ Account.objects().get(objectId=aaf_account_id) account.set("store", store) # create subscription subscription = Subscription() subscription.subscriptionType = 0 subscription.date_last_billed = timezone.now() subscription.create() # create settings settings.create() # create store store.Settings = settings.objectId store.Subscription = subscription.objectId store.create() # add the pointer to the created store settings.Store = store.objectId settings.update() subscription.Store = store.objectId subscription.update() # create the store location store_location = StoreLocation(**postDict) # format the phone number store_location.store_timezone =\ rputils.get_timezone(postDict.get("zip")).zone store_location.set("hours", []) # coordinates and neighborhood # the call to get map data is actually also in the clean full_address = " ".join(\ store_location.get_full_address().split(", ")) map_data = rputils.get_map_data(full_address) store_location.set("coordinates", map_data.get("coordinates")) store_location.set("neighborhood", store_location.get_best_fit_neighborhood(\ map_data.get("neighborhood"))) store_location.phone_number =\ format_phone_number(postDict["phone_number"]) store_location.Store = store.objectId store_location.create() # add the StoreLocation to the relation store.store_locations = [store_location] store.update() # create account account.Store = store.objectId if not from_associated_account: account.create() else: account.update() # create the store ACL with the account having r/w access store.ACL = { "*": {"read": True, "write": True}, account.objectId: {"read": True, "write": True}, } store.owner_id = account.objectId store.update() # note that username has been fed the email # this shouldn't change anything though shouldn't matter # need to put username and pass in request postDict['username'] = account.username postDict['password'] = account.password # send matt and new user a pretty email. send_email_signup(account) # auto login user_login = login(request, postDict, no_recaptcha=True) if user_login != None: data = {"code":-1} # response to signup.js - not login returns # 0 - Associated account already exists # 2 - subscription is not active # 3 - success (login now) if type(user_login) is int: # subscription not active data['code'] = 2 else: # required for datetime awareness! rputils.set_timezone(request, tz) data['code'] = 3 return HttpResponse(json.dumps(data), content_type="application/json") else: store_form = StoreSignUpForm() store_location_form = StoreLocationForm() account_form = AccountSignUpForm() data['store_form'] = store_form data['store_location_form'] = store_location_form data['account_form'] = account_form return render(request, 'public/signup.djhtml', data)
def test_punch(): """ Tests for punch customers section of the workbench. Assumes that at least 1 PatronStore exists for the above Store. """ account = Account.objects().get(username=TEST_USER['username'], include="Store.Settings") store = account.store settings = store.settings ps = store.get("patronStores", include="Patron", limit=1)[0] patron = ps.patron test = SeleniumTest() parts = [ {'test_name': "User needs to be logged in to access page"}, {'test_name': "Punching works"}, {'test_name': "PatronStore punch_count is updated"}, {'test_name': "PatronStore all_time_punches is updated"}, {'test_name': "Punch Code is required"}, {'test_name': "Punch Codes consist of only numbers"}, {'test_name': "Punch Codes are 5 characters long"}, {'test_name': "Amount of punches is required"}, {'test_name': "Amount should not exceed maximum employee punches allowed"}, {'test_name': "Amount must be greater than 0"}, {'test_name': "Non-existent Punch Code shows approprirate message"}, ] section = { "section_name": "Punch working properly?", "parts": parts, } test.results.append(section) ########## User needs to be logged in to access page test.open(reverse("workbench_index")) # ACTION! sleep(1) parts[0]['success'] = test.is_current_url(reverse(\ 'manage_login') + "?next=" + reverse("workbench_index")) # login selectors = ( ("#login_username", TEST_USER['username']), ("#login_password", TEST_USER['password']), ("", Keys.RETURN) ) test.action_chain(0, selectors, "send_keys") # ACTION! sleep(5) def punch(punch_code="", punch_amount="", sleep_prior=0, sleep_after=0): if sleep_prior > 0: sleep(sleep_prior) test.find("#punch_code").clear() test.find("#punch_amount").clear() test.find("#punch_code").send_keys(punch_code) test.find("#punch_amount").send_keys(punch_amount) test.find("#punch-form a.button.blue").click() if sleep_after > 0: sleep(sleep_after) ########## Punching works try: punch_count, all_time_punches =\ ps.punch_count, ps.all_time_punches punch(patron.punch_code, "1", sleep_after=3) parts[1]['success'] =\ test.find("#punch-notification") is not None except Exception as e: print e parts[1]['test_message'] = str(e) ########## PatronStore punch_count is updated try: ps.punch_count = None parts[2]['success'] = punch_count+1 == ps.get("punch_count") except Exception as e: print e parts[2]['test_message'] = str(e) ########## PatronStore all_time_punches is updated try: ps.all_time_punches = None parts[3]['success'] =\ all_time_punches+1 == ps.get("all_time_punches") except Exception as e: print e parts[3]['test_message'] = str(e) ########## Punch Code is required try: punch(sleep_prior=5, sleep_after=1) parts[4]['success'] =\ test.find("#punch-notification").text ==\ "Please enter the customer's punch code." except Exception as e: print e parts[4]['test_message'] = str(e) ########## Punch Codes consist of only numbers try: punch("123ab", sleep_prior=5, sleep_after=1) parts[5]['success'] =\ test.find("#punch-notification").text ==\ "Punch Codes consist of only numbers." except Exception as e: print e parts[5]['test_message'] = str(e) ########## Punch Codes are 5 characters long try: punch("123", sleep_prior=5, sleep_after=1) parts[6]['success'] =\ test.find("#punch-notification").text ==\ "Punch Codes are 5 characters long." except Exception as e: print e parts[6]['test_message'] = str(e) ########## Amount of punches is required try: punch(patron.punch_code, sleep_prior=5, sleep_after=1) parts[7]['success'] =\ test.find("#punch-notification").text ==\ "Please enter the number of punches to give." except Exception as e: print e parts[7]['test_message'] = str(e) ########## Amount should not exceed maximum employee punches allowed try: punch(patron.punch_code, str(settings.punches_employee+1), sleep_prior=5, sleep_after=1) parts[8]['success'] =\ test.find("#punch-notification").text ==\ "Maximum amount of punches is %s." % (str(settings.punches_employee),) except Exception as e: print e parts[8]['test_message'] = str(e) ########## Amount must be greater than 0 try: punch(patron.punch_code, "-1", sleep_prior=5, sleep_after=1) parts[9]['success'] =\ test.find("#punch-notification").text ==\ "Amount of punches must be greater than 0." except Exception as e: print e parts[9]['test_message'] = str(e) ########## Non-existent Punch Code shows approprirate message try: punch("39393", "1", sleep_prior=5, sleep_after=1) parts[10]['success'] =\ test.find("#punch-notification").text ==\ "A customer with that Punch Code was not found." pass except Exception as e: print e parts[10]['test_message'] = str(e) # END OF ALL TESTS - cleanup return test.tear_down()
def test_employee_access(): """ Tests for employee dashboard access. This tests any user with ACL of ACCESS_PUNCHREDEEM and NO_ACCESS. Tests for ACCESS_ADMIN are not necessary since all other tests involving the store owner covers it. """ # delete the employees and associated User objects in the relation account = Account.objects().get(email=TEST_USER['username'], include="Store.Settings") store = account.store settings = store.settings emps = store.get("employees") if emps: for emp in emps: Account.objects().get(Employee=emp.objectId).delete() emp.delete() store.set("employees", None) test = SeleniumTest() parts = [ {"test_name" : "Pending employee has no access"}, {"test_name" : "Approved employee initially not in store ACL"}, {"test_name" : "Employee with ACCESS_NONE cannot login " +\ "using the login dialog"}, {"test_name" : "Employee with ACCESS_NONE cannot login " +\ "using the dedicated login page"}, {"test_name" : "Employee with ACCESS_PUNCHREDEEM can " +\ "login to the dashboard through the login dialog"}, {"test_name" : "Employee with ACCESS_PUNCHREDEEM can " +\ "login to the dashboard through the dedicated dialog pg"}, {"test_name" : "Account settings accessible"}, {"test_name" : "No store edit button"}, {"test_name" : "Requesting edit store detail through url " +\ "redirects user to store index"}, {"test_name" : "No update subscription button"}, {"test_name" : "Requesting update subscription through url " +\ "redirects user to store index"}, {"test_name" : "No deactivate my store button"}, {"test_name" : "Requesting store deactivation through url " +\ "redirects user to store index"}, {"test_name" : "Rewards accessible"}, {"test_name" : "No create new reward button"}, {"test_name" : "Requesting create new reward through url " +\ "redirects user to rewards index"}, {"test_name" : "Rewards are not clickable"}, {"test_name" : "Requesting edit reward through url " +\ "redirects user to rewards index"}, {"test_name" : "Messages accessible"}, {"test_name" : "No create new message button"}, {"test_name" : "Requesting create new message through url " +\ "redirects user to messages index"}, {"test_name" : "Sent messages are viewable"}, {"test_name" : "Feedbacks are viewable"}, {"test_name" : "No reply button"}, {"test_name" : "Requesting reply through url " +\ "redirects user to messages index"}, {"test_name" : "No delete message button"}, {"test_name" : "Requesting delete message through url " +\ "redirects user to messages index"}, {"test_name" : "Analysis accessible"}, {"test_name" : "Employees accessible"}, {"test_name" : "No register new employee button"}, {"test_name" : "Requesting new employee registration"+\ "redirects user to employees_index"}, {"test_name" : "Approved employees are not clickable"}, {"test_name" : "Requesting edit employee through url " +\ "redirects user to employees index"}, {"test_name" : "No remove button in approved employees"}, {"test_name" : "Requesting remove employee through url " +\ "redirects user to employees index"}, {"test_name" : "No deny button in pending employees"}, {"test_name" : "Requesting deny employee through url " +\ "redirects user to employees index"}, {"test_name" : "No approve button in pending employees"}, {"test_name" : "Requesting approve employee through url " +\ "redirects user to employees index"}, {"test_name" : "Settings accessible"}, {"test_name" : "No refresh button for retailer pin"}, {"test_name" : "Requesting refresh through url returns " +\ "a json object with error Permission denied"}, {"test_name" : "Punches employee is readonly"}, {"test_name" : "Punches facebook is readonly"}, {"test_name" : "No save button"}, {"test_name" : "No cancel changes button"}, {"test_name" : "Workbench accessible"}, {"test_name" : "Employee can punch"}, {"test_name" : "Employee can reject redeem"}, {"test_name" : "Employee can validate redeem"}, ] section = { "section_name":\ "Employee dashboard access working as expected?", "parts": parts, } test.results.append(section) try: # register the test employee register_employee("employee", "ex", TEST_EMPLOYEE['username'], TEST_EMPLOYEE['username'], TEST_EMPLOYEE['password'], settings.retailer_pin) sleep(3) employee_acc = Account.objects().get(username=TEST_EMPLOYEE[\ 'username'], include="Employee") employee = employee_acc.employee except Exception as e: print e ########## Pending employee has no access try: test.login(TEST_EMPLOYEE['username'], TEST_EMPLOYEE['password'], reverse("employees_index")) parts[0]['success'] =\ test.find("#dialog-login-message").text ==\ "You are not yet approved." except Exception as e: print e parts[0]['test_message'] = str(e) try: # login to approve test.login(TEST_USER['username'], TEST_USER['password'], reverse("employees_index")) # approve test.find("#tab-pending-employees").click() approveRow = test.find("#tab-body-pending-employees " +\ "div.tr") approveRow.find_element_by_css_selector(\ "div.td.approve a.approve").click() sleep(1) test.switch_to_alert().accept() sleep(2) test.logout() except Exception as e: print e ########## Approved employee initially not in store ACL try: # store.ACL = None cannot retrieve Parse built-ins! account = Account.objects().get(email=TEST_USER['username'], include="Store.Settings") store = account.store parts[1]['success'] = employee_acc.objectId not in store.ACL except Exception as e: print e parts[1]['test_message'] = str(e) ########## Employee with ACCESS_NONE cannot login ### using the login dialog try: test.dev_login() test.login(TEST_EMPLOYEE['username'], TEST_EMPLOYEE['password']) parts[2]['success'] =\ test.find("#dialog-login-message").text ==\ "You do not have permission to access the dashboard." except Exception as e: print e parts[2]['test_message'] = str(e) ########## Employee with ACCESS_NONE cannot login ### using the dedicated login page try: test.login(TEST_EMPLOYEE['username'], TEST_EMPLOYEE['password'], reverse("employees_index")) parts[3]['success'] =\ test.find("#dialog-login-message").text ==\ "You do not have permission to access the dashboard." except Exception as e: print e parts[3]['test_message'] = str(e) ### Update the store's ACL store.ACL = {"*": {"read": True, "write": True}} store.set_access_level(employee_acc, ACCESS_PUNCHREDEEM[0]) store.update() test.new_driver(False) ########## Employee with ACCESS_PUNCHREDEEM can ### login to the dashboard through the login dialog try: test.login(TEST_EMPLOYEE['username'], TEST_EMPLOYEE['password'], final_sleep=6) parts[4]['success'] = test.is_current_url(reverse("store_index")) test.logout() except Exception as e: print e parts[4]['test_message'] = str(e) ########## Employee with ACCESS_PUNCHREDEEM can ### login to the dashboard through the dedicated dialog page try: test.dev_login() test.login(TEST_EMPLOYEE['username'], TEST_EMPLOYEE['password'], reverse("employees_index"), final_sleep=6) parts[5]['success'] = test.is_current_url(reverse("employees_index")) sleep(4) except Exception as e: print e parts[5]['test_message'] = str(e) ########## Account settings accessible try: test.find("#header-right a").click() sleep(2) parts[6]['success'] = test.is_current_url(reverse("account_edit")) except Exception as e: print e parts[6]['test_message'] = str(e) ########## No edit store detail button try: test.open(reverse("store_index")) sleep(2) test.set_to_implicit_wait(False) try: test.find("#store-details a[href='%s']" % (reverse("store_edit"), )) except NoSuchElementException: parts[7]['success'] = True except Exception as e: print e parts[7]['test_message'] = str(e) finally: test.set_to_implicit_wait(True) ########## Requesting edit store detail through url ### redirects user to store details try: test.open(reverse("store_edit")) sleep(2) parts[8]['success'] = test.is_current_url(reverse("store_index")+\ "?" + urlencode({'error': "Permission denied"})) except Exception as e: print e parts[8]['test_message'] = str(e) ########## No update subscription button try: test.set_to_implicit_wait(False) try: test.find("#account-options a[href='%s']" %\ (reverse("subscription_update"),)) except NoSuchElementException: parts[9]['success'] = True except Exception as e: print e parts[9]['test_message'] = str(e) finally: test.set_to_implicit_wait(True) ########## Requesting update subscription through url ### redirects user to store index try: test.open(reverse("subscription_update")) sleep(2) parts[10]['success'] = test.is_current_url(reverse(\ "store_index")+ "?" + urlencode({'error':\ "Permission denied"})) except Exception as e: print e parts[10]['test_message'] = str(e) ########## No deactivate my store button try: test.set_to_implicit_wait(False) try: test.find("#deactivate_account") except NoSuchElementException: parts[11]['success'] = True except Exception as e: print e parts[11]['test_message'] = str(e) finally: test.set_to_implicit_wait(True) ########## Requesting store deactivation through url ### redirects user to store index try: test.open(reverse("store_deactivate")) sleep(2) parts[12]['success'] = test.is_current_url(reverse(\ "store_index")+ "?" + urlencode({'error':\ "Permission denied"})) except Exception as e: print e parts[12]['test_message'] = str(e) ########## Rewards accessible try: test.open(reverse("rewards_index")) sleep(2) parts[13]['success'] = test.is_current_url(reverse("rewards_index")) except Exception as e: print e parts[13]['test_message'] = str(e) ########## No create new reward button try: test.set_to_implicit_wait(False) try: test.find("#add_reward") except NoSuchElementException: parts[14]['success'] = True except Exception as e: print e parts[14]['test_message'] = str(e) finally: test.set_to_implicit_wait(True) ########## Requesting create new reward through url ### redirects user to rewards index try: test.open(reverse("reward_edit", args=(-1, ))) sleep(2) parts[15]['success'] = test.is_current_url(reverse(\ "rewards_index")+ "?" + urlencode({'error':\ "Permission denied"})) except Exception as e: print e parts[15]['test_message'] = str(e) ########## Rewards are not clickable try: test.set_to_implicit_wait(False) try: test.find("div.tr.reward a") except NoSuchElementException: parts[16]['success'] = True except Exception as e: print e parts[16]['test_message'] = str(e) finally: test.set_to_implicit_wait(True) ########## Requesting edit reward through url ### redirects user to rewards index try: test.open(reverse("reward_edit", args=\ (int(test.find("div.tr.reward").get_attribute("id")),))) sleep(3) parts[17]['success'] = test.is_current_url(reverse(\ "rewards_index")+ "?" + urlencode({'error':\ "Permission denied"})) except Exception as e: print e parts[17]['test_message'] = str(e) ########## Messages accessible try: test.open(reverse("messages_index")) sleep(3) parts[18]['success'] = test.is_current_url(reverse(\ "messages_index")) except Exception as e: print e parts[18]['test_message'] = str(e) ########## No create new message button try: test.set_to_implicit_wait(False) try: test.find("#create_message") except NoSuchElementException: parts[19]['success'] = True except Exception as e: print e parts[19]['test_message'] = str(e) finally: test.set_to_implicit_wait(True) ########## Requesting create new message through url ### redirects user to messages index try: test.open(reverse("message_edit", args=("0", ))) sleep(3) parts[20]['success'] = test.is_current_url(reverse(\ "messages_index")+ "?" + urlencode({'error':\ "Permission denied"})) except Exception as e: print e parts[20]['test_message'] = str(e) ########## Sent messages are viewable try: row = test.find("#tab-body-sent div.tr a") # get id from /manage/messages/H1llFritVJ/details message_id = MESSAGE_DETAIL_RE.search(\ row.get_attribute("href")).group(1) row.click() sleep(3) parts[21]['success'] = test.is_current_url(reverse(\ "message_details", args=(message_id,))) except Exception as e: print e parts[21]['test_message'] = str(e) ########## Feedbacks are viewable try: test.open(reverse("messages_index")) sleep(2) test.find("#tab-feedback").click() sleep(1) row = test.find("#tab-body-feedback div.tr a") # get id from /manage/messages/feedback/H1llFritVJ feedback_id = row.get_attribute("href").split("/")[-1] row.click() sleep(3) parts[22]['success'] = test.is_current_url(reverse(\ "feedback_details", args=(feedback_id,))) except Exception as e: print e parts[22]['test_message'] = str(e) ########## No reply button try: test.set_to_implicit_wait(False) try: test.find("#reply-button") except NoSuchElementException: parts[23]['success'] = True except Exception as e: print e parts[23]['test_message'] = str(e) finally: test.set_to_implicit_wait(True) ########## Requesting reply through url ### redirects user to messages index try: test.open(reverse("feedback_reply", args=(feedback_id, ))) sleep(3) parts[24]['success'] = test.is_current_url(reverse(\ "messages_index")+ "?" + urlencode({'error':\ "Permission denied"}) + "&tab_feedback=1") except Exception as e: print e parts[24]['test_message'] = str(e) ########## No delete message button try: test.open(reverse("feedback_details", args=(feedback_id, ))) sleep(3) test.set_to_implicit_wait(False) try: test.find("#delete-button") except NoSuchElementException: parts[25]['success'] = True except Exception as e: print e parts[25]['test_message'] = str(e) finally: test.set_to_implicit_wait(True) ########## Requesting delete message through url ### redirects user to messages index try: test.open(reverse("feedback_delete", args=(feedback_id, ))) sleep(2) parts[26]['success'] = test.is_current_url(reverse(\ "messages_index")+ "?" + urlencode({'error':\ "Permission denied"}) + "&tab_feedback=1") except Exception as e: print e parts[26]['test_message'] = str(e) ########## Analysis accessible try: test.open(reverse("analysis_index")) sleep(4) parts[27]['success'] = test.is_current_url(reverse(\ "analysis_index")) except Exception as e: print e parts[27]['test_message'] = str(e) ########## Employees accessible try: test.open(reverse("employees_index")) sleep(4) parts[28]['success'] = test.is_current_url(reverse(\ "employees_index")) except Exception as e: print e parts[28]['test_message'] = str(e) ########## No register new employee button try: test.set_to_implicit_wait(False) try: test.find("#register_employee") except NoSuchElementException: parts[29]['success'] = True except Exception as e: print e parts[29]['test_message'] = str(e) finally: test.set_to_implicit_wait(True) ########## Requesting new employee registration ### redirects user to employees_index try: test.open(reverse("employee_register")) sleep(3) parts[30]['success'] = test.is_current_url(reverse(\ "employees_index")+ "?" + urlencode({'error':\ "Permission denied"})) except Exception as e: print e parts[30]['test_message'] = str(e) ########## Approved employees are not clickable try: test.set_to_implicit_wait(False) try: test.find("#tab-body-approved-employees div.tr a") except NoSuchElementException: parts[31]['success'] = True except Exception as e: print e parts[31]['test_message'] = str(e) finally: test.set_to_implicit_wait(True) ########## Requesting edit employee through url ### redirects user to employees index try: row = test.find("#tab-body-approved-employees div.tr") employee_id = row.get_attribute("id") test.open(reverse("employee_edit", args=(employee_id, ))) sleep(3) parts[32]['success'] = test.is_current_url(reverse(\ "employees_index")+ "?" + urlencode({'error':\ "Permission denied"})) except Exception as e: print e parts[32]['test_message'] = str(e) ########## No remove button in approved employees try: test.set_to_implicit_wait(False) try: test.find("#tab-body-approved-employees div.tr "+\ "div.remove a") except NoSuchElementException: parts[33]['success'] = True except Exception as e: print e parts[33]['test_message'] = str(e) finally: test.set_to_implicit_wait(True) ########## Requesting remove employee through url ### redirects user to employees index try: test.open(reverse("employee_delete", args=(employee_id, ))) sleep(3) parts[34]['success'] = test.is_current_url(reverse(\ "employees_index")+ "?" + urlencode({'error':\ "Permission denied"})) except Exception as e: print e parts[34]['test_message'] = str(e) # create a pending register_rand_employee(store.objectId) test.new_driver(False) test.login(TEST_EMPLOYEE['username'], TEST_EMPLOYEE['password'], reverse("employees_index"), final_sleep=6) ########## No deny button in pending employees try: test.find("#tab-pending-employees").click() sleep(1) test.set_to_implicit_wait(False) try: test.find("#tab-body-pending-employees div.tr "+\ "div.deny a") except NoSuchElementException: parts[35]['success'] = True except Exception as e: print e parts[35]['test_message'] = str(e) finally: test.set_to_implicit_wait(True) ########## Requesting deny employee through url ### redirects user to employees index try: test.find("#tab-pending-employees").click() row = test.find("#tab-body-pending-employees div.tr") employee_id = row.get_attribute("id") test.open(reverse("employee_deny", args=(employee_id, ))) sleep(3) parts[36]['success'] = test.is_current_url(reverse(\ "employees_index") + "?" + urlencode({'error':\ "Permission denied"})) except Exception as e: print e parts[36]['test_message'] = str(e) ########## No approve button in pending employees try: test.find("#tab-pending-employees").click() sleep(1) test.set_to_implicit_wait(False) try: test.find("#tab-body-pending-employees div.tr "+\ "div.approve a") except NoSuchElementException: parts[37]['success'] = True except Exception as e: print e parts[37]['test_message'] = str(e) finally: test.set_to_implicit_wait(True) ########## Requesting approve employee through url ### redirects user to employees index try: test.open(reverse("employee_approve", args=(employee_id, ))) sleep(3) parts[38]['success'] = test.is_current_url(reverse(\ "employees_index") + "?" + urlencode({'error':\ "Permission denied"})) except Exception as e: print e parts[38]['test_message'] = str(e) ########## Settings accessible try: test.open(reverse("store_settings")) sleep(3) parts[39]['success'] = test.is_current_url(reverse(\ "store_settings")) except Exception as e: print e parts[39]['test_message'] = str(e) ########## No refresh button for retailer pin try: test.set_to_implicit_wait(False) try: test.find("#link_refresh_retailer_pin") except NoSuchElementException: parts[40]['success'] = True except Exception as e: print e parts[40]['test_message'] = str(e) finally: test.set_to_implicit_wait(True) ########## Requesting refresh through url returns ### a json object with error Permission denied try: test.open(reverse("refresh_retailer_pin")) sleep(3) parts[41]['success'] = "Permission denied" in\ test.driver.page_source except Exception as e: print e parts[41]['test_message'] = str(e) ########## Punches employee is readonly try: test.open(reverse("store_settings")) sleep(3) parts[42]['success'] =\ test.find("#id_punches_employee").get_attribute(\ "readonly") == "true" except Exception as e: print e parts[42]['test_message'] = str(e) ########## Punches facebook is readonly try: parts[43]['success'] =\ test.find("#id_punches_facebook").get_attribute(\ "readonly") == "true" except Exception as e: print e parts[43]['test_message'] = str(e) ########## No save button try: test.set_to_implicit_wait(False) try: test.find("#settings-form-submit") except NoSuchElementException: parts[44]['success'] = True except Exception as e: print e parts[44]['test_message'] = str(e) finally: test.set_to_implicit_wait(True) ########## No cancel changes button try: test.set_to_implicit_wait(False) try: test.find("#settings-options a.red") except NoSuchElementException: parts[45]['success'] = True except Exception as e: print e parts[45]['test_message'] = str(e) finally: test.set_to_implicit_wait(True) ########## Workbench accessible try: test.open(reverse("workbench_index")) sleep(3) parts[46]['success'] = test.is_current_url(\ reverse("workbench_index")) except Exception as e: print e parts[46]['test_message'] = str(e) ########## Employee can punch try: test.find("#punch_code").send_keys("00010") test.find("#punch_amount").send_keys("1") test.find("#punch-form a.button.blue").click() sleep(2) parts[47]['success'] =\ test.find(".notification.hide") is not None except Exception as e: print e parts[47]['test_message'] = str(e) ########## Employee can reject redeem try: request_redeem_ps("eiZa6Mzu7f") sleep(COMET_PULL_RATE * 2 + 4) row = test.find("#tab-body-pending-redemptions div.tr") test.find("//div[@id='%s']/" % (row.get_attribute("id"),) +\ "div[contains(@class, 'redemption_redeem')]/a[2]", type="xpath").click() sleep(3) parts[48]['success'] = row.text.__contains__("Successfully") except Exception as e: print e parts[48]['test_message'] = str(e) ########## Employee can validate redeem try: request_redeem_ps("eiZa6Mzu7f") sleep(COMET_PULL_RATE * 2 + 4) row = test.find("#tab-body-pending-redemptions div.tr") test.find("//div[@id='%s']/" % (row.get_attribute("id"),) +\ "div[contains(@class, 'redemption_redeem')]/a[1]", type="xpath").click() sleep(3) parts[49]['success'] = row.text.__contains__("Successfully") except Exception as e: print e parts[49]['test_message'] = str(e) # END OF ALL TESTS - cleanup return test.tear_down()
def test_redemptions(): """ Tests for redemptions section of the workbench. Does not test offers/gifts. Assumes that at least 1 PatronStore exists for the above Store. Assumes that at least 1 reward exists for the above Store. """ account = Account.objects().get(username=TEST_USER['username'], include="Store.Settings") store = account.store settings = store.settings ps = store.get("patronStores", include="Patron", limit=1)[0] patron = ps.patron reward = store.rewards[0] reward_id = reward["reward_id"] test = SeleniumTest() parts = [ {'test_name': "User needs to be logged in to access page"}, {'test_name': "Approving redeem works"}, {'test_name': "PatronStore's punch_count is updated"}, {'test_name': "PatronStore's pending_reward is set to false"}, {'test_name': "The reward's redemption_count is updated"}, {'test_name': "Rejecting redeem works"}, {'test_name': "PatronStore's punch_count remains the same"}, {'test_name': "PatronStore's pending_reward is set to false"}, {'test_name': "Approved redeem is in the history tab"}, {'test_name': "Rejected redeem is not in the history tab"}, {'test_name': "Approving a redeem when the PatronStore does" +\ " not have enough punches shows error"}, {'test_name': "PatronStore's punch_count remains the same"}, {'test_name': "PatronStore's pending_reward is set to false"}, {'test_name': "Approving redeem from a PatronStore that does" +\ " not exist shows error"}, ] section = { "section_name": "Redeems working properly?", "parts": parts, } test.results.append(section) ########## User needs to be logged in to access page test.open(reverse("workbench_index")) # ACTION! sleep(1) parts[0]['success'] = test.is_current_url(reverse(\ 'manage_login') + "?next=" + reverse("workbench_index")) # login selectors = ( ("#login_username", TEST_USER['username']), ("#login_password", TEST_USER['password']), ("", Keys.RETURN) ) test.action_chain(0, selectors, "send_keys") # ACTION! sleep(5) ########## Approving redeem works try: punch_count = ps.punch_count red_count = reward["redemption_count"] request_redeem_ps(ps.objectId, reward_id=reward_id, num_punches=1) sleep(COMET_PULL_RATE*2+4) row_id = test.find("#tab-body-pending-redemptions div.tr").get_attribute("id") test.find("//div[@id='tab-body-pending-redemptions']/"+\ "div[contains(@class, 'tr')]/"+\ "div[contains(@class, 'redemption_redeem')]/a[1]", type="xpath").click() sleep(4) parts[1]['success'] = test.find("#"+row_id).text.__contains__("Successfully") except Exception as e: print e parts[1]['test_message'] = str(e) ########## PatronStore's punch_count is updated try: ps.punch_count = None parts[2]['success'] = punch_count-1 == ps.get("punch_count") except Exception as e: print e parts[2]['test_message'] = str(e) ########## PatronStore's pending_reward is set to false try: ps.pending_reward = None parts[3]['success'] = not ps.get("pending_reward") except Exception as e: print e parts[3]['test_message'] = str(e) ########## The reward's redemption_count is updated try: store.rewards = None parts[4]['success'] = red_count+1 ==\ store.get("rewards")[0]["redemption_count"] except Exception as e: print e parts[4]['test_message'] = str(e) ########## Rejecting redeem works TODO try: pass except Exception as e: print e parts[5]['test_message'] = str(e) ########## PatronStore's punch_count remains the same TODO try: pass except Exception as e: print e parts[6]['test_message'] = str(e) ########## PatronStore's pending_reward is set to false TODO try: pass except Exception as e: print e parts[7]['test_message'] = str(e) ########## Approved redeem is in the history tab TODO try: pass except Exception as e: print e parts[8]['test_message'] = str(e) ########## Rejected redeem is not in the history tab TODO try: pass except Exception as e: print e parts[9]['test_message'] = str(e) ########## Approving a redeem when the PatronStore does ### not have enough punches shows error TODO try: pass except Exception as e: print e parts[10]['test_message'] = str(e) ########## PatronStore's punch_count remains the same TODO try: pass except Exception as e: print e parts[11]['test_message'] = str(e) ########## PatronStore's pending_reward is set to false TODO try: pass except Exception as e: print e parts[12]['test_message'] = str(e) ########## Approving redeem from a PatronStore that does ### not exist shows error TODO try: pass except Exception as e: print e parts[13]['test_message'] = str(e) # END OF ALL TESTS - cleanup return test.tear_down()
def test_update_subscription(): # TODO test place_order account = Account.objects().get(username=TEST_USER['username'], include="Store.Subscription") store = account.store subscription = store.subscription subscription.update_locally(SUBSCRIPTION_INFO, False) subscription.update() test = SeleniumTest() parts = [ {'test_name': "User needs to be logged in to access page"}, {'test_name': "Update account page reachable"}, {'test_name': "Changes to first name are visible"}, {'test_name': "Changes to first name are saved to parse"}, {'test_name': "Changes to last name are visible"}, {'test_name': "Changes to last name are saved to parse"}, {'test_name': "Changes to card number are visible"}, {'test_name': "Changes to card number are saved to parse"}, {'test_name': "A paypal credit card id is generated & saved"}, {'test_name': "Changes to cc expiration are visible"}, {'test_name': "Changes to cc expiration are saved to parse"}, {'test_name': "Changes to address are visible"}, {'test_name': "Changes to address are saved to parse"}, {'test_name': "Changes to city are visible"}, {'test_name': "Changes to city are saved to parse"}, {'test_name': "Changes to state are visible"}, {'test_name': "Changes to state are saved to parse"}, {'test_name': "Changes to zip are visible"}, {'test_name': "Changes to zip are saved to parse"}, {'test_name': "First name is required"}, {'test_name': "Last name is required"}, {'test_name': "Card number is required"}, {'test_name': "Security code (cvc) is required"}, {'test_name': "Address is required"}, {'test_name': "City is required"}, {'test_name': "State is required"}, {'test_name': "Zip is required"}, {'test_name': "ToS checked is required"}, {'test_name': "Invalid credit card number shows error"}, {'test_name': "Past expiration date is invalid"}, {'test_name': "Only the last 4 digits of the card number" +\ " are shown"}, {'test_name': "Not changing the card number does not " +\ "generate new paypal credit card id"}, ] section = { "section_name": "Edit account/subscription working properly?", "parts": parts, } self.results.append(section) ########## User needs to be logged in to access page self.open(reverse("store_index")) sleep(1) parts[0]['success'] = self.is_current_url(reverse(\ 'manage_login') + "?next=" + reverse("store_index")) # login selectors = (("#login_username", TEST_USER['username']), ("#login_password", TEST_USER['password']), ("", Keys.RETURN)) self.action_chain(0, selectors, "send_keys") sleep(7) ########## Update account page reachable try: self.find("//div[@id='account-options']/a[1]", type="xpath").click() sleep(3) parts[1]['success'] =\ self.is_current_url(reverse("subscription_update")) except Exception as e: print e parts[1]['test_message'] = str(e) ## Make changes # first clear all inputs for el in self.find("input[type='text']", multiple=True): el.clear() selectors = ( ("#id_first_name", TEST_SUBSCRIPTION_INFO['first_name']), ("#id_last_name", TEST_SUBSCRIPTION_INFO['last_name']), ("#id_cc_number", TEST_SUBSCRIPTION_INFO['cc_number']), ("#id_cc_cvv", "905"), ("#id_address", TEST_SUBSCRIPTION_INFO['address']), ("#id_city", TEST_SUBSCRIPTION_INFO['city']), ("#id_state", TEST_SUBSCRIPTION_INFO['state']), ("#id_zip", TEST_SUBSCRIPTION_INFO['zip']), ) self.action_chain(0, selectors, action="send_keys") month_el =\ self.find("//select[@id='id_date_cc_expiration_month']/" +\ "option[@value='%s']" % (str(TEST_SUBSCRIPTION_INFO[\ 'date_cc_expiration'].month),), type="xpath") year_el =\ self.find("//select[@id='id_date_cc_expiration_year']/" +\ "option[@value='%s']" % (str(TEST_SUBSCRIPTION_INFO[\ 'date_cc_expiration'].year),), type="xpath") month = month_el.get_attribute("value") year = year_el.get_attribute("value") month_el.click() year_el.click() self.find("#id_recurring").click() self.find("#update-form-submit").click() sleep(5) # back to update account page self.find("//div[@id='account-options']/a[1]", type="xpath").click() sleep(3) ########## Changes to first name are visible parts[2]['success'] =\ self.find("#id_first_name").get_attribute("value") ==\ TEST_SUBSCRIPTION_INFO['first_name'] ########## Changes to first name are saved to parse subscription.first_name = None parts[3]['success'] = subscription.get("first_name") ==\ TEST_SUBSCRIPTION_INFO['first_name'] ########## Changes to last name are visible parts[4]['success'] =\ self.find("#id_last_name").get_attribute("value") ==\ TEST_SUBSCRIPTION_INFO['last_name'] ########## Changes to last name are saved to parse subscription.last_name = None parts[5]['success'] = subscription.get("last_name") ==\ TEST_SUBSCRIPTION_INFO['last_name'] ########## Changes to card number are visible parts[6]['success'] =\ self.find("#id_cc_number").get_attribute("value")[-4:] ==\ TEST_SUBSCRIPTION_INFO['cc_number'][-4:] ########## Changes to card number are saved to parse subscription.cc_number = None parts[7]['success'] = subscription.get("cc_number") ==\ TEST_SUBSCRIPTION_INFO['cc_number'][-4:] ########## A paypal credit card id is generated & saved # CARD-97223025G70599255KHHCCVQ subscription.pp_cc_id = None parts[8]['success'] = subscription.get("pp_cc_id").__contains__(\ "CARD") and len(subscription.get("pp_cc_id")) == 29 ########## Changes to cc expiration are visible parts[9]['success'] = month == self.get_selected("//select" +\ "[@id='id_date_cc_expiration_month']/option", type="xpath").get_attribute("value") and\ year == self.get_selected(\ "//select[@id='id_date_cc_expiration_year']/option", type="xpath").get_attribute("value") ########## Changes to cc expiration are saved to parse subscription.date_cc_expiration = None exp = subscription.get("date_cc_expiration") parts[10]['success'] = exp.month == TEST_SUBSCRIPTION_INFO[\ 'date_cc_expiration'].month and exp.year ==\ TEST_SUBSCRIPTION_INFO['date_cc_expiration'].year ########## Changes to address are visible parts[11]['success'] =\ self.find("#id_address").get_attribute("value") ==\ TEST_SUBSCRIPTION_INFO['address'] ########## Changes to address are saved to parse subscription.address = None parts[12]['success'] = subscription.get("address") ==\ TEST_SUBSCRIPTION_INFO['address'] ########## Changes to city are visible parts[13]['success'] =\ self.find("#id_city").get_attribute("value") ==\ TEST_SUBSCRIPTION_INFO['city'] ########## Changes to city are saved to parse subscription.city = None parts[14]['success'] = subscription.get("city") ==\ TEST_SUBSCRIPTION_INFO['city'] ########## Changes to state are visible self.find("//select[@id='id_date_cc_expiration_year']/" +\ "option[@value='%s']" % (str(TEST_SUBSCRIPTION_INFO[\ 'date_cc_expiration'].year)), type="xpath") parts[15]['success'] =\ self.find("#id_state").get_attribute("value") ==\ TEST_SUBSCRIPTION_INFO['state'] ########## Changes to state are saved to parse subscription.state = None parts[16]['success'] = subscription.get("state") ==\ TEST_SUBSCRIPTION_INFO['state'] ########## Changes to zip are visible parts[17]['success'] =\ self.find("#id_zip").get_attribute("value") ==\ TEST_SUBSCRIPTION_INFO['zip'] ########## Changes to zip are saved to parse subscription.zip = None parts[18]['success'] = subscription.get("zip") ==\ TEST_SUBSCRIPTION_INFO['zip'] ## Make changes selectors = [ "#id_first_name", "#id_last_name", "#id_cc_number", "#id_cc_cvv", "#id_address", "#id_city", "#id_state", "#id_zip", ] self.action_chain(0, selectors, action="clear") for i in range(len(selectors)): selectors[i] = (selectors[i], " ") self.action_chain(0, selectors, action="send_keys") self.find("#update-form-submit").click() sleep(3) ########## First name is required ########## Last name is required ########## Card number is required ########## Security code (cvc) is required ########## Address is required ########## City is required ########## State is required ########## Zip is required ########## ToS checked is required def field_is_required(part, selector, message="This field is required."): try: parts[part]['success'] = self.find(selector).text == message except Exception as e: print e parts[part]['test_message'] = str(e) selectors = ( (19, "#first_name_ic ul.errorlist li"), (20, "#last_name_ic ul.errorlist li"), (21, "#card_number_container ul.errorlist li", "Enter a valid credit card number."), (22, "#cc_cvv_ic ul.errorlist li"), (23, "#address_ic ul.errorlist li"), (24, "#city_ic ul.errorlist li"), (25, "#state_ic ul.errorlist li"), (26, "#zip_ic ul.errorlist li"), (27, "#recurring_charge_container ul.errorlist li", "You must accept the Terms & Conditions to continue."), ) for selector in selectors: field_is_required(*selector) ########## Invalid credit card number shows error try: cc_number = self.find("#id_cc_number") cc_number.clear() cc_number.send_keys("8769233313929990") self.find("#update-form-submit").click() sleep(3) parts[28]['success'] = self.find("#card_number_container " +\ "ul.errorlist li").text ==\ "Enter a valid credit card number." except Exception as e: print e parts[28]['test_message'] = str(e) ########## Past expiration date is invalid if timezone.now().month == 1: # note that if this test is being run on a January, this will # fail so to prevent that just skip the test if it is January parts[29]['test_message'] = "READ ME. This test has been " +\ "skipped because the month is January, which means " +\ "that this test will always fail due to limited " +\ "select options." else: try: # select january of this year. self.find("//select[@id='id_date_cc_expiration_month']/" +\ "option[@value='1']", type="xpath").click() self.find("//select[@id='id_date_cc_expiration_year']/" +\ "option[@value='%s']" %\ (str(timezone.now().year),), type="xpath").click() self.find("#update-form-submit").click() sleep(3) parts[29]['success'] =\ self.find("#date_cc_expiration_ic ul.errorlist " +\ "li").text == "Your credit card has expired!" except Exception as e: print e parts[29]['test_message'] = str(e) ########## Only the last 4 digits of the card number are shown try: self.find("//div[@class='form-options']/a[2]", type="xpath").click() sleep(1) self.find("//div[@id='account-options']/a[1]", type="xpath").click() sleep(3) masked_number =\ self.find("#id_cc_number").get_attribute("value") parts[30]['success'] = masked_number[:-4] ==\ "************" and str(masked_number[-4:]).isdigit() except Exception as e: print e parts[30]['test_message'] = str(e) ########## Not changing the card number does not ###### generate new paypal credit card id try: subscription.pp_cc_id = None cc_id = subscription.get("pp_cc_id") self.find("#id_cc_cvv").send_keys("123") self.find("#id_recurring").click() self.find("#update-form-submit").click() sleep(5) subscription.pp_cc_id = None parts[31]['success'] = cc_id == subscription.get("pp_cc_id") except Exception as e: print e parts[31]['test_message'] = str(e) # END OF ALL TESTS - cleanup return self.tear_down()
def test_cancel_account(): """ A test just for the cancel account link. """ account = Account.objects().get(username=TEST_USER['username'], include="Store") store = account.store test = SeleniumTest() parts = [ {'test_name': "User needs to be logged in to access page"}, {'test_name': "Clicking the cancel button brings up a " +\ "confirmation dialog"}, {'test_name': "Clicking cancel on the dialog dimisses the " +\ "dialog and the account remains active"}, {'test_name': "Clicking OK logs the user out"}, {'test_name': "Clicking OK sets the store's active field " +\ "to false on Parse"}, ] section = { "section_name": "Deactivate store link functional?", "parts": parts, } self.results.append(section) ########## User needs to be logged in to access page self.open(reverse("store_index")) sleep(1) parts[0]['success'] = self.is_current_url(reverse(\ 'manage_login') + "?next=" + reverse("store_index")) # login selectors = (("#login_username", TEST_USER['username']), ("#login_password", TEST_USER['password']), ("", Keys.RETURN)) self.action_chain(0, selectors, "send_keys") sleep(7) ########## Clicking the cancel button brings up a confrmtn dialog try: self.find("#deactivate_account").click() sleep(1) alert = self.switch_to_alert() parts[1]['success'] = alert is not None except Exception as e: print e parts[1]['test_message'] = str(e) ########## Clicking cancel on the dialog dimisses the ### dialog and the account remains active try: alert.dismiss() try: alert.text except NoAlertPresentException: store.active = None parts[2]['success'] = store.get("active") except Exception as e: print e parts[2]['test_message'] = str(e) ########## Clicking OK logs the user out try: self.find("#deactivate_account").click() sleep(1) alert = self.switch_to_alert() alert.accept() sleep(4) if SeleniumTest.DEV_LOGIN: parts[3]["success"] =\ self.is_current_url(reverse("manage_dev_login")+"?next=/") else: parts[3]["success"] =\ self.is_current_url(reverse("public_home")) except Exception as e: print e parts[3]['test_message'] = str(e) ########## Clicking OK sets the store's active ### field to false on Parse store.active = None parts[4]['success'] = not store.get("active") # undo store.active = True store.update() # END OF ALL TESTS - cleanup return self.tear_down()
def test_feedbacks(): """ This test also tests cloud code send_feedback. """ # we can clear the list locally but just re-pull from parse account = Account.objects().get(username=TEST_USER['username'], include="Store.Subscription") store = account.store subscription = store.subscription account = Account.objects().get(username=TEST_PATRON['username'], include="Patron") patron = account.patron # make sure that the account is not used for these tests account = None # clear the received messages relation received_messages = store.get("receivedMessages", keys="") if received_messages: store.remove_relation("ReceivedMessages_", [m.objectId for m in received_messages]) store.set("receivedMessages", None) test = SeleniumTest() parts = [ {'test_name': "User needs to be logged in to access page"}, {'test_name': "Cloud code send_feedback works"}, {'test_name': "Notification badge appears if there are" +\ " unread feedbacks"}, {'test_name': "A new row appears when feedback is received"}, {'test_name': "Feedback is initially unread (dashboard)"}, {'test_name': "Feedback is in store's ReceivedMessages " +\ "relation and is initially unread"}, {'test_name': "Clicking the row redirects user to the " +\ "feedback detail page"}, {'test_name': "Clicking back to feedback inbox redirects " +\ "user back to messages index with feedback tab active"}, {'test_name': "Feedback is now read (dashboard)"}, {'test_name': "Feedback is now read (Parse)"}, {'test_name': "Clicking reply redirects user to feedback " +\ "reply page"}, {'test_name': "Reply body is required."}, {'test_name': "Replying redirects user back to feedback " +\ "details"}, {'test_name': "The reply is visible"}, {'test_name': "The reply message is saved in the store's " +\ "sent messages relation with message_type of feedback"}, {'test_name': "The reply message is saved in the Patron's " +\ "received messages relation wrapped in a Message Status"}, {'test_name': "A feedback with a reply does not have a " +\ "reply button"}, {'test_name': "Clicking delete message prompts the user " +\ "to confirm the deletion"}, {'test_name': "The user is redirected to messages index " +\ "with feedback tab active"}, {'test_name': "Deleting the reply only removes the message" +\ " from the store's sent messages relation"}, {'test_name': "The deleted feedback is no longer in " +\ "the table"}, {'test_name': "Multiple feedbacks (testing 3 here) " +\ "can appear at the same time"}, ] section = { "section_name": "Receiving messages works?", "parts": parts, } test.results.append(section) ########## User needs to be logged in to access page test.open(reverse("messages_index")) # ACTION! sleep(1) parts[0]['success'] = test.is_current_url(reverse(\ 'manage_login') + "?next=" + reverse("messages_index")) # login selectors = ( ("#login_username", TEST_USER['username']), ("#login_password", TEST_USER['password']), ("", Keys.RETURN) ) test.action_chain(0, selectors, "send_keys") # ACTION! sleep(5) def send_feedback(subject, body): """ This is a consumer action - not dashboard """ return cloud_call("send_feedback", { "store_id": store.objectId, "patron_id": patron.objectId, "sender_name": patron.get_fullname(), "subject": subject, "body": body, }) def feedback_id(feedback_tr): return feedback_tr.find_element_by_css_selector(\ "a").get_attribute("href").split("/")[-1] def feedback_unread(feedback_tr): """ check parse if the fb is unread """ fb_id = feedback_id(feedback_tr) store.set("receivedMessages", None) msg = store.get("receivedMessages", objectId=fb_id) if msg and len(msg) > 0: return not msg[0].is_read return False ########## Cloud code send_feedback works try: parts[1]['success'] = send_feedback("feedback #1", "body #1").get("result") == "success" except Exception as e: print e parts[1]['test_message'] = str(e) ########## Notification badge appears if there are ### unread feedbacks try: sleep(COMET_PULL_RATE*2 + 2) parts[2]['success'] = test.element_exists("#messages-nav " +\ "a div.nav-item-badge") except Exception as e: print e parts[2]['test_message'] = str(e) ########## A new row appears when feedback is received try: test.find("#tab-feedback").click() feedbacks =\ test.find("#tab-body-feedback div.tr", multiple=True) parts[3]['success'] = len(feedbacks) > 0 except Exception as e: print e parts[3]['test_message'] = str(e) ########## Feedback is initially unread (dashboard) try: parts[4]['success'] =\ feedbacks[0].get_attribute("class").__contains__("unread") except Exception as e: print e parts[4]['test_message'] = str(e) ########## Feedback is in store's ReceivedMessages ### relation and is initially unread try: parts[5]['success'] = feedback_unread(feedbacks[0]) except Exception as e: print e parts[5]['test_message'] = str(e) ########## Clicking the row redirects user to the ### feedback detail page try: fb_id = feedback_id(feedbacks[0]) feedbacks[0].find_element_by_css_selector("a").click() sleep(2) parts[6]['success'] = test.is_current_url(reverse(\ "feedback_details", args=(fb_id,))) except Exception as e: print e parts[6]['test_message'] = str(e) ########## Clicking back to feedback inbox redirects ### user back to messages index with feedback tab active try: test.find("#back_to_feedback").click() sleep(1) parts[7]['success'] =\ test.find("#tab-feedback").get_attribute(\ "class").__contains__("active") except Exception as e: print e parts[7]['test_message'] = str(e) ########## Feedback is now read (dashboard) try: feedbacks =\ test.find("#tab-body-feedback div.tr", multiple=True) parts[8]['success'] = not feedbacks[0].get_attribute(\ "class").__contains__("unread") except Exception as e: print e parts[8]['test_message'] = str(e) ########## Feedback is now read (Parse) try: parts[9]['success'] = not feedback_unread(feedbacks[0]) except Exception as e: print e parts[9]['test_message'] = str(e) ########## Clicking reply redirects user to feedback reply page try: fb_id = feedback_id(feedbacks[0]) feedbacks[0].find_element_by_css_selector("a").click() sleep(2) test.find("#reply-button").click() sleep(1) parts[10]['success'] = test.is_current_url(reverse(\ "feedback_reply", args=(fb_id,))) except Exception as e: print e parts[10]['test_message'] = str(e) ########## Reply body is required. try: test.find("#body").send_keys(" ") test.find("#reply-form-submit").click() sleep(2) parts[11]['success'] = test.find("div.notification.hide " +\ "div").text == "Please enter a message." except Exception as e: print e parts[11]['test_message'] = str(e) ########## Replying redirects user back to feedback details try: test.find("#body").send_keys("Hey") test.find("#reply-form-submit").click() parts[12]['success'] = test.is_current_url(reverse(\ "feedback_details", args=(fb_id,)) +\ "?%s" % urlencode({'success':\ 'Reply has been sent.'})) sleep(5) except Exception as e: print e parts[12]['test_message'] = str(e) ########## The reply is visible try: parts[13]['success'] = test.find("#reply-box " +\ "div.sect.body").text == "Hey" except Exception as e: print e parts[13]['test_message'] = str(e) ########## The reply message is saved in the store's ### sent messages relation with message_type of feedback try: test.find("#back_to_feedback").click() sleep(1) store.set("sentMessages", None) store.set("receivedMessages", None) parts[14]['success'] = len(store.get("sentMessages", objectId=store.get("receivedMessages", objectId=fb_id)[0].Reply, message_type=FEEDBACK)) > 0 except Exception as e: print e parts[14]['test_message'] = str(e) ########## The reply message is saved in the Patron's ### received messages relation wrapped in a Message Status try: patron.set("receivedMessages", None) parts[15]['success'] = len(patron.get("receivedMessages", Message=fb_id)) > 0 except Exception as e: print e parts[15]['test_message'] = str(e) ########## A feedback with a reply does not have a reply button try: feedbacks =\ test.find("#tab-body-feedback div.tr", multiple=True) feedbacks[0].find_element_by_css_selector("a").click() sleep(2) parts[16]['success'] = not test.element_exists(\ "#reply-form-submit") except Exception as e: print e parts[16]['test_message'] = str(e) ########## Clicking delete message prompts the user ### to confirm the deletion try: test.find("#delete-button").click() sleep(1) alert = test.switch_to_alert() parts[17]['success'] = alert.text ==\ "Are you sure you want to delete this feedback thread?" except Exception as e: print e parts[17]['test_message'] = str(e) ########## The user is redirected to messages index ### with feedback tab active try: alert.accept() sleep(4) parts[18]['success'] =\ test.find("#tab-feedback").get_attribute(\ "class").__contains__("active") except Exception as e: print e parts[18]['test_message'] = str(e) ########## Deleting the reply only removes the message ### from the store's sent messages relation try: store.set("receivedMessages", None) parts[19]['success'] = not store.get("receivedMessages", objectId=fb_id, message_type=FEEDBACK) except Exception as e: print e parts[19]['test_message'] = str(e) ########## The deleted feedback is no longer in the table try: test.find("#tab-feedback").click() sleep(1) feedbacks =\ test.find("#tab-body-feedback div.tr a[href='%s']" %\ (fb_id,), multiple=True) parts[20]['success'] = len(feedbacks) == 0 except Exception as e: print e parts[20]['test_message'] = str(e) ########## Multiple feedbacks (testing 3 here) ### can appear at the same time try: send_feedback("feedback #2", "body #2") send_feedback("feedback #3", "body #3") send_feedback("feedback #4", "body #4") sleep(COMET_PULL_RATE*3 + 4) feedbacks =\ test.find("#tab-body-feedback div.tr a", multiple=True) parts[21]['success'] = len(feedbacks) == 3 except Exception as e: print e parts[21]['test_message'] = str(e) # END OF ALL TESTS - cleanup return test.tear_down()
def test_employee_registration(): """ Tests for employee registrain via dashboard. """ # delete the employees and associated User objects in the relation account = Account.objects().get(email=TEST_USER['username'], include="Store.Settings") store = account.store settings = store.settings emps = store.get("employees") if emps: for emp in emps: Account.objects().get(Employee=emp.objectId).delete() emp.delete() store.set("employees", None) test = SeleniumTest() parts = [ {"test_name" : "Clicking register new employee button "+\ " redirects user to employee register page."}, {"test_name" : "New registered employee is immediately placed"+\ " in the approved group."}, {"test_name" : "Access level of new employee is the same as"+\ " the one chosen in the registration form."}, {"test_name" : "A valid email is required."}, {"test_name" : "Email must be unique."}, {"test_name" : "Email is required."}, {"test_name" : "First name is required."}, {"test_name" : "Last name is required."}, {"test_name" : "Password is required."}, {"test_name" : "Password confirm is required."}, {"test_name" : "Passwords must match."}, {"test_name" : "Password must be at least 6 characters long."}, ] section = { "section_name":\ "Employee registration functional?", "parts": parts, } test.results.append(section) test.login(TEST_USER['username'], TEST_USER['password'], reverse("employees_index")) ########## Clicking register new employee button ### redirects user to employee register page. try: test.find("#register_employee").click() sleep(3) parts[0]['success'] = test.is_current_url(reverse("employee_register")) except Exception as e: print e parts[0]['test_message'] = str(e) ########## New registered employee is immediately placed ### in the approved group. try: acl = test.find("#id_acl option[selected]").get_attribute("value") selectors = ( ("#id_email", TEST_EMPLOYEE['username']), ("#id_password", TEST_EMPLOYEE['password']), ("#id_confirm_password", TEST_EMPLOYEE['password']), ("#id_first_name", TEST_EMPLOYEE['first_name']), ("#id_last_name", TEST_EMPLOYEE['last_name']), ) test.action_chain(0, selectors, action="send_keys") test.find("#register-employee-submit").click() sleep(7) test.open(reverse("employees_index")) sleep(3) parts[1]['success'] = test.find("#tab-body-approved-employees"+\ " div.tr a div.td.first_name_approved").text ==\ TEST_EMPLOYEE['first_name'] except Exception as e: print e parts[1]['test_message'] = str(e) ########## Access level of new employee is the same as ### the one chosen in the registration form. try: test.find("#tab-body-approved-employees div.tr a").click() sleep(2) parts[2]['success'] = acl ==\ test.find("#ACL option[selected]").get_attribute("value") except Exception as e: print e parts[2]['test_message'] = str(e) ########## A valid email is required. try: test.open(reverse("employee_register")) sleep(2) test.find("#id_email").send_keys("jhfghjdfg@jfgh") test.find("#register-employee-submit").click() sleep(3) parts[3]['success'] = test.find("#email_ic > ul.errorlist > li").text ==\ "Enter a valid email address." except Exception as e: print e parts[3]['test_message'] = str(e) ########## Email must be unique. try: selectors = ( ("#id_email", TEST_EMPLOYEE['username']), ("#id_password", "123456123"), ("#id_confirm_password", "123456123"), ("#id_first_name", "YOLO"), ("#id_last_name", "BOLO"), ) test.action_chain(0, selectors, action="clear") test.action_chain(0, selectors, action="send_keys") test.find("#register-employee-submit").click() sleep(5) parts[4]['success'] = test.find("#email_ic > ul.errorlist > li").text ==\ "Email is already being used." except Exception as e: print e parts[4]['test_message'] = str(e) ########## Email is required. 5 ########## First name is required. 6 ########## Last name is required. 7 ########## Password is required. 8 ########## Password confirm is required. 9 try: selectors = ( "#id_email", "#id_password", "#id_confirm_password", "#id_first_name", "#id_last_name", ) test.action_chain(0, selectors, action="clear") test.find("#register-employee-submit").click() sleep(3) for i, sel in enumerate(selectors): id = sel.replace("#id_", "") + "_ic" parts[i+5]['success'] =\ test.find("#%s > ul.errorlist > li" % (id,)).text ==\ "This field is required." except Exception as e: print e for i, sel in enumerate(selectors): parts[i+5]['test_message'] = str(e) ########## Passwords must match. try: selectors = ( ("#id_password", "123456"), ("#id_confirm_password", "abcdefg"), ) test.action_chain(0, selectors, action="clear") test.action_chain(0, selectors, action="send_keys") test.find("#register-employee-submit").click() sleep(3) parts[10]['success'] = test.find("#password_ic > ul.errorlist > li").text ==\ "Passwords don't match." except Exception as e: print e parts[10]['test_message'] = str(e) ########## Password must be at least 6 characters long. try: test.find("#id_password").clear() test.find("#id_password").send_keys("123") test.find("#register-employee-submit").click() sleep(3) parts[11]['success'] =\ test.find("#password_ic > ul.errorlist > li").text ==\ "Ensure this value has at least 6 characters (it has 3)." except Exception as e: print e parts[11]['success'] = str(e) # END OF ALL TESTS - cleanup return test.tear_down()
def register(request): """ Adds a new employee to the currently logged in Store. This automatically sets this employee to approved. """ data = {'employees_nav': True} settings = SESSION.get_settings(request.session) store = SESSION.get_store(request.session) if request.method == "POST": from_associated_account = False # check if this post is from the associated account dialog # if it is then skip form validations aaf_nonce_id = request.POST.get('aaf-nonce') aaf_account_id = request.POST.get('aaf-account_id') if len(aaf_nonce_id) > 0 and len(aaf_account_id) > 0: aa_nonce = AssociatedAccountNonce.objects.filter(\ id=aaf_nonce_id, account_id=aaf_account_id) if len(aa_nonce) > 0 and aa_nonce[0].verified: aa_nonce[0].delete() from_associated_account = True account_form = EmployeeAccountSignUpForm(request.POST) employee_form = EmployeeForm(request.POST) if not from_associated_account: all_forms_valid = account_form.is_valid() and\ employee_form.is_valid() else: all_forms_valid = True if all_forms_valid: postDict = request.POST.dict() # make the cloud call # see cloud param for possible access level values acl = postDict['acl'] if acl == ACCESS_ADMIN[0]: access_level = "admin" elif acl == ACCESS_PUNCHREDEEM[0]: access_level = "punch_redeem" else: access_level = None params = { "retailer_pin": settings.get("retailer_pin"), "username": postDict['email'].strip().lower(), "first_name": postDict['first_name'].capitalize(), "last_name": postDict['last_name'].capitalize(), "email": postDict['email'].strip().lower(), "status": APPROVED, "access_level": access_level, } if from_associated_account: res = cloud_call("link_employee", params) else: params["password"] = postDict['password'] res = cloud_call("register_employee", params) # don't forget to retrieve the latest session request.session.clear() request.session.update(SessionStore(request.session.session_key)) # check if email already taken here to handle the case where # the user already has a patron/employee account # but also want to sign up for a Store account if "error" in res and res['error'] in ("EMAIL_TAKEN_AVAILABLE", "USERNAME_TAKEN_AVAILABLE"): aa = Account.objects().get(email=postDict['email'].strip().lower()) aan = AssociatedAccountNonce.objects.create(\ account_id=aa.objectId) return HttpResponse(json.dumps({"associated_account":\ aa.objectId, "associated_account_nonce":aan.id, "email": aa.email, "code": 0}), content_type="application/json") elif "error" in res and res['error'] in ("EMAIL_TAKEN", "USERNAME_TAKEN"): account_form._errors.setdefault("email", ErrorList()).append(u"Email is already being used.") elif "error" not in res: # add the employee to the approved list employees_approved_list =\ SESSION.get_employees_approved_list(request.session) employees_approved_ids =\ [ emp.objectId for emp in employees_approved_list ] new_employee = Employee(**res["result"]) if new_employee.objectId not in employees_approved_ids: employees_approved_list.insert(0, new_employee) request.session['employees_approved_list'] =\ employees_approved_list # update our local store's acl - don't wait for # the cloud post store = SESSION.get_store(request.session) store.set_access_level(Account.objects().get(\ email=postDict['email'].strip().lower()), acl) request.session['store'] = store # notify other dashboards of this change payload = { COMET_RECEIVE_KEY_NAME: COMET_RECEIVE_KEY, "updatedStore":store.jsonify() } comet_receive(store.objectId, payload) return HttpResponse(json.dumps({"code": 2}), content_type="application/json") else: return HttpResponse(json.dumps({"code": 3}), content_type="application/json") else: employee_form = EmployeeForm(initial={"acl":\ ACCESS_PUNCHREDEEM[0]}) account_form = EmployeeAccountSignUpForm() data["employee_form"] = employee_form data["account_form"] = account_form return render(request, 'manage/employee_register.djhtml', data)
def processCometReceivedDict(session, postDict): employees_pending_list =\ SESSION.get_employees_pending_list(session) employees_approved_list =\ SESSION.get_employees_approved_list(session) messages_received_list =\ SESSION.get_messages_received_list(session) redemptions_pending =\ SESSION.get_redemptions_pending(session) redemptions_past =\ SESSION.get_redemptions_past(session) ############################################################# # FEEDBACKS_UNREAD ################################## newFeedback = postDict.get('newFeedback') if newFeedback: messages_received_ids =\ [ fb.objectId for fb in messages_received_list ] m = Message(**newFeedback) if m.objectId not in messages_received_ids: messages_received_list.insert(0, m) session['messages_received_list'] =\ messages_received_list ############################################################# # FEEDBACK DELETED ################################## deletedFeedback = postDict.get("deletedFeedback") if deletedFeedback: fb = Message(**deletedFeedback) for i, mro in enumerate(messages_received_list): if fb.objectId == mro.objectId: messages_received_list.pop(i) break session['messages_received_list'] =\ messages_received_list ############################################################# # MESSAGE SENT ################################## # need to check if this new message is an original message # or a reply to a feedback (the message sent by the patron)! # also may increment the message count! newMessage = postDict.get("newMessage") if newMessage: messages_received_ids =\ [ fb.objectId for fb in messages_received_list ] messages_sent_list =\ SESSION.get_messages_sent_list(session) messages_sent_ids =\ [ msg.objectId for msg in messages_sent_list ] m = Message(**newMessage) if m.objectId not in messages_sent_ids and\ m.message_type != FEEDBACK: messages_sent_list.insert(0, m) if 'message_count' in session: session['message_count'] =\ int(session['message_count']) + 1 # update an existing feedback if m.objectId in messages_received_ids and\ m.message_type == FEEDBACK: for i, mrl in enumerate(messages_received_list): if mrl.objectId == m.objectId: messages_received_list.pop(i) messages_received_list.insert(i, m) break session['messages_received_list'] =\ messages_received_list session['messages_sent_list'] = messages_sent_list ############################################################# # EMPLOYEES_PENDING ################################## # must also check if employee is already approved! pendingEmployee = postDict.get("pendingEmployee") if pendingEmployee: employees_approved_ids =\ [ emp.objectId for emp in employees_approved_list ] employees_pending_ids =\ [ emp.objectId for emp in employees_pending_list ] e = Employee(**pendingEmployee) if e.objectId not in employees_pending_ids and\ e.objectId not in employees_approved_ids: employees_pending_list.insert(0, e) session['employees_pending_list'] =\ employees_pending_list ############################################################# # EMPLOYEES APPROVED (pending to approved) ################# approvedEmployee = postDict.get("approvedEmployee") if approvedEmployee: emp = Employee(**approvedEmployee) # first check if the employee is in the pending list # if not then check if it is already approved for i, emp_pending in\ enumerate(employees_pending_list): if emp.objectId == emp_pending.objectId: emp = employees_pending_list.pop(i) emp.status = APPROVED employees_approved_list.insert(0, emp) break session['employees_pending_list'] =\ employees_pending_list session['employees_approved_list'] =\ employees_approved_list ############################################################# # EMPLOYEES NEW (straight to approved) ################# newEmployee = postDict.get("newEmployee") if newEmployee: employees_approved_ids =\ [ emp.objectId for emp in employees_approved_list ] emp = Employee(**newEmployee) if emp.objectId not in employees_approved_ids: employees_approved_list.insert(0, emp) session['employees_approved_list'] =\ employees_approved_list ############################################################# # EMPLOYEES DELETED/DENIED/REJECTED (pending/approved to pop)! deletedEmployee = postDict.get("deletedEmployee") if deletedEmployee: emp = Employee(**deletedEmployee) # check in approved emps for i, cop in enumerate(employees_approved_list): if cop.objectId == emp.objectId: employees_approved_list.pop(i) break # check in pending emps for i, cop in enumerate(employees_pending_list): if cop.objectId == emp.objectId: employees_pending_list.pop(i) break session['employees_approved_list'] =\ employees_approved_list session['employees_pending_list'] =\ employees_pending_list ############################################################# # EMPLOYEE UPDATED PUNCHES updatedEmployeePunch = postDict.get("updatedEmployeePunch") if updatedEmployeePunch: u_emp = Employee(**updatedEmployeePunch) for emp in employees_approved_list: if u_emp.objectId == emp.objectId: emp.set("lifetime_punches", u_emp.lifetime_punches) break session['employees_approved_list'] =\ employees_approved_list ############################################################# # REDEMPTIONS PENDING ### Only added to cache if it has the store_location_id as ### active_store_location_id pendingRedemption = postDict.get("pendingRedemption") if pendingRedemption: rr = RedeemReward(**pendingRedemption) # store_location_id can be null for backwards compat if not rr.store_location_id or rr.store_location_id ==\ session.get('active_store_location_id'): redemptions_pending_ids =\ [ red.objectId for red in redemptions_pending ] redemptions_past_ids =\ [ red.objectId for red in redemptions_past ] # need to check here if the redemption is new because # the dashboard that validated it will also receive # the validated redemption back. if rr.objectId not in redemptions_past_ids and\ rr.objectId not in redemptions_pending_ids: redemptions_pending.insert(0, rr) session['redemptions_pending'] =\ redemptions_pending ############################################################# # REDEMPTIONS APPROVED (pending to history) # Save cpu by skipping those that do not have the same # store_location_id as active_store_location_id approvedRedemption = postDict.get("approvedRedemption") if approvedRedemption: redemp = RedeemReward(**approvedRedemption) # store_location_id can be null for backwards compat if not redemp.store_location_id or redemp.store_location_id ==\ session.get('active_store_location_id'): # check if redemp is still in pending for i, redem in enumerate(redemptions_pending): if redem.objectId == redemp.objectId: r = redemptions_pending.pop(i) r.is_redeemed = True r.updatedAt = redemp.updatedAt redemptions_past.insert(0, r) break # if not then check if it is in the history already # the above shouldn't happen! session['redemptions_pending'] =\ redemptions_pending session['redemptions_past'] =\ redemptions_past ############################################################# # REDEMPTIONS DELETED ############################## # remove from pending (should not be in history!) # Save cpu by skipping those that do not have the same # store_location_id as active_store_location_id deletedRedemption = postDict.get("deletedRedemption") if deletedRedemption: redemp = RedeemReward(**deletedRedemption) # store_location_id can be null for backwards compat if not redemp.store_location_id or redemp.store_location_id ==\ session.get('active_store_location_id'): # check if redemp is still in pending for i, redem in enumerate(redemptions_pending): if redem.objectId == redemp.objectId: redemptions_pending.pop(i) break session['redemptions_pending'] =\ redemptions_pending ############################################################# # STORE UPDATED ############################## updatedStore = postDict.get("updatedStore") if updatedStore: store = Store(**updatedStore) # have to add the image url manually store.thumbnail_image_url = updatedStore.get("thumbnail_image_url") store.cover_image_url = updatedStore.get("cover_image_url") # below here for backwards compat store.store_avatar_url = store.thumbnail_image_url session['store'] = store updatedStoreThumbnailName = postDict.get("updatedStoreThumbnailName") if updatedStoreThumbnailName: store = session['store'] store.thumbnail_image = updatedStoreThumbnailName store.thumbnail_image_url = postDict.get( "updatedStoreThumbnailUrl") # below here for backwards compat store.store_avatar = store.thumbnail_image store.store_avatar_url = store.thumbnail_image_url session['store'] = store updatedStoreCoverName = postDict.get("updatedStoreCoverName") if updatedStoreCoverName: store = session['store'] store.cover_image = updatedStoreCoverName store.cover_image_url = postDict.get("updatedStoreCoverUrl") session['store'] = store # this is in the settings tab in the dashboard but the field # is in the Store class updatedPunchesFacebook_int =\ postDict.get("updatedPunchesFacebook_int") if updatedPunchesFacebook_int: store = session['store'] store.punches_facebook = int(updatedPunchesFacebook_int) session['store'] = store ############################################################# # STORE LOCATION UPDATED ############################## ### Note that this is also being used to insert new StoreLocations updatedStoreLocation = postDict.get("updatedStoreLocation") if updatedStoreLocation: store_location = StoreLocation(**updatedStoreLocation) session['store_locations'][store_location.objectId] =\ store_location try: # also update the store_timezone session['store_timezone'] =\ pytz.timezone(store_location.get('store_timezone')) except Exception: # assign a default timezone session['store_timezone'] =\ pytz.timezone(TIME_ZONE) ############################################################# # ACCOUNT UPDATED ############################## updatedAccount = postDict.get("updatedAccount") if updatedAccount: updatedAccountObject = Account(**updatedAccount) # need to make sure that these are the same accounts! if session['account'].objectId ==\ updatedAccountObject.objectId: session['account'] = updatedAccountObject ############################################################# # SUBSCRIPTION UPDATED ############################## updatedSubscription =\ postDict.get("updatedSubscription") if updatedSubscription: subscription = Subscription(**updatedSubscription) store = session["store"] store.set('subscription', subscription) store.set('Subscription', subscription.objectId) session['subscription'] = subscription session['store'] = store ############################################################# # SETTINGS UPDATED ############################## updatedSettings = postDict.get("updatedSettings") if updatedSettings: settings = Settings(**updatedSettings) store = session["store"] store.set('settings', settings) store.set("Settings", settings.objectId) session['settings'] = settings session['store'] = store ############################################################# # REWARDS NEW ############################## newReward = postDict.get("newReward") if newReward: store = session['store'] rewards = store.get("rewards") rewards_ids = [r['reward_id'] for r in rewards] if newReward['reward_id'] not in rewards_ids: rewards.append(newReward) store.rewards = rewards session['store'] = store ############################################################# # REWARDS UPDATED ############################## updatedReward = postDict.get('updatedReward') if updatedReward: store = session['store'] mod_rewards = store.get("rewards") for i, mreward in enumerate(mod_rewards): # [{"reward_name":"Free bottle of wine", # "description":"Must be under $25 in value", # "punches":10,"redemption_count":0,reward_id:0},] if updatedReward['reward_id'] == mreward['reward_id']: if updatedReward.has_key("redemption_count"): mod_rewards[i]['redemption_count'] =\ updatedReward['redemption_count'] if updatedReward.has_key("reward_name"): mod_rewards[i]['reward_name'] =\ updatedReward['reward_name'] if updatedReward.has_key("punches"): mod_rewards[i]['punches'] =\ updatedReward['punches'] if updatedReward.has_key("description"): mod_rewards[i]['description'] =\ updatedReward['description'] break store.rewards = mod_rewards session['store'] = store ############################################################# # REWARDS DELETED ############################## deletedReward = postDict.get("deletedReward") if deletedReward: store = session['store'] rewards = store.get("rewards") rewards_ids = [r['reward_id'] for r in rewards] if deletedReward['reward_id'] in rewards_ids: for i, r in enumerate(rewards): if r['reward_id'] == deletedReward['reward_id']: rewards.pop(i) break store.rewards = rewards session['store'] = store ############################################################# # PATRONSTORE_COUNT ################################## patronStore_int = postDict.get('patronStore_int') if patronStore_int: patronStore_int = int(patronStore_int) session['patronStore_count'] = patronStore_int
def test_employee_registration(): """ Tests for employee registrain via dashboard. """ # delete the employees and associated User objects in the relation account = Account.objects().get(email=TEST_USER['username'], include="Store.Settings") store = account.store settings = store.settings emps = store.get("employees") if emps: for emp in emps: Account.objects().get(Employee=emp.objectId).delete() emp.delete() store.set("employees", None) test = SeleniumTest() parts = [ {"test_name" : "Clicking register new employee button "+\ " redirects user to employee register page."}, {"test_name" : "New registered employee is immediately placed"+\ " in the approved group."}, {"test_name" : "Access level of new employee is the same as"+\ " the one chosen in the registration form."}, {"test_name" : "A valid email is required."}, {"test_name" : "Email must be unique."}, {"test_name" : "Email is required."}, {"test_name" : "First name is required."}, {"test_name" : "Last name is required."}, {"test_name" : "Password is required."}, {"test_name" : "Password confirm is required."}, {"test_name" : "Passwords must match."}, {"test_name" : "Password must be at least 6 characters long."}, ] section = { "section_name":\ "Employee registration functional?", "parts": parts, } test.results.append(section) test.login(TEST_USER['username'], TEST_USER['password'], reverse("employees_index")) ########## Clicking register new employee button ### redirects user to employee register page. try: test.find("#register_employee").click() sleep(3) parts[0]['success'] = test.is_current_url(reverse("employee_register")) except Exception as e: print e parts[0]['test_message'] = str(e) ########## New registered employee is immediately placed ### in the approved group. try: acl = test.find("#id_acl option[selected]").get_attribute("value") selectors = ( ("#id_email", TEST_EMPLOYEE['username']), ("#id_password", TEST_EMPLOYEE['password']), ("#id_confirm_password", TEST_EMPLOYEE['password']), ("#id_first_name", TEST_EMPLOYEE['first_name']), ("#id_last_name", TEST_EMPLOYEE['last_name']), ) test.action_chain(0, selectors, action="send_keys") test.find("#register-employee-submit").click() sleep(7) test.open(reverse("employees_index")) sleep(3) parts[1]['success'] = test.find("#tab-body-approved-employees"+\ " div.tr a div.td.first_name_approved").text ==\ TEST_EMPLOYEE['first_name'] except Exception as e: print e parts[1]['test_message'] = str(e) ########## Access level of new employee is the same as ### the one chosen in the registration form. try: test.find("#tab-body-approved-employees div.tr a").click() sleep(2) parts[2]['success'] = acl ==\ test.find("#ACL option[selected]").get_attribute("value") except Exception as e: print e parts[2]['test_message'] = str(e) ########## A valid email is required. try: test.open(reverse("employee_register")) sleep(2) test.find("#id_email").send_keys("jhfghjdfg@jfgh") test.find("#register-employee-submit").click() sleep(3) parts[3]['success'] = test.find("#email_ic > ul.errorlist > li").text ==\ "Enter a valid email address." except Exception as e: print e parts[3]['test_message'] = str(e) ########## Email must be unique. try: selectors = ( ("#id_email", TEST_EMPLOYEE['username']), ("#id_password", "123456123"), ("#id_confirm_password", "123456123"), ("#id_first_name", "YOLO"), ("#id_last_name", "BOLO"), ) test.action_chain(0, selectors, action="clear") test.action_chain(0, selectors, action="send_keys") test.find("#register-employee-submit").click() sleep(5) parts[4]['success'] = test.find("#email_ic > ul.errorlist > li").text ==\ "Email is already being used." except Exception as e: print e parts[4]['test_message'] = str(e) ########## Email is required. 5 ########## First name is required. 6 ########## Last name is required. 7 ########## Password is required. 8 ########## Password confirm is required. 9 try: selectors = ( "#id_email", "#id_password", "#id_confirm_password", "#id_first_name", "#id_last_name", ) test.action_chain(0, selectors, action="clear") test.find("#register-employee-submit").click() sleep(3) for i, sel in enumerate(selectors): id = sel.replace("#id_", "") + "_ic" parts[i+5]['success'] =\ test.find("#%s > ul.errorlist > li" % (id,)).text ==\ "This field is required." except Exception as e: print e for i, sel in enumerate(selectors): parts[i + 5]['test_message'] = str(e) ########## Passwords must match. try: selectors = ( ("#id_password", "123456"), ("#id_confirm_password", "abcdefg"), ) test.action_chain(0, selectors, action="clear") test.action_chain(0, selectors, action="send_keys") test.find("#register-employee-submit").click() sleep(3) parts[10]['success'] = test.find("#password_ic > ul.errorlist > li").text ==\ "Passwords don't match." except Exception as e: print e parts[10]['test_message'] = str(e) ########## Password must be at least 6 characters long. try: test.find("#id_password").clear() test.find("#id_password").send_keys("123") test.find("#register-employee-submit").click() sleep(3) parts[11]['success'] =\ test.find("#password_ic > ul.errorlist > li").text ==\ "Ensure this value has at least 6 characters (it has 3)." except Exception as e: print e parts[11]['success'] = str(e) # END OF ALL TESTS - cleanup return test.tear_down()