pull/1186/head
commit
9e389a1c0f
@ -0,0 +1,32 @@ |
||||
Feature: Localized boolean custom fields can be created |
||||
|
||||
Background: |
||||
Given I am admin |
||||
And the following languages are active: |
||||
| en | |
||||
| de | |
||||
When I go to the custom fields page |
||||
When I follow "New custom field" |
||||
|
||||
@javascript |
||||
Scenario: Available fields |
||||
When I select "Boolean" from "custom_field_field_format" |
||||
Then there should be the following localizations: |
||||
| locale | name | default_value | possible_values | |
||||
| en | | 0 | | |
||||
And there should be a "custom_field_tracker_ids_1" field visible |
||||
And I should see "Bug" |
||||
And there should be a "custom_field_tracker_ids_2" field visible |
||||
And I should see "Feature" |
||||
And there should be a "custom_field_tracker_ids_3" field visible |
||||
And I should see "Support" |
||||
And there should be a "custom_field_is_required" field visible |
||||
And there should be a "custom_field_is_for_all" field visible |
||||
And there should be a "custom_field_is_filter" field visible |
||||
And there should be a "custom_field_searchable" field invisible |
||||
|
||||
@javascript |
||||
Scenario: Creating a boolean custom field |
||||
And I add the english localization of the "name" attribute as "New Field" |
||||
And I select "Boolean" from "custom_field_field_format" |
||||
Then I should not see "Possible values" |
@ -0,0 +1,17 @@ |
||||
Feature: Date custom fields can be created |
||||
|
||||
Background: |
||||
Given I am admin |
||||
And the following languages are active: |
||||
| en | |
||||
| de | |
||||
When I go to the custom fields page |
||||
When I follow "New custom field" |
||||
|
||||
@javascript |
||||
Scenario: Creating a date custom field |
||||
When I select "Date" from "custom_field_field_format" |
||||
And I add the english localization of the "name" attribute as "New Field" |
||||
And I add the english localization of the "default_value" attribute as "2012-01-01" |
||||
And I press "Save" |
||||
Then I should be on the custom fields page |
@ -0,0 +1,18 @@ |
||||
Feature: Localized decimal custom fields can be created |
||||
|
||||
Background: |
||||
Given I am admin |
||||
And the following languages are active: |
||||
| en | |
||||
| de | |
||||
When I go to the custom fields page |
||||
When I follow "New custom field" |
||||
|
||||
@javascript |
||||
Scenario: Creating a decimal custom field |
||||
When I select "Float" from "custom_field_field_format" |
||||
And I add the english localization of the "name" attribute as "New Field" |
||||
And I add the english localization of the "default_value" attribute as "20.34" |
||||
And I press "Save" |
||||
Then I should be on the custom fields page |
||||
|
@ -0,0 +1,17 @@ |
||||
Feature: Int custom fields can be created |
||||
|
||||
Background: |
||||
Given I am admin |
||||
And the following languages are active: |
||||
| en | |
||||
| de | |
||||
When I go to the custom fields page |
||||
When I follow "New custom field" |
||||
|
||||
@javascript |
||||
Scenario: Creating a date custom field |
||||
When I select "Integer" from "custom_field_field_format" |
||||
And I add the english localization of the "name" attribute as "New Field" |
||||
And I add the english localization of the "default_value" attribute as "342" |
||||
And I press "Save" |
||||
Then I should be on the custom fields page |
@ -0,0 +1,21 @@ |
||||
Feature: Localized list custom fields can be created |
||||
|
||||
Background: |
||||
Given I am admin |
||||
And the following languages are active: |
||||
| en | |
||||
| de | |
||||
When I go to the custom fields page |
||||
When I follow "New custom field" |
||||
|
||||
@javascript |
||||
Scenario: Creating a list custom field |
||||
When I select "List" from "custom_field_field_format" |
||||
And I add the english localization of the "name" attribute as "New Field" |
||||
And I add the english localization of the "possible_values" attribute as "one\ntwo\nthree\n" |
||||
And I add the english localization of the "default_value" attribute as "two" |
||||
And I press "Save" |
||||
And I follow "New Field" |
||||
Then there should be the following localizations: |
||||
| locale | name | possible_values | default_value | |
||||
| en | New Field | one\ntwo\nthree | two | |
@ -0,0 +1,32 @@ |
||||
Feature: Text custom fields can be created |
||||
|
||||
Background: |
||||
Given I am admin |
||||
And the following languages are active: |
||||
| en | |
||||
| de | |
||||
| fr | |
||||
When I go to the custom fields page |
||||
When I follow "New custom field" |
||||
|
||||
@javascript |
||||
Scenario: Creating a text custom field with multiple name and default_value localizations |
||||
When I select "Text" from "custom_field_field_format" |
||||
And I add the english localization of the "name" attribute as "New Field" |
||||
And I add the german localization of the "name" attribute as "Neues Feld" |
||||
And I add the french localization of the "name" attribute as "Lorem ipsum" |
||||
And I add the english localization of the "default_value" attribute as "default" |
||||
And I add the german localization of the "default_value" attribute as "Standard" |
||||
And I add the french localization of the "default_value" attribute as "Lorem" |
||||
And I press "Save" |
||||
And I follow "New Field" |
||||
Then there should be the following localizations: |
||||
| locale | name | default_value | |
||||
| en | New Field | default | |
||||
| de | Neues Feld | Standard | |
||||
| fr | Lorem ipsum | Lorem | |
||||
|
||||
Scenario: Creating a custom field with one name |
||||
And I add the english localization of the "name" attribute as "Issue Field" |
||||
And I press "Save" |
||||
Then I should be on the custom fields page |
@ -0,0 +1,46 @@ |
||||
Feature: Editing a bool custom field |
||||
|
||||
Background: |
||||
Given I am already logged in as "admin" |
||||
And the following languages are active: |
||||
| en | |
||||
| de | |
||||
| fr | |
||||
And the following issue custom fields are defined: |
||||
| name | type | |
||||
| IssueCustomField | bool | |
||||
When I go to the custom fields page |
||||
|
||||
@javascript |
||||
Scenario: Adding a localized name |
||||
When I follow "IssueCustomField" |
||||
And I add the english localization of the "name" attribute as "Issue Field" |
||||
And I add the german localization of the "name" attribute as "Ticket Feld" |
||||
And I add the french localization of the "name" attribute as "Lorem" |
||||
And I press "Save" |
||||
Then I should be on the custom fields page |
||||
When I follow "Issue Field" |
||||
Then there should be the following localizations: |
||||
| locale | name | default_value | |
||||
| en | Issue Field | 0 | |
||||
| de | Ticket Feld | nil | |
||||
| fr | Lorem | nil | |
||||
And I should not see "Add" within "#custom_field_name_attributes" |
||||
|
||||
Scenario: Entering a long name displays an error |
||||
When I follow "IssueCustomField" |
||||
And I fill in "custom_field_translations_attributes_0_name" with "Long name which forces an error" |
||||
And I press "Save" |
||||
Then the "custom_field_translations_attributes_0_name" field should contain "Long name which forces an error" |
||||
And I should see "Name is too long" within "#errorExplanation" |
||||
|
||||
Scenario: Entering an already taken name displays an error |
||||
Given the following issue custom fields are defined: |
||||
| name | type | |
||||
| Taken name | bool | |
||||
When I follow "IssueCustomField" |
||||
And I add the english localization of the "name" attribute as "Taken name" |
||||
And I press "Save" |
||||
Then I should see "Name has already been taken" within "#errorExplanation" |
||||
And the "custom_field_translations_attributes_0_name" field should contain "Taken name" |
||||
|
@ -0,0 +1,67 @@ |
||||
Feature: Name localizations of bool custom fields can be deleted |
||||
|
||||
Background: |
||||
Given I am already logged in as "admin" |
||||
And the following languages are active: |
||||
| en | |
||||
| de | |
||||
| fr | |
||||
And the following issue custom fields are defined: |
||||
| name | type | |
||||
| My Custom Field | bool | |
||||
And the Custom Field called "My Custom Field" has the following localizations: |
||||
| locale | name | |
||||
| en | My Custom Field | |
||||
| de | Mein Benutzerdefiniertes Feld | |
||||
When I go to the custom fields page |
||||
|
||||
@javascript |
||||
Scenario: Deleting a localized name |
||||
When I follow "My Custom Field" |
||||
And I delete the german localization of the "name" attribute |
||||
And I press "Save" |
||||
And I follow "My Custom Field" |
||||
Then there should be the following localizations: |
||||
| locale | name | default_value | |
||||
| en | My Custom Field | 0 | |
||||
|
||||
@javascript |
||||
Scenario: Deleting a name localization and adding another of same locale in same action |
||||
When I follow "My Custom Field" |
||||
And I delete the german localization of the "name" attribute |
||||
And I add the german localization of the "name" attribute as "Neuer Name" |
||||
And I press "Save" |
||||
And I follow "My Custom Field" |
||||
Then there should be the following localizations: |
||||
| locale | name | default_value | |
||||
| en | My Custom Field | 0 | |
||||
| de | Neuer Name | nil | |
||||
|
||||
@javascript |
||||
Scenario: Deleting a name localization frees the locale to be used by other translation field |
||||
When I follow "My Custom Field" |
||||
And I delete the english localization of the "name" attribute |
||||
And I change the german localization of the "name" attribute to be english |
||||
And I press "Save" |
||||
And I follow "Mein Benutzerdefiniertes Feld" |
||||
Then there should be the following localizations: |
||||
| locale | name | default_value | |
||||
| en | Mein Benutzerdefiniertes Feld | 0 | |
||||
|
||||
@javascript |
||||
Scenario: Deleting a newly added localization |
||||
When I follow "My Custom Field" |
||||
And I add the french localization of the "name" attribute as "To delete" |
||||
And I delete the french localization of the "name" attribute |
||||
And I press "Save" |
||||
And I follow "My Custom Field" |
||||
Then there should be the following localizations: |
||||
| locale | name | default_value | |
||||
| en | My Custom Field | 0 | |
||||
| de | Mein Benutzerdefiniertes Feld | nil | |
||||
|
||||
@javascript |
||||
Scenario: Deletion link is hidden when only one localization exists |
||||
When I follow "My Custom Field" |
||||
And I delete the german localization of the "name" attribute |
||||
Then the delete link for the english localization of the "name" attribute should not be visible |
@ -0,0 +1,55 @@ |
||||
Feature: Editing text custom fields |
||||
|
||||
Background: |
||||
Given I am already logged in as "admin" |
||||
And the following languages are active: |
||||
| en | |
||||
| de | |
||||
And the following issue custom fields are defined: |
||||
| name | type | |
||||
| My Custom Field | text | |
||||
|
||||
@javascript |
||||
Scenario: Adding localized default_values |
||||
When I go to the custom fields page |
||||
And I follow "My Custom Field" |
||||
And I add the english localization of the "default_value" attribute as "default" |
||||
And I add the german localization of the "default_value" attribute as "Standard" |
||||
And I press "Save" |
||||
And I follow "My Custom Field" |
||||
Then there should be the following localizations: |
||||
| locale | default_value | name | |
||||
| en | default | My Custom Field | |
||||
| de | Standard | nil | |
||||
|
||||
@javascript |
||||
Scenario: Changing a localization which is not present for any other attribute to a locale existing in another attribute deletes the localization completely |
||||
When the Custom Field called "My Custom Field" has the following localizations: |
||||
| locale | name | default_value | |
||||
| en | My Custom Field | nil | |
||||
| de | nil | default | |
||||
And I go to the custom fields page |
||||
And I follow "My Custom Field" |
||||
And I select "English" from "custom_field_translations_attributes_1_locale" |
||||
And I press "Save" |
||||
And I follow "My Custom Field" |
||||
Then there should be the following localizations: |
||||
| locale | name | default_value | |
||||
| en | My Custom Field | default | |
||||
|
||||
@javascript |
||||
Scenario: Changing a localization of one attribute to a non existent localization creates the localization |
||||
When the Custom Field called "My Custom Field" has the following localizations: |
||||
| locale | name | default_value | |
||||
| en | My Custom Field | default | |
||||
And I go to the custom fields page |
||||
And I follow "My Custom Field" |
||||
And I select "Deutsch" from "custom_field_translations_attributes_0_locale" |
||||
And I press "Save" |
||||
And I follow "My Custom Field" |
||||
Then there should be the following localizations: |
||||
| locale | name | default_value | |
||||
| en | nil | default | |
||||
| de | My Custom Field | nil | |
||||
|
||||
|
@ -0,0 +1,156 @@ |
||||
Feature: Group Memberships |
||||
|
||||
Background: |
||||
Given there is a role "Manager" |
||||
And there is a role "Developer" |
||||
|
||||
And there is 1 project with the following: |
||||
| Name | Project1 | |
||||
| Identifier | project1 | |
||||
|
||||
And there is 1 User with: |
||||
| Login | peter | |
||||
| Firstname | Peter | |
||||
| Lastname | Pan | |
||||
|
||||
And there is 1 User with: |
||||
| Login | bob | |
||||
| Firstname | Bob | |
||||
| Lastname | Bobbit | |
||||
|
||||
And there is 1 User with: |
||||
| Login | hannibal | |
||||
| Firstname | Hannibal | |
||||
| Lastname | Smith | |
||||
|
||||
And there is a group named "A-Team" with the following members: |
||||
| peter | |
||||
| bob | |
||||
|
||||
|
||||
@javascript |
||||
Scenario: Adding a group to a project on the project's page adds the group members as well |
||||
Given I am admin |
||||
|
||||
When I go to the settings page of the project called "project1" |
||||
And I click on "tab-members" |
||||
And I check "A-Team" |
||||
And I check "Manager" |
||||
And I press "Add" |
||||
Then I should be on the settings page of the project called "project1" |
||||
And I should see "A-Team" within ".members" |
||||
And I should see "Bob Bobbit" within ".members" |
||||
And I should see "Peter Pan" within ".members" |
||||
|
||||
|
||||
@javascript |
||||
Scenario: Group-based memberships and individual memberships are handled separately |
||||
Given I am admin |
||||
|
||||
When I go to the settings page of the project called "project1" |
||||
And I click on "tab-members" |
||||
And I check "Bob Bobbit" |
||||
And I check "Manager" |
||||
And I press "Add" |
||||
And I wait for the AJAX requests to finish |
||||
|
||||
And I check "A-Team" |
||||
And I check "Developer" |
||||
And I press "Add" |
||||
And I wait for the AJAX requests to finish |
||||
|
||||
When I delete the "A-Team" membership |
||||
And I wait for the AJAX requests to finish |
||||
|
||||
Then I should see "Bob Bobbit" within ".members" |
||||
And I should not see "A-Team" within ".members" |
||||
And I should not see "Peter Pan" within ".members" |
||||
|
||||
|
||||
@javascript |
||||
Scenario: Removing a group from a project on the project's page removes all group members as well |
||||
|
||||
Given I am admin |
||||
|
||||
When I go to the settings page of the project called "project1" |
||||
And I click on "tab-members" |
||||
And I check "A-Team" |
||||
And I check "Manager" |
||||
And I press "Add" |
||||
|
||||
Then I should be on the settings page of the project called "project1" |
||||
And I wait for the AJAX requests to finish |
||||
|
||||
When I delete the "A-Team" membership |
||||
And I wait for the AJAX requests to finish |
||||
|
||||
Then I should not see "A-Team" within ".members" |
||||
And I should not see "Bob Bobbit" within ".members" |
||||
And I should not see "Peter Pan" within ".members" |
||||
|
||||
@javascript |
||||
Scenario: Adding a user to a group adds the user to projects as well |
||||
Given I am admin |
||||
|
||||
When I go to the admin page of the group called "A-Team" |
||||
And I click on "tab-memberships" |
||||
And I select "Project1" from "Projects" |
||||
And I check "Manager" |
||||
And I press "Add" |
||||
And I wait for the AJAX requests to finish |
||||
|
||||
And I click on "tab-users" |
||||
And I check "Hannibal Smith" |
||||
And I press "Add" |
||||
And I wait for the AJAX requests to finish |
||||
|
||||
When I go to the settings page of the project called "project1" |
||||
And I click on "tab-members" |
||||
|
||||
Then I should see "A-Team" within ".members" |
||||
And I should see "Bob Bobbit" within ".members" |
||||
And I should see "Peter Pan" within ".members" |
||||
And I should see "Hannibal Smith" within ".members" |
||||
|
||||
|
||||
@javascript |
||||
Scenario: Removing a user from a group removes the user from projects as well |
||||
Given I am admin |
||||
|
||||
When I go to the admin page of the group called "A-Team" |
||||
And I click on "tab-memberships" |
||||
And I select "Project1" from "Projects" |
||||
And I check "Manager" |
||||
And I press "Add" |
||||
And I wait for the AJAX requests to finish |
||||
|
||||
When I click on "tab-users" |
||||
And I delete "bob" from the group |
||||
And I wait for the AJAX requests to finish |
||||
|
||||
When I go to the settings page of the project called "project1" |
||||
And I click on "tab-members" |
||||
|
||||
Then I should see "A-Team" within ".members" |
||||
And I should not see "Bob Bobbit" within ".members" |
||||
And I should see "Peter Pan" within ".members" |
||||
|
||||
@javascript |
||||
Scenario: Adding a group to project on the group's page adds the group members as well |
||||
Given I am admin |
||||
|
||||
When I go to the admin page of the group called "A-Team" |
||||
And I click on "tab-memberships" |
||||
And I select "Project1" from "Projects" |
||||
And I check "Manager" |
||||
And I press "Add" |
||||
And I wait for the AJAX requests to finish |
||||
|
||||
Then the project member "A-Team" should have the role "Manager" |
||||
|
||||
When I go to the settings page of the project called "project1" |
||||
And I click on "tab-members" |
||||
|
||||
Then I should see "A-Team" within ".members" |
||||
And I should see "Bob Bobbit" within ".members" |
||||
And I should see "Peter Pan" within ".members" |
@ -0,0 +1,70 @@ |
||||
Feature: Group memberships |
||||
Background: |
||||
Given there is 1 project with the following: |
||||
| name | project1 | |
||||
| identifier | project1 | |
||||
And there is 1 user with the following: |
||||
| login | bob | |
||||
| firstname | Bob | |
||||
| Lastname | Bobbit | |
||||
And there is 1 user with the following: |
||||
| login | alice | |
||||
| firstname | Alice | |
||||
| lastname | Wonderland | |
||||
And there is 1 group with the following: |
||||
| name | group1 | |
||||
And there is a role "alpha" |
||||
And there is a role "beta" |
||||
And the role "alpha" may have the following rights: |
||||
| manage_members | |
||||
And the user "bob" is a "alpha" in the project "project1" |
||||
|
||||
Scenario: Adding a group with members to a project |
||||
Given the group "group1" has the following members: |
||||
| alice | |
||||
And I am already logged in as "bob" |
||||
When I go to the members tab of the settings page of the project "project1" |
||||
And I add the principal "group1" as a member with the roles: |
||||
| beta | |
||||
Then I should see the principal "group1" as a member with the roles: |
||||
| beta | |
||||
And I should see the principal "alice" as a member with the roles: |
||||
| beta | |
||||
|
||||
Scenario: Adding members to a group after the group has been added to the project adds the users to the project |
||||
Given the group "group1" is a "beta" in the project "project1" |
||||
And I am already logged in as "admin" |
||||
When I go to the edit page of the group called "group1" |
||||
And I follow "Users" within ".tabs" |
||||
And I add the user "alice" to the group |
||||
And I go to the members tab of the settings page of the project "project1" |
||||
Then I should see the principal "group1" as a member with the roles: |
||||
| beta | |
||||
And I should see the principal "alice" as a member with the roles: |
||||
| beta | |
||||
|
||||
@javascript |
||||
Scenario: Removing a group from a project removes it's members (users) as well if they have no roles of their own |
||||
Given the group "group1" has the following members: |
||||
| alice | |
||||
And the group "group1" is a "beta" in the project "project1" |
||||
And I am already logged in as "bob" |
||||
When I go to the members tab of the settings page of the project "project1" |
||||
And I follow the delete link of the project member "group1" |
||||
Then I should not see the principal "group1" as a member |
||||
And I should not see the principal "alice" as a member |
||||
|
||||
@javascript |
||||
Scenario: Removing a group from a project leaves a member if he has other roles besides those inherited from the group |
||||
Given the group "group1" has the following members: |
||||
| alice | |
||||
And the user "alice" is a "alpha" in the project "project1" |
||||
And the group "group1" is a "beta" in the project "project1" |
||||
And I am already logged in as "bob" |
||||
When I go to the members tab of the settings page of the project "project1" |
||||
And I follow the delete link of the project member "group1" |
||||
Then I should not see the principal "group1" as a member |
||||
And I should see the principal "alice" as a member with the roles: |
||||
| alpha | |
||||
|
||||
|
@ -0,0 +1,48 @@ |
||||
Feature: Copying an issue should copy the watchers |
||||
Background: |
||||
Given there is 1 project with the following: |
||||
| identifier | omicronpersei8 | |
||||
And there is a role "CanCopyIssues" |
||||
And the role "CanCopyIssues" may have the following rights: |
||||
| add_issues | |
||||
| view_issues | |
||||
| view_issue_watchers | |
||||
And there is a role "CanAddWatchers" |
||||
And the role "CanAddWatchers" may have the following rights: |
||||
| add_issues | |
||||
| view_issues | |
||||
| view_issue_watchers | |
||||
| add_issue_watchers | |
||||
And there is 1 user with the following: |
||||
| login | ndnd | |
||||
And the user "ndnd" is a "CanCopyIssues" in the project "omicronpersei8" |
||||
And there is 1 user with the following: |
||||
| login | lrrr | |
||||
And the user "lrrr" is a "CanAddWatchers" in the project "omicronpersei8" |
||||
And there are the following issue status: |
||||
| name | is_default | |
||||
| New | true | |
||||
And the user "lrrr" has 1 issue with the following: |
||||
| subject | Improve sex drive | |
||||
| description | Acquire human horn | |
||||
And the issue "Improve sex drive" is watched by: |
||||
| lrrr | |
||||
| ndnd | |
||||
|
||||
Scenario: Watchers shouldn't be copied when the user doesn't have the permission to |
||||
Given I am logged in as "ndnd" |
||||
When I go to the copy page for the issue "Improve sex drive" |
||||
Then I should not see "Watchers" |
||||
When I fill in "issue_subject" with "Improve sex drive even more" |
||||
When I click on "Create" |
||||
Then I should not see "Watchers" |
||||
And the issue "Improve sex drive even more" should have 0 watchers |
||||
|
||||
Scenario: Watchers should be copied when the user has the permission to |
||||
Given I am logged in as "lrrr" |
||||
When I go to the copy page for the issue "Improve sex drive" |
||||
Then I should see "Watchers" within "p#watchers_form" |
||||
When I fill in "issue_subject" with "Improve sex drive even more" |
||||
When I click on "Create" |
||||
Then I should see "Watchers (2)" |
||||
And the issue "Improve sex drive even more" should have 2 watchers |
@ -0,0 +1,45 @@ |
||||
Feature: Exporting issues |
||||
|
||||
Background: |
||||
Given there is 1 user with the following: |
||||
| login | bob | |
||||
And there is a role "member" |
||||
And the role "member" may have the following rights: |
||||
| view_issues | |
||||
And there is 1 project with the following: |
||||
| name | project1 | |
||||
| identifier | project1 | |
||||
And the user "bob" is a "member" in the project "project1" |
||||
And the user "bob" has 1 issue with the following: |
||||
| subject | Some Issue | |
||||
And I am already logged in as "bob" |
||||
|
||||
Scenario: No export links on project issues index if user has no "export_issues" permission |
||||
When I go to the issues index page of the project called "project1" |
||||
Then I should not see "CSV" within ".other-formats" |
||||
And I should not see "PDF" within ".other-formats" |
||||
|
||||
Scenario: Export links on project issues index if user has the "export_issues" permission |
||||
Given the role "member" may have the following rights: |
||||
| view_issues | |
||||
| export_issues | |
||||
When I go to the issues index page of the project called "project1" |
||||
Then I should see "CSV" within ".other-formats" |
||||
And I should see "PDF" within ".other-formats" |
||||
|
||||
Scenario: No export links on global issues index if user has no "export_issues" permission |
||||
When I go to the global index page of issues |
||||
Then I should not see "CSV" within ".other-formats" |
||||
And I should not see "PDF" within ".other-formats" |
||||
|
||||
Scenario: Export links on global issues index if user has the "export_issues" permission |
||||
Given the role "member" may have the following rights: |
||||
| view_issues | |
||||
| export_issues | |
||||
When I go to the global index page of issues |
||||
Then I should see "CSV" within ".other-formats" |
||||
And I should see "PDF" within ".other-formats" |
||||
|
||||
|
||||
|
||||
|
@ -0,0 +1,30 @@ |
||||
Feature: Paginated issue index list |
||||
|
||||
Background: |
||||
Given there is 1 project with the following: |
||||
| identifier | project1 | |
||||
| name | project1 | |
||||
And there is 1 user with the following: |
||||
| login | bob | |
||||
And there is a role "member" |
||||
And the role "member" may have the following rights: |
||||
| view_issues | |
||||
| create_issues | |
||||
And the user "bob" is a "member" in the project "project1" |
||||
And the user "bob" has 26 issues with the following: |
||||
| subject | Issuesubject | |
||||
And I am already logged in as "bob" |
||||
|
||||
Scenario: Pagination within a project |
||||
When I go to the issues index page of the project "project1" |
||||
Then I should see 25 issues |
||||
When I follow "2" within ".pagination" |
||||
Then I should be on the issues index page of the project "project1" |
||||
And I should see 1 issue |
||||
|
||||
Scenario: Pagination outside a project |
||||
When I go to the global index page of issues |
||||
Then I should see 25 issues |
||||
When I follow "2" within ".pagination" |
||||
Then I should be on the global index page of issues |
||||
And I should see 1 issue |
@ -0,0 +1,49 @@ |
||||
Feature: Issue textile quickinfo links |
||||
Background: |
||||
Given there is 1 project with the following: |
||||
| name | parent | |
||||
| identifier | parent | |
||||
And there is a role "member" |
||||
And the role "member" may have the following rights: |
||||
| add_issues | |
||||
| view_issues | |
||||
And there is 1 user with the following: |
||||
| login | bob| |
||||
And the user "bob" is a "member" in the project "parent" |
||||
And there are the following issue status: |
||||
| name | is_closed | is_default | |
||||
| New | false | true | |
||||
| In Progress | false | false | |
||||
Given the user "bob" has 1 issue with the following: |
||||
| subject | issue1 | |
||||
| due_date | 2012-05-04 | |
||||
| start_date | 2011-05-04 | |
||||
| description | Aioli Sali Grande | |
||||
And I am already logged in as "bob" |
||||
|
||||
Scenario: Adding an issue link |
||||
When I go to the issues/new page of the project called "parent" |
||||
And I fill in "One hash key" for "issue_subject" |
||||
And I fill in the ID of "issue1" with 1 hash for "issue_description" |
||||
And I press "Create" |
||||
Then I should see an issue link for "issue1" within "div.wiki" |
||||
When I follow the issue link with 1 hash for "issue1" |
||||
Then I should be on the page of the issue "issue1" |
||||
|
||||
Scenario: Adding an issue quickinfo link |
||||
When I go to the issues/new page of the project called "parent" |
||||
And I fill in "One hash key" for "issue_subject" |
||||
And I fill in the ID of "issue1" with 2 hash for "issue_description" |
||||
And I press "Create" |
||||
Then I should see a quickinfo link for "issue1" within "div.wiki" |
||||
When I follow the issue link with 2 hash for "issue1" |
||||
Then I should be on the page of the issue "issue1" |
||||
|
||||
Scenario: Adding an issue quickinfo link with description |
||||
When I go to the issues/new page of the project called "parent" |
||||
And I fill in "One hash key" for "issue_subject" |
||||
And I fill in the ID of "issue1" with 3 hash for "issue_description" |
||||
And I press "Create" |
||||
Then I should see a quickinfo link with description for "issue1" within "div.wiki" |
||||
When I follow the issue link with 3 hash for "issue1" |
||||
Then I should be on the page of the issue "issue1" |
@ -0,0 +1,172 @@ |
||||
Feature: Issue Sum Calculations for Currency |
||||
Background: |
||||
Given there is 1 project with the following: |
||||
| name | project1 | |
||||
| identifier | project1 | |
||||
And the following issue custom fields are defined: |
||||
| name | type | |
||||
| cf1 | float | |
||||
And the custom field "cf1" is summable |
||||
And there is a role "Manager" |
||||
And there is 1 user with: |
||||
| Login | manager | |
||||
And the user "manager" is a "Manager" in the project "project1" |
||||
And I am already logged in as "admin" |
||||
|
||||
@javascript |
||||
Scenario: Should calculate an overall sum for a standard issue query |
||||
Given the user "manager" has 1 issue with the following: |
||||
| subject | Some issue | |
||||
| cf1 | 100 | |
||||
And the user "manager" has 1 issue with the following: |
||||
| subject | Some other issue | |
||||
| cf1 | 50 | |
||||
When I go to the issues index page for the project called "project1" |
||||
And I select to see columns |
||||
| cf1 | |
||||
And I toggle the Options fieldset |
||||
And I check "display_sums" |
||||
And I click on "Apply" |
||||
And I wait 10 seconds for AJAX |
||||
Then I should see "150" in the overall sum |
||||
|
||||
@javascript |
||||
Scenario: Should not calculate an overall sum for a standard issue query if the column isn't summable |
||||
Given the user "manager" has 1 issue with the following: |
||||
| subject | Some issue | |
||||
| cf1 | 100 | |
||||
And the user "manager" has 1 issue with the following: |
||||
| subject | Some other issue | |
||||
| cf1 | 50 | |
||||
And the custom field "cf1" is not summable |
||||
When I go to the issues index page for the project called "project1" |
||||
And I select to see columns |
||||
| cf1 | |
||||
And I toggle the Options fieldset |
||||
And I check "display_sums" |
||||
And I click on "Apply" |
||||
And I wait 10 seconds for AJAX |
||||
Then I should not see "150" in the overall sum |
||||
|
||||
@javascript |
||||
Scenario: Should tick the checkbox on query edit if we previously displayed sums |
||||
Given the user "manager" has 1 issue with the following: |
||||
| subject | Some issue | |
||||
| cf1 | 100 | |
||||
And the user "manager" has 1 issue with the following: |
||||
| subject | Some other issue | |
||||
| cf1 | 50 | |
||||
When I go to the issues index page for the project called "project1" |
||||
And I select to see columns |
||||
| cf1 | |
||||
And I toggle the Options fieldset |
||||
And I check "display_sums" |
||||
And I click on "Apply" |
||||
And I click on "Save" |
||||
And I fill in "TestQuery" for "query_name" |
||||
And I press "Save" |
||||
And I go to the issues index page for the project called "project1" |
||||
And I click on "TestQuery" |
||||
Then I should be on the issues index page for the project called "project1" |
||||
And the "display_sums" checkbox should be checked |
||||
And I should see "150" in the overall sum |
||||
And I click on "Edit" |
||||
Then the "query[display_sums]" checkbox should be checked |
||||
|
||||
@javascript |
||||
Scenario: Should not tick the checkbox on query edit if we did not previously display sums |
||||
Given the user "manager" has 1 issue with the following: |
||||
| subject | Some issue | |
||||
| cf1 | 100 | |
||||
And the user "manager" has 1 issue with the following: |
||||
| subject | Some other issue | |
||||
| cf1 | 50 | |
||||
When I go to the issues index page for the project called "project1" |
||||
And I select to see columns |
||||
| cf1 | |
||||
And I toggle the Options fieldset |
||||
And I uncheck "display_sums" |
||||
And I click on "Apply" |
||||
And I click on "Save" |
||||
And I fill in "TestQuery" for "query_name" |
||||
And I press "Save" |
||||
And I go to the issues index page for the project called "project1" |
||||
And I click on "TestQuery" |
||||
Then I should be on the issues index page for the project called "project1" |
||||
And the "display_sums" checkbox should not be checked |
||||
And I click on "Edit" |
||||
Then the "query[display_sums]" checkbox should not be checked |
||||
|
||||
@javascript |
||||
Scenario: Should calculate an overall sum for a grouped issue query with multiple groups |
||||
Given there is 1 user with: |
||||
| Login | alice | |
||||
And the user "alice" is a "Manager" in the project "project1" |
||||
And there is 1 user with: |
||||
| Login | bob | |
||||
And the user "bob" is a "Manager" in the project "project1" |
||||
And the user "manager" has 1 issue with the following: |
||||
| subject | Some issue | |
||||
| cf1 | 100 | |
||||
And the user "manager" has 1 issue with the following: |
||||
| subject | Some issue | |
||||
| cf1 | 50 | |
||||
And the user "alice" has 1 issue with the following: |
||||
| subject | Some issue | |
||||
| cf1 | 300 | |
||||
And the user "bob" has 1 issue with the following: |
||||
| subject | Some issue | |
||||
| cf1 | 200 | |
||||
And the user "bob" has 1 issue with the following: |
||||
| subject | Some issue | |
||||
| cf1 | 250 | |
||||
When I go to the issues index page for the project called "project1" |
||||
And I select to see columns |
||||
| cf1 | |
||||
And I toggle the Options fieldset |
||||
And I check "display_sums" |
||||
And I select "Assignee" from "group_by" |
||||
And I click on "Apply" |
||||
And I wait 10 seconds for AJAX |
||||
Then I should see "150" in the grouped sum |
||||
And I should see "300" in the grouped sum |
||||
And I should see "450" in the grouped sum |
||||
And I should see "900" in the overall sum |
||||
|
||||
@javascript |
||||
Scenario: Should calculate an overall sum for a grouped issue query with a single group |
||||
Given the user "manager" has 1 issue with the following: |
||||
| subject | Some issue | |
||||
| cf1 | 100 | |
||||
And the user "manager" has 1 issue with the following: |
||||
| subject | Some issue | |
||||
| cf1 | 50 | |
||||
When I go to the issues index page for the project called "project1" |
||||
And I select to see columns |
||||
| cf1 | |
||||
And I toggle the Options fieldset |
||||
And I check "display_sums" |
||||
And I select "Assignee" from "group_by" |
||||
And I click on "Apply" |
||||
And I wait 10 seconds for AJAX |
||||
Then I should see "150" in the grouped sum |
||||
And I should see "150" in the overall sum |
||||
|
||||
@javascript |
||||
Scenario: Should strip floats down to a precission of 2 number |
||||
Given the user "manager" has 1 issue with the following: |
||||
| subject | Some issue | |
||||
| cf1 | 100.0000001 | |
||||
And the user "manager" has 1 issue with the following: |
||||
| subject | Some issue | |
||||
| cf1 | 50.09 | |
||||
When I go to the issues index page for the project called "project1" |
||||
And I select to see columns |
||||
| cf1 | |
||||
And I toggle the Options fieldset |
||||
And I check "display_sums" |
||||
And I select "Assignee" from "group_by" |
||||
And I click on "Apply" |
||||
And I wait 10 seconds for AJAX |
||||
Then I should see "150.09" in the grouped sum |
||||
And I should see "150.09" in the overall sum |
@ -0,0 +1,13 @@ |
||||
Feature: Creating Projects |
||||
|
||||
@javascript |
||||
Scenario: Creating a Subproject |
||||
Given there is 1 project with the following: |
||||
| name | Parent | |
||||
| identifier | parent | |
||||
And I am admin |
||||
When I go to the overview page of the project "Parent" |
||||
And I follow "New subproject" |
||||
And I fill in "project_name" with "child" |
||||
And I press "Save" |
||||
Then I should be on the settings page of the project called "child" |
@ -0,0 +1,22 @@ |
||||
# encoding: utf-8 |
||||
|
||||
Then /^I should see an issue link for "([^"]*)" within "([^"]*)"$/ do |issue_name, container| |
||||
issue = Issue.find_by_subject(issue_name) |
||||
text = "##{issue.id}" |
||||
|
||||
step %Q{I should see "#{text}" within "#{container}"} |
||||
end |
||||
|
||||
Then /^I should see a quickinfo link for "([^"]*)" within "([^"]*)"$/ do |issue_name, container| |
||||
issue = Issue.find_by_subject(issue_name) |
||||
|
||||
text = "##{issue.id} #{issue.status}: #{issue.subject} #{issue.start_date.to_s} – #{issue.due_date.to_s} (#{issue.assigned_to.to_s})" |
||||
step %Q{I should see "#{text}" within "#{container}"} |
||||
end |
||||
|
||||
Then /^I should see a quickinfo link with description for "([^"]*)" within "([^"]*)"$/ do |issue_name, container| |
||||
issue = Issue.find_by_subject(issue_name) |
||||
|
||||
step %Q{I should see a quickinfo link for "#{issue_name}" within "#{container}"} |
||||
step %Q{I should see "#{issue.description}" within "#{container}"} |
||||
end |
@ -0,0 +1,19 @@ |
||||
When /^I fill in the ID of "([^"]*)" with (\d+) hash for "([^"]*)"$/ do |issue_name, number_hash_keys, container| |
||||
issue = Issue.find_by_subject(issue_name) |
||||
text = "#{('#' * number_hash_keys.to_i)}#{issue.id}" |
||||
|
||||
step %Q{I fill in "#{text}" for "#{container}"} |
||||
end |
||||
|
||||
When /^I follow the issue link with (\d+) hash for "([^"]*)"$/ do |hash_count, issue_name| |
||||
issue = Issue.find_by_subject(issue_name) |
||||
|
||||
text = "" |
||||
if hash_count.to_i > 1 |
||||
text = "##{issue.id} #{issue.status}: #{issue.subject}" |
||||
elsif hash_count.to_i == 1 |
||||
text = "##{issue.id}" |
||||
end |
||||
|
||||
step %Q{I follow "#{text}"} |
||||
end |
@ -0,0 +1,7 @@ |
||||
Then /^the breadcrumb should contain "(.+)"$/ do |string| |
||||
container = ChiliProject::VERSION::MAJOR < 2 ? "p.breadcrumb a" : "#breadcrumb a" |
||||
|
||||
steps %Q{ Then I should see "#{string}" within "#{container}" } |
||||
end |
||||
|
||||
|
@ -0,0 +1,46 @@ |
||||
# "Then I should see 5 articles" |
||||
Then /^I should see (\d+) ([^\" ]+)(?: within "([^\"]*)")?$/ do |number, name, selector| |
||||
with_scope(selector) do |
||||
if defined?(Spec::Rails::Matchers) |
||||
page.should have_css(".#{name.singularize}", :count => number.to_i) |
||||
else |
||||
assert page.has_css?(".#{name.singularize}", :count => number.to_i) |
||||
end |
||||
end |
||||
end |
||||
|
||||
Then /^I should not see(?: (\d+))? ([^\" ]+)(?: within "([^\"]*)")?$/ do |number, name, selector| |
||||
options = number ? {:count => number.to_i} : {} |
||||
with_scope(selector) do |
||||
if defined?(Spec::Rails::Matchers) |
||||
page.should have_no_css(".#{name.singularize}", options) |
||||
else |
||||
assert page.has_no_css?(".#{name.singularize}", options) |
||||
end |
||||
end |
||||
end |
||||
|
||||
Around('@changes_environment') do |scenario, block| |
||||
saved_env = ENV["RAILS_ENV"] |
||||
block.call |
||||
ENV["RAILS_ENV"] = saved_env |
||||
end |
||||
|
||||
|
||||
Then /^I am in "([^\"]*)" mode$/ do |env| |
||||
ENV["RAILS_ENV"] = env |
||||
end |
||||
|
||||
Given /^the [pP]roject(?: "([^\"]+?)")? uses the following trackers:$/ do |project, table| |
||||
project = get_project(project) |
||||
|
||||
trackers = table.raw.map do |line| |
||||
name = line.first |
||||
tracker = Tracker.find_by_name(name) |
||||
|
||||
tracker = FactoryGirl.create(:tracker, :name => name) if tracker.blank? |
||||
tracker |
||||
end |
||||
|
||||
project.update_attributes :tracker_ids => trackers.map(&:id).map(&:to_s) |
||||
end |
@ -0,0 +1,35 @@ |
||||
Given /^the following (user|issue) custom fields are defined:$/ do |type, table| |
||||
type = (type + "_custom_field").to_sym |
||||
|
||||
as_admin do |
||||
table.hashes.each_with_index do |r, i| |
||||
attr_hash = { :name => r['name'], |
||||
:field_format => r['type']} |
||||
|
||||
attr_hash[:possible_values] = r['possible_values'].split(",").collect(&:strip) if r['possible_values'] |
||||
attr_hash[:is_required] = (r[:required] == 'true') if r[:required] |
||||
attr_hash[:editable] = (r[:editable] == 'true') if r[:editable] |
||||
attr_hash[:visible] = (r[:visible] == 'true') if r[:visible] |
||||
attr_hash[:default_value] = r[:default_value] ? r[:default_value] : nil |
||||
attr_hash[:is_for_all] = r[:is_for_all] || true |
||||
|
||||
FactoryGirl.create type, attr_hash |
||||
end |
||||
end |
||||
end |
||||
|
||||
Given /^the user "(.+?)" has the user custom field "(.+?)" set to "(.+?)"$/ do |login, field_name, value| |
||||
user = User.find_by_login(login) |
||||
custom_field = UserCustomField.find_by_name(field_name) |
||||
|
||||
user.custom_values.build(:custom_field => custom_field, :value => value) |
||||
user.save! |
||||
end |
||||
|
||||
Given /^the custom field "(.+)" is( not)? summable$/ do |field_name, negative| |
||||
custom_field = IssueCustomField.find_by_name(field_name) |
||||
|
||||
Setting.issue_list_summable_columns = negative ? |
||||
Setting.issue_list_summable_columns - ["cf_#{custom_field.id}"] : |
||||
Setting.issue_list_summable_columns << "cf_#{custom_field.id}" |
||||
end |
@ -0,0 +1,70 @@ |
||||
module Capybara::Node::Finders |
||||
alias old_find find |
||||
|
||||
def find(*args) |
||||
tries = 0 |
||||
begin |
||||
old_find(*args) |
||||
rescue Capybara::ElementNotFound => e |
||||
tries += 1 |
||||
tries < 3 ? retry : raise(e) |
||||
end |
||||
end |
||||
end |
||||
|
||||
Then /^I should (not )?see "([^"]*)"\s*\#.*$/ do |negative, name| |
||||
steps %Q{ |
||||
Then I should #{negative}see "#{name}" |
||||
} |
||||
end |
||||
|
||||
Then /^I should (not )?see "([^"]*)" within "([^"]*)"\s*\#.*$/ do |negative, name, scope| |
||||
steps %Q{ |
||||
Then I should #{negative}see "#{name}" within "#{scope}" |
||||
} |
||||
end |
||||
|
||||
When /^I click(?:| on) "([^"]*)"$/ do |name| |
||||
begin |
||||
steps %Q{ |
||||
When I follow "#{name}" |
||||
} |
||||
rescue Capybara::ElementNotFound |
||||
steps %Q{ |
||||
When I press "#{name}" |
||||
} |
||||
end |
||||
end |
||||
|
||||
When /^(?:|I )jump to [Pp]roject "([^\"]*)"$/ do |project| |
||||
begin |
||||
find(:xpath, '//div[@id="quick-search"]/select[last()]').select project |
||||
rescue Capybara::ElementNotFound |
||||
click_link('Projects') |
||||
find(:css, '#account-nav .chzn-results li', :text => project).click |
||||
end |
||||
end |
||||
|
||||
Then /^"([^"]*)" should be selected for "([^"]*)"$/ do |value, select_id| |
||||
# that makes capybara wait for the ajax request |
||||
find(:xpath, "//body") |
||||
# if you wanna see ugly things, look at the following line |
||||
(page.evaluate_script("$('#{select_id}').value") =~ /^#{value}$/).should be_present |
||||
end |
||||
|
||||
Then /^"([^"]*)" should (not )?be selectable from "([^"]*)"$/ do |value, negative, select_id| |
||||
#more page.evaluate ugliness |
||||
find(:xpath, "//body") |
||||
bool = negative ? false : true |
||||
(page.evaluate_script("$('#{select_id}').select('option[value=#{value}]').first.disabled") =~ /^#{bool}$/).should be_present |
||||
end |
||||
|
||||
# This does NOT trigger actual hovering by means of :hover. |
||||
# To use this, you have to adjust your stylesheet accordingly. |
||||
When /^I hover over "([^"]+)"$/ do |selector| |
||||
page.execute_script "jQuery(#{selector.inspect}).addClass('hover');" |
||||
end |
||||
|
||||
When /^I stop hovering over "([^"]*)"$/ do |selector| |
||||
page.execute_script "jQuery(#{selector.inspect}).removeClass('hover');" |
||||
end |
@ -0,0 +1,19 @@ |
||||
Given /^the "(.+)" drop-down should( not)? have the following options:$/ do |id, neg, table| |
||||
meth = neg ? :should_not : :should |
||||
table.raw.each do | option | |
||||
page.send(meth, have_xpath("//select[@id = '#{id}']//option[@value = '#{option[0]}']")) |
||||
end |
||||
end |
||||
|
||||
Then /^the "(.+)" drop-down should have the following options (enabled|disabled):$/ do |id, state, table| |
||||
state = state == "disabled" ? "" : "not" |
||||
table.raw.each do | option | |
||||
page.should have_xpath "//select[@id = '#{id}']//option[@value = '#{option[0]}' and #{state}(@disabled)]" |
||||
end |
||||
end |
||||
|
||||
Then /^the "(.+)" drop-down(?: within "([^\"]*)")? should have "([^\"]*)" selected$/ do |field_name, selector, option_name| |
||||
with_scope(selector) do |
||||
find_field(field_name).find('option[selected]').text.should == option_name |
||||
end |
||||
end |
@ -0,0 +1,559 @@ |
||||
# encoding: utf-8 |
||||
require 'active_record/fixtures' |
||||
|
||||
Before do |scenario| |
||||
unless ScenarioDisabler.empty_if_disabled(scenario) |
||||
# Reset the DB and load the minimal fixtures for a functional system before each scenario |
||||
system "RAILS_ENV=test db:test:load >/dev/null 2>/dev/null" |
||||
ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations['test']) |
||||
ActiveRecord::Schema.verbose = false |
||||
|
||||
fixtures_path = File.expand_path(Rails.root.join('test/fixtures')) |
||||
FactoryGirl.create(:admin) unless User.find_by_login("admin") |
||||
FactoryGirl.create(:anonymous) unless AnonymousUser.count > 0 |
||||
Setting.notified_events = [] #can not test mailer |
||||
Dir.glob(File.join(fixtures_path, "*.yml")) do |table_name| |
||||
ActiveRecord::Fixtures.create_fixtures(fixtures_path, File.basename(table_name).gsub(".yml", "")) |
||||
end |
||||
|
||||
if Capybara.current_driver.to_s.include?("selenium") |
||||
Capybara.current_session.driver.browser.manage.window.resize_to(3000, 3000) |
||||
end |
||||
end |
||||
end |
||||
|
||||
Given /^(?:|I )am not logged in$/ do |
||||
User.current = AnonymousUser.first |
||||
end |
||||
|
||||
Given /^(?:|I )am [aA]dmin$/ do |
||||
steps %Q{ |
||||
Given I am logged in as \"admin\" |
||||
} |
||||
end |
||||
|
||||
Given /^I am already logged in as "(.+?)"$/ do |login| |
||||
# consider using: http://collectiveidea.com/blog/archives/2012/01/05/capybara-cucumber-and-how-the-cookie-crumbles/ |
||||
# or: https://github.com/railsware/rack_session_access |
||||
# once the application is rails3 |
||||
user = User.find_by_login(login) |
||||
|
||||
ApplicationController.class_eval do |
||||
define_method :set_user_session do |
||||
session[:user_id] = user.id |
||||
|
||||
ApplicationController.skip_before_filter :set_user_session |
||||
ApplicationController.subclasses.each do |subclass_name| |
||||
unless subclass_name.to_s.include?("Spec::Rails") |
||||
subclass_name.prepend_before_filter :set_user_session |
||||
end |
||||
end |
||||
end |
||||
end |
||||
|
||||
ApplicationController.prepend_before_filter :set_user_session |
||||
ApplicationController.subclasses.each do |subclass_name| |
||||
unless subclass_name.to_s.include?("Spec::Rails") |
||||
subclass_name.prepend_before_filter :set_user_session |
||||
end |
||||
end |
||||
end |
||||
|
||||
def login_user(username, password = "admin") |
||||
steps %Q{ |
||||
Given I am on the logout page |
||||
When I go to the login page |
||||
And I fill in "#{username}" for "username" |
||||
And I fill in "#{password}" for "password" |
||||
And I press "Login" |
||||
Then I should be logged in as "#{username}" |
||||
} |
||||
end |
||||
|
||||
Given /^(?:|I )am logged in as "([^\"]*)"$/ do |username| |
||||
FactoryGirl.create(:admin) unless User.find_by_login("admin") |
||||
FactoryGirl.create(:anonymous) unless AnonymousUser.count > 0 |
||||
login_user(username) |
||||
end |
||||
|
||||
Given /^there is 1 [pP]roject with(?: the following)?:$/ do |table| |
||||
p = FactoryGirl.build(:project) |
||||
send_table_to_object(p, table) |
||||
end |
||||
|
||||
Then /^the project "([^"]*)" is( not)? public$/ do |project_name, negation| |
||||
p = Project.find_by_name(project_name) |
||||
p.update_attribute(:is_public, !negation) |
||||
end |
||||
|
||||
Given /^the [Pp]roject "([^\"]*)" has 1 [wW]iki(?: )?[pP]age with the following:$/ do |project, table| |
||||
p = Project.find_by_name(project) |
||||
|
||||
p.wiki.create! unless p.wiki |
||||
|
||||
page = FactoryGirl.create(:wiki_page, :wiki => p.wiki) |
||||
content = FactoryGirl.create(:wiki_content, :page => page) |
||||
|
||||
send_table_to_object(page, table) |
||||
end |
||||
|
||||
Given /^there is 1 [Uu]ser with(?: the following)?:$/ do |table| |
||||
login = table.rows_hash[:Login].to_s + table.rows_hash[:login].to_s |
||||
user = User.find_by_login(login) unless login.blank? |
||||
|
||||
if user |
||||
table = table.reject_key(/(L|l)ogin/) |
||||
else |
||||
user = FactoryGirl.create(:user) |
||||
user.password = user.password_confirmation = nil |
||||
end |
||||
|
||||
modify_user(user, table) |
||||
end |
||||
|
||||
Given /^the [Uu]ser "([^\"]*)" has:$/ do |user, table| |
||||
u = User.find_by_login(user) |
||||
raise "No such user: #{user}" unless u |
||||
modify_user(u, table) |
||||
end |
||||
|
||||
Given /^there are the following users:$/ do |table| |
||||
table.raw.flatten.each do |login| |
||||
FactoryGirl.create(:user, :login => login) |
||||
end |
||||
end |
||||
|
||||
Given /^the plugin (.+) is loaded$/ do |plugin_name| |
||||
plugin_name = plugin_name.gsub("\"", "") |
||||
Redmine::Plugin.all.detect {|x| x.id == plugin_name.to_sym}.present? ? nil : pending("Plugin #{plugin_name} not loaded") |
||||
end |
||||
|
||||
Given /^(?:the )?[pP]roject "([^\"]*)" uses the following [mM]odules:$/ do |project, table| |
||||
p = Project.find_by_name(project) |
||||
|
||||
p.enabled_module_names += table.raw.map { |row| row.first } |
||||
p.reload |
||||
end |
||||
|
||||
Given /^the [Uu]ser "([^\"]*)" is a "([^\"]*)" (?:in|of) the [Pp]roject "([^\"]*)"$/ do |user, role, project| |
||||
u = User.find_by_login(user) |
||||
r = Role.find_by_name(role) |
||||
p = Project.find_by_name(project) || Project.find_by_identifier(project) |
||||
as_admin do |
||||
Member.new.tap do |m| |
||||
m.user = u |
||||
m.privacy_unnecessary = true if plugin_loaded?("redmine_dtag_privacy") |
||||
m.roles << r |
||||
m.project = p |
||||
end.save! |
||||
end |
||||
end |
||||
|
||||
Given /^there is a(?:n)? (default )?(?:issue)?status with:$/ do |default, table| |
||||
name = table.raw.select { |ary| ary.include? "name" }.first[table.raw.first.index("name") + 1].to_s |
||||
IssueStatus.find_by_name(name) || IssueStatus.create(:name => name.to_s, :is_default => !!default) |
||||
end |
||||
|
||||
Given /^there is a(?:n)? (default )?issuepriority with:$/ do |default, table| |
||||
name = table.raw.select { |ary| ary.include? "name" }.first[table.raw.first.index("name") + 1].to_s |
||||
project = get_project |
||||
IssuePriority.new.tap do |prio| |
||||
prio.name = name |
||||
prio.is_default = !!default |
||||
prio.project = project |
||||
prio.save! |
||||
end |
||||
end |
||||
|
||||
Given /^there is a [rR]ole "([^\"]*)"$/ do |name| |
||||
Role.spawn.tap { |r| r.name = name }.save! unless Role.find_by_name(name) |
||||
end |
||||
|
||||
Given /^there are the following roles:$/ do |table| |
||||
table.raw.flatten.each do |name| |
||||
FactoryGirl.create(:role, :name => name) unless Role.find_by_name(name) |
||||
end |
||||
end |
||||
|
||||
Given /^the [rR]ole "([^\"]*)" may have the following [rR]ights:$/ do |role, table| |
||||
r = Role.find_by_name(role) |
||||
raise "No such role was defined: #{role}" unless r |
||||
as_admin do |
||||
available_perms = Redmine::AccessControl.permissions.collect(&:name) |
||||
r.permissions = [] |
||||
|
||||
table.raw.each do |_perm| |
||||
perm = _perm.first |
||||
unless perm.blank? |
||||
perm = perm.gsub(" ", "_").underscore.to_sym |
||||
if available_perms.include?(:"#{perm}") |
||||
r.permissions << perm |
||||
end |
||||
end |
||||
end |
||||
|
||||
r.save! |
||||
end |
||||
end |
||||
|
||||
Given /^the [rR]ole "(.+?)" has no (?:[Pp]ermissions|[Rr]ights)$/ do |role_name| |
||||
role = Role.find_by_name(role_name) |
||||
raise "No such role was defined: #{role_name}" unless role |
||||
as_admin do |
||||
role.permissions = [] |
||||
role.save! |
||||
end |
||||
end |
||||
|
||||
Given /^the [Uu]ser "([^\"]*)" has 1 time [eE]ntry$/ do |user| |
||||
u = User.find_by_login user |
||||
p = u.projects.last |
||||
raise "This user must be member of a project to have issues" unless p |
||||
i = Issue.generate_for_project!(p) |
||||
t = TimeEntry.generate |
||||
t.user = u |
||||
t.issue = i |
||||
t.project = p |
||||
t.activity.project = p |
||||
t.activity.save! |
||||
t.save! |
||||
end |
||||
|
||||
Given /^the [Uu]ser "([^\"]*)" has 1 time entry with (\d+\.?\d*) hours? at the project "([^\"]*)"$/ do |user, hours, project| |
||||
p = Project.find_by_name(project) || Project.find_by_identifier(project) |
||||
as_admin do |
||||
t = TimeEntry.generate |
||||
i = Issue.generate_for_project!(p) |
||||
t.project = p |
||||
t.issue = i |
||||
t.hours = hours.to_f |
||||
t.user = User.find_by_login user |
||||
t.activity.project = p |
||||
t.activity.save! |
||||
t.save! |
||||
end |
||||
end |
||||
|
||||
Given /^the [Uu]ser "([^\"]*)" has (\d+) [iI]ssue(?:s)? with(?: the following)?:$/ do |user, count, table| |
||||
u = User.find_by_login user |
||||
raise "This user must be member of a project to have issues" unless u.projects.last |
||||
as_admin count do |
||||
i = Issue.generate_for_project!(u.projects.last) |
||||
i.author = u |
||||
i.assigned_to = u |
||||
i.tracker = Tracker.find_by_name(table.rows_hash.delete("tracker")) if table.rows_hash["tracker"] |
||||
send_table_to_object(i, table, {}, method(:add_custom_value_to_issue)) |
||||
i.save! |
||||
end |
||||
end |
||||
|
||||
Given /^the [Pp]roject "([^\"]*)" has (\d+) [tT]ime(?: )?[eE]ntr(?:ies|y) with the following:$/ do |project, count, table| |
||||
p = Project.find_by_name(project) || Project.find_by_identifier(project) |
||||
as_admin count do |
||||
t = TimeEntry.generate |
||||
i = Issue.generate_for_project!(p) |
||||
t.project = p |
||||
t.issue = i |
||||
t.activity.project = p |
||||
t.activity.save! |
||||
send_table_to_object(t, table, |
||||
:user => Proc.new do |o,v| |
||||
o.user = User.find_by_login(v) |
||||
o.save! |
||||
end, |
||||
:spent_on => Proc.new do |object, value| |
||||
# This works for definitions like "2 years ago" |
||||
number, time_unit, tempus = value.split |
||||
time = number.to_i.send(time_unit.to_sym).send(tempus.to_sym) |
||||
object.spent_on = time |
||||
object.save! |
||||
end |
||||
) |
||||
end |
||||
end |
||||
|
||||
Given /^the [Pp]roject "([^\"]*)" has (\d+) [iI]ssue(?:s)? with(?: the following)?:$/ do |project, count, table| |
||||
p = Project.find_by_name(project) || Project.find_by_identifier(project) |
||||
as_admin count do |
||||
i = Issue.generate_for_project!(p) |
||||
send_table_to_object(i, table, {}, method(:add_custom_value_to_issue)) |
||||
end |
||||
end |
||||
|
||||
Given /^the [Pp]roject "([^\"]*)" has (\d+) [Dd]ocument with(?: the following)?:$/ do |project, count, table| |
||||
p = Project.find_by_name(project) || Project.find_by_identifier(project) |
||||
as_admin count do |
||||
d = Document.spawn |
||||
d.project = p |
||||
d.category = DocumentCategory.first |
||||
d.save! |
||||
send_table_to_object(d, table) |
||||
end |
||||
end |
||||
|
||||
Given /^the [Pp]roject (.+) has 1 version with(?: the following)?:$/ do |project, table| |
||||
project.gsub!("\"", "") |
||||
p = Project.find_by_name(project) || Project.find_by_identifier(project) |
||||
table.rows_hash["effective_date"] = eval(table.rows_hash["effective_date"]).to_date if table.rows_hash["effective_date"] |
||||
|
||||
as_admin do |
||||
v = Version.generate |
||||
send_table_to_object(v, table) |
||||
p.versions << v |
||||
end |
||||
end |
||||
|
||||
Given /^the [pP]roject "([^\"]*)" has 1 [sS]ubproject$/ do |project| |
||||
parent = Project.find_by_name(project) |
||||
p = Project.generate |
||||
p.set_parent!(parent) |
||||
p.save! |
||||
end |
||||
|
||||
Given /^the [pP]roject "([^\"]*)" has 1 [sS]ubproject with the following:$/ do |project, table| |
||||
parent = Project.find_by_name(project) |
||||
p = FactoryGirl.build(:project) |
||||
as_admin do |
||||
send_table_to_object(p, table) |
||||
end |
||||
|
||||
p.set_parent!(parent) |
||||
p.save! |
||||
end |
||||
|
||||
Given /^there are the following trackers:$/ do |table| |
||||
|
||||
table.hashes.each_with_index do |t, i| |
||||
tracker = Tracker.find_by_name(t['name']) |
||||
tracker = Tracker.new :name => t['name'] if tracker.nil? |
||||
tracker.position = t['position'] ? t['position'] : i |
||||
tracker.is_in_roadmap = t['is_in_roadmap'] ? t['is_in_roadmap'] : true |
||||
tracker.save! |
||||
end |
||||
end |
||||
|
||||
Given /^there are the following issue status:$/ do |table| |
||||
|
||||
table.hashes.each_with_index do |t, i| |
||||
status = IssueStatus.find_by_name(t['name']) |
||||
status = IssueStatus.new :name => t['name'] if status.nil? |
||||
status.is_closed = t['is_closed'] == 'true' ? true : false |
||||
status.is_default = t['is_default'] == 'true' ? true : false |
||||
status.position = t['position'] ? t['position'] : i |
||||
status.default_done_ratio = t['default_done_ratio'] |
||||
status.save! |
||||
end |
||||
end |
||||
|
||||
Given /^the tracker "(.+?)" has the default workflow for the role "(.+?)"$/ do |tracker_name, role_name| |
||||
role = Role.find_by_name(role_name) |
||||
tracker = Tracker.find_by_name(tracker_name) |
||||
tracker.workflows = [] |
||||
|
||||
IssueStatus.all(:order => "id ASC").collect(&:id).combination(2).each do |c| |
||||
tracker.workflows.build(:old_status_id => c[0], :new_status_id => c[1], :role => role) |
||||
end |
||||
tracker.save! |
||||
end |
||||
|
||||
|
||||
Given /^the [iI]ssue "([^\"]*)" has (\d+) [tT]ime(?: )?[eE]ntr(?:ies|y) with the following:$/ do |issue, count, table| |
||||
i = Issue.find(:last, :conditions => ["subject = '#{issue}'"]) |
||||
raise "No such issue: #{issue}" unless i |
||||
as_admin count do |
||||
t = TimeEntry.generate |
||||
t.project = i.project |
||||
t.spent_on = DateTime.now |
||||
t.issue = i |
||||
send_table_to_object(t, table, |
||||
{:user => Proc.new do |o,v| |
||||
o.user = User.find_by_login(v) |
||||
o.save! |
||||
end}) |
||||
end |
||||
end |
||||
|
||||
Given /^I select to see [cC]olumn "([^\"]*)"$/ do |column_name| |
||||
steps %Q{ |
||||
When I select \"#{column_name}\" from \"available_columns\" |
||||
When I click on \"→\" |
||||
When I click on \"Apply\" |
||||
} |
||||
end |
||||
|
||||
Given /I select to see [cC]olumn(?:s)?$/ do |table| |
||||
params = "?set_filter=1&" + table.raw.collect(&:first).collect do |name| |
||||
page.source =~ /<option value="(.*?)">#{name}<\/option>/ |
||||
column_name = $1 || name.gsub(" ", "_").downcase |
||||
"query[column_names][]=#{column_name}" |
||||
end.join("&") |
||||
visit(current_path + params) |
||||
end |
||||
|
||||
Given /^I start debugging$/ do |
||||
save_and_open_page |
||||
binding.pry |
||||
true |
||||
end |
||||
|
||||
Given /^I (?:stop|pause) (?:step )?execution$/ do |
||||
loop do |
||||
$stdout.puts "\nPausing step execution. Press <Enter> to continue. Enter `debug` to start debugging." |
||||
text = $stdin.readline |
||||
|
||||
step "I start debugging" if text =~ /debug/ |
||||
|
||||
break if text.strip.empty? |
||||
end |
||||
end |
||||
|
||||
When /^(?:|I )login as (.+)? with password (.+)?$/ do |username, password| |
||||
username = username.gsub("\"", "") |
||||
password = password.gsub("\"", "") |
||||
login_user(username, password) |
||||
end |
||||
|
||||
Then /^I should be logged in as "([^\"]*)"?$/ do |username| |
||||
user = User.find_by_login(username) || User.anonymous |
||||
page.should have_xpath("//div[contains(., 'Logged in as #{username}')] | //a[contains(.,'#{user.name}')]") |
||||
|
||||
User.current = user |
||||
end |
||||
|
||||
When /^(?:|I )login as (.+)?$/ do |username| |
||||
steps %Q{ |
||||
When I login as #{username} with password admin |
||||
} |
||||
end |
||||
|
||||
When /^I satisfy the "(.+)" plugin to (.+)$/ do |plugin_name, action| |
||||
if plugin_loaded?(plugin_name) |
||||
action_name = action.gsub("\"", "") |
||||
|
||||
plugin_action(plugin_name, action_name) |
||||
end |
||||
end |
||||
|
||||
Given /^I am working in [pP]roject "(.+?)"$/ do |project_name| |
||||
@project = Project.find_by_name(project_name) |
||||
end |
||||
|
||||
Given /^the [pP]roject uses the following modules:$/ do |table| |
||||
step %Q{the project "#{get_project}" uses the following modules:}, table |
||||
end |
||||
|
||||
|
||||
Given /the user "(.*?)" is a "(.*?)"/ do |user, role| |
||||
step %Q{the user "#{user}" is a "#{role}" in the project "#{get_project.name}"} |
||||
end |
||||
|
||||
Given /^the [pP]roject(?: "([^\"]*)")? has the following trackers:$/ do |project_name, table| |
||||
p = get_project(project_name) |
||||
table.hashes.each_with_index do |t, i| |
||||
tracker = Tracker.find_by_name(t['name']) |
||||
tracker = Tracker.new :name => t['name'] if tracker.nil? |
||||
tracker.position = t['position'] ? t['position'] : i |
||||
tracker.is_in_roadmap = t['is_in_roadmap'] ? t['is_in_roadmap'] : true |
||||
tracker.save! |
||||
p.trackers << tracker |
||||
p.save! |
||||
end |
||||
end |
||||
|
||||
def get_project(project_name = nil) |
||||
if project_name.blank? |
||||
project = @project |
||||
else |
||||
project = Project.find_by_name(project_name) |
||||
end |
||||
if project.nil? |
||||
if project_name.blank? |
||||
raise "Could not identify the current project. Make sure to use the 'I am working in project \"Project Name\" step beforehand." |
||||
else |
||||
raise "Could not find project with the name \"#{project_name}\"." |
||||
end |
||||
end |
||||
project |
||||
end |
||||
|
||||
|
||||
# Modify a given user using the specified table |
||||
def modify_user(u, table) |
||||
as_admin do |
||||
send_table_to_object(u, table, |
||||
:default_rate => Proc.new do |user, value| |
||||
user.save! |
||||
DefaultHourlyRate.new.tap do |r| |
||||
r.valid_from = 3.years.ago.to_date |
||||
r.rate = value |
||||
r.user_id = user.id |
||||
end.save! |
||||
end, |
||||
:name => Proc.new {|user, value| user.login = name; user.save!}, |
||||
:hourly_rate => Proc.new do |user, value| |
||||
user.save! |
||||
HourlyRate.new.tap do |r| |
||||
r.valid_from = (2.years.ago + HourlyRate.count.days).to_date |
||||
r.rate = value |
||||
r.user_id = user.id |
||||
r.project = user.projects.last |
||||
end.save! |
||||
end |
||||
) |
||||
|
||||
u.save! |
||||
end |
||||
u |
||||
end |
||||
|
||||
# Encapsule the logic to set a custom field on an issue |
||||
def add_custom_value_to_issue(object, key, value) |
||||
if IssueCustomField.all.collect(&:name).include? key.to_s |
||||
cv = CustomValue.find(:first, :conditions => ["customized_id = '#{object.id}'"]) |
||||
cv ||= CustomValue.new |
||||
cv.customized_type = "Issue" |
||||
cv.customized_id = object.id |
||||
cv.custom_field_id = IssueCustomField.first(:joins => :translations, :conditions => ["custom_field_translations.name = ?", key]).id |
||||
cv.value = value |
||||
cv.save! |
||||
end |
||||
end |
||||
|
||||
# Try to assign an object the values set in a table |
||||
def send_table_to_object(object, table, except = {}, rescue_block = nil) |
||||
return unless table.raw.present? |
||||
as_admin do |
||||
table.rows_hash.each do |key, value| |
||||
_key = key.gsub(" ", "_").underscore.to_sym |
||||
if except[_key] |
||||
except[_key].call(object, value) |
||||
elsif except[key] |
||||
except[key].call(object, value) |
||||
elsif object.respond_to? :"#{_key}=" |
||||
object.send(:"#{_key}=", value) |
||||
elsif rescue_block |
||||
rescue_block.call(object, key, value) |
||||
else |
||||
raise "No such method #{_key} on a #{object.class}" |
||||
end |
||||
end |
||||
object.save! if object.changed? |
||||
end |
||||
end |
||||
|
||||
# Do something as admin |
||||
def as_admin(count = 1) |
||||
cur_user = User.current |
||||
User.current = User.find_by_login("admin") |
||||
retval = nil |
||||
count.to_i.times do |
||||
retval = yield |
||||
end |
||||
User.current = cur_user |
||||
retval |
||||
end |
||||
|
||||
def plugin_loaded?(name) |
||||
Redmine::Plugin.all.detect {|x| x.id == name.to_sym}.present? |
||||
end |
||||
|
@ -0,0 +1,54 @@ |
||||
Given /^there is 1 group with the following:$/ do |table| |
||||
group = FactoryGirl.build(:group) |
||||
|
||||
send_table_to_object group, table, { :name => Proc.new { |group, name| group.lastname = name } } |
||||
end |
||||
|
||||
Given /^the group "(.+)" is a "(.+)" in the project "(.+)"$/ do |group_name, role_name, project_identifier| |
||||
steps %Q{ Given the principal "#{group_name}" is a "#{role_name}" in the project "#{project_identifier}" } |
||||
end |
||||
|
||||
Given /^the group "(.+?)" has the following members:$/ do |name, table| |
||||
group = Group.find_by_lastname(name) |
||||
|
||||
raise "No group with name #{name} found" unless group.present? |
||||
|
||||
user_names = table.raw.flatten |
||||
|
||||
users = User.find_all_by_login(user_names) |
||||
|
||||
not_found = user_names - users.map(&:login) |
||||
|
||||
raise "Could not find users with login: #{not_found}" if not_found.size > 0 |
||||
|
||||
group.add_member!(users) |
||||
end |
||||
|
||||
When /^I add the user "(.+)" to the group$/ do |user_login| |
||||
user = User.find_by_login(user_login) |
||||
|
||||
raise "Could not find users with login: #{user_login}" if user.nil? |
||||
|
||||
steps %Q{ When I check "#{user.name}" within "#tab-content-users #users" |
||||
And I press "Add" } |
||||
end |
||||
|
||||
Given /^there is a group named "(.*?)" with the following members:$/ do |name, table| |
||||
group = FactoryGirl.create(:group, :lastname => name) |
||||
|
||||
table.raw.flatten.each do |login| |
||||
group.users << User.find_by_login!(login) |
||||
end |
||||
end |
||||
|
||||
When /^I delete the "([^"]*)" membership$/ do |group_name| |
||||
membership = member_for_login(group_name) |
||||
step %Q(I follow "Delete" within "#member-#{membership.id}") |
||||
end |
||||
|
||||
When /^I delete "([^"]*)" from the group$/ do |login| |
||||
user = User.find_by_login!(login) |
||||
step %Q(I follow "Delete" within "#user-#{user.id}") |
||||
end |
||||
|
||||
InstanceFinder.register(Group, Proc.new{ |name| Group.find_by_lastname(name) }) |
@ -0,0 +1,134 @@ |
||||
Given /^the following languages are active:$/ do |table| |
||||
Setting.available_languages = table.raw.flatten |
||||
end |
||||
|
||||
Given /^the (.+) called "(.+)" has the following localizations:$/ do |model_name, object_name, table| |
||||
model = model_name.downcase.gsub(/\s/, "_").camelize.constantize |
||||
object = model.find_by_name(object_name) |
||||
|
||||
object.translations = [] |
||||
|
||||
table.hashes.each do |h| |
||||
h.each do |k, v| |
||||
h[k] = nil if v == "nil" |
||||
end |
||||
|
||||
object.translations.create h |
||||
end |
||||
end |
||||
|
||||
When /^I delete the (.+) localization of the "(.+)" attribute$/ do |language, attribute| |
||||
locale = { "german" => "de", "english" => "en", "french" => "fr" }[language] |
||||
|
||||
attribute_spans = [] |
||||
|
||||
wait_until(5) do |
||||
attribute_spans = page.all(:css, "span.#{attribute}_translation") |
||||
attribute_spans.size > 0 |
||||
end |
||||
|
||||
attribute_span = attribute_spans.detect do |attribute_span| |
||||
attribute_span.find(:css, ".locale_selector")["value"] == locale |
||||
end |
||||
|
||||
destroy = attribute_span.find(:css, "a.destroy_locale") |
||||
|
||||
destroy.click |
||||
end |
||||
|
||||
When /^I change the (.+) localization of the "(.+)" attribute to be (.+)$/ do |language, attribute, new_language| |
||||
attribute_span = span_for_localization language, attribute |
||||
|
||||
locale_selector = attribute_span.find(:css, ".locale_selector") |
||||
|
||||
locale_name = locale_selector.all(:css, "option").detect{ |o| o.value == locale_for_language(new_language) } |
||||
locale_selector.select(locale_name.text) if locale_name |
||||
end |
||||
|
||||
When /^I add the (.+) localization of the "(.+)" attribute as "(.+)"$/ do |language, attribute, value| |
||||
new_elements = span_for_localization language, attribute |
||||
unless new_elements.present? |
||||
attribute_p = page.find(:xpath, "//span[contains(@class, '#{attribute}_translation')]/..") |
||||
add_link = attribute_p.find(:css, ".add_locale") |
||||
|
||||
add_link.click |
||||
|
||||
new_elements = attribute_p.all(:css, ".#{attribute}_translation").last |
||||
end |
||||
|
||||
new_value = new_elements.find(:css, "input[type=text], textarea") |
||||
new_locale = new_elements.find(:css, ".locale_selector") |
||||
|
||||
new_value.set(value.gsub("\\n", "\n")) |
||||
|
||||
locale_name = new_locale.all(:css, "option").detect{|o| o.value == locale_for_language(language)} |
||||
new_locale.select(locale_name.text) if locale_name |
||||
end |
||||
|
||||
Then /^there should be the following localizations:$/ do |table| |
||||
wait_for_page_load |
||||
|
||||
cleaned_expectation = table.hashes.map do |x| |
||||
x.reject{ |k, v| v == "nil" } |
||||
end |
||||
|
||||
attributes = [] |
||||
|
||||
wait_until(5) do |
||||
attributes = page.all(:css, "[name*=\"translations_attributes\"]:not([disabled=disabled])") |
||||
attributes.size > 0 |
||||
end |
||||
|
||||
name_regexp = /\[(\d)+\]\[(\w+)\]$/ |
||||
|
||||
attribute_group = attributes.inject({}) do |h, element| |
||||
if element['name'] =~ name_regexp |
||||
h[$1] ||= [] |
||||
h[$1] << element |
||||
end |
||||
h |
||||
end |
||||
|
||||
actual_localizations = attribute_group.inject([]) do |a, (k, group)| |
||||
a << group.inject({}) do |h, element| |
||||
if element['name'] =~ name_regexp |
||||
|
||||
if $2 != "id" and |
||||
$2 != "_destroy" and |
||||
(element['type'] != 'checkbox' or (element['type'] == 'checkbox' and element.checked?)) |
||||
|
||||
h[$2] = element['value'] |
||||
end |
||||
end |
||||
|
||||
h |
||||
end |
||||
|
||||
a |
||||
end |
||||
|
||||
actual_localizations = actual_localizations.group_by{|e| e["locale"]}.collect{|(k, v)| v.inject({}){|a, x| a.merge(x)} } |
||||
|
||||
actual_localizations.should =~ cleaned_expectation |
||||
end |
||||
|
||||
Then /^the delete link for the (.+) localization of the "(.+)" attribute should not be visible$/ do |locale, attribute_name| |
||||
attribute_span = span_for_localization locale, attribute_name |
||||
|
||||
attribute_span.find(:css, "a.destroy_locale").should_not be_visible |
||||
end |
||||
|
||||
def span_for_localization language, attribute |
||||
locale = locale_for_language language |
||||
|
||||
attribute_spans = page.all(:css, "span.#{attribute}_translation") |
||||
|
||||
attribute_spans.detect do |attribute_span| |
||||
attribute_span.find(:css, ".locale_selector")["value"] == locale && |
||||
attribute_span.visible? |
||||
end |
||||
end |
||||
|
||||
def locale_for_language language |
||||
{ "german" => "de", "english" => "en", "french" => "fr" }[language] |
||||
end |
@ -0,0 +1,18 @@ |
||||
Then /^I should see the "(.+)" image(?: within "([^\"]*)")?$/ do |image, selector| |
||||
search_string = selector ? Nokogiri::CSS.xpath_for(selector).first + "//img[contains(@src,\"#{image}\")]" : "//img[contains(@src,\"#{image}\")]" |
||||
page.should have_xpath(search_string) |
||||
end |
||||
|
||||
Then /^I should not see the "(.+)" image(?: within "([^\"]*)")?$/ do |image, selector| |
||||
search_string = selector ? Nokogiri::CSS.xpath_for(selector).first + "//img[contains(@src,\"#{image}\")]" : "//img[contains(@src,\"#{image}\")]" |
||||
page.should have_no_xpath(search_string) |
||||
end |
||||
|
||||
Then /^I should see an image(?: within "(.+?)")$/ do |selector| |
||||
with_scope(selector) do |
||||
img = find :css, 'img' |
||||
img.should be_present |
||||
img.should be_visible |
||||
end |
||||
end |
||||
|
@ -0,0 +1,67 @@ |
||||
# TODO: check if this step can be removed as it is plugin specific |
||||
Given /^there is a standard project named "([^\"]*)"$/ do |name| |
||||
steps %Q{ |
||||
Given there is 1 project with the following: |
||||
| Name | #{name} | |
||||
And there is a role "Manager" |
||||
And there is a role "Developer" |
||||
And there is a role "Designer" |
||||
And the role "Manager" may have the following rights: |
||||
| view_own_hourly_rate | |
||||
| view_hourly_rates | |
||||
| view_cost_rates | |
||||
| view_own_time_entries | |
||||
| view_own_cost_entries | |
||||
| view_cost_entries | |
||||
| view_time_entries | |
||||
And the role "Developer" may have the following rights: |
||||
| view_own_hourly_rate | |
||||
| view_hourly_rates | |
||||
| view_cost_rates | |
||||
| view_own_time_entries | |
||||
| view_own_cost_entries | |
||||
| view_cost_entries | |
||||
| view_time_entries | |
||||
And the role "Designer" may have the following rights: |
||||
| view_own_hourly_rate | |
||||
| view_hourly_rates | |
||||
| view_cost_rates | |
||||
| view_own_time_entries | |
||||
| view_own_cost_entries | |
||||
| view_cost_entries | |
||||
| view_time_entries | |
||||
And there is 1 user with: |
||||
| Login | manager | |
||||
| Firstname | Mac | |
||||
| Lastname | Moneysack | |
||||
| default rate | 10.00 | |
||||
And there is 1 user with: |
||||
| Login | developer | |
||||
| Firstname | Alan | |
||||
| Lastname | Kay | |
||||
| default rate | 10.00 | |
||||
And there is 1 user with: |
||||
| Login | designer | |
||||
| Firstname | Tom | |
||||
| Lastname | Kelley | |
||||
| default rate | 10.00 | |
||||
And the user "manager" is a "Manager" in the project "#{name}" |
||||
And the user "designer" is a "Designer" in the project "#{name}" |
||||
And the user "developer" is a "Developer" in the project "#{name}" |
||||
} |
||||
end |
||||
|
||||
Then /^[iI] should (not )?see "([^\"]*)" in the overall sum(?:s)?$/ do |negative, sum| |
||||
step %Q{I should #{negative}see "#{sum}" within "tr.sum.all"} |
||||
end |
||||
|
||||
Then /^[iI] should (not )?see "([^\"]*)" in the grouped sum(?:s)?$/ do |negative, sum| |
||||
step %Q{I should #{negative}see "#{sum}" within "tr.sum.grouped"} |
||||
end |
||||
|
||||
Then /^[iI] toggle the [oO]ptions fieldset$/ do |
||||
page.execute_script <<-JS |
||||
f = $$("fieldset").without($("filters")).first(); |
||||
toggleFieldset($(f).select("legend").first()); |
||||
JS |
||||
end |
@ -0,0 +1,9 @@ |
||||
Given /^the issue "(.*?)" is watched by:$/ do |issue_subject, watchers| |
||||
issue = Issue.find(:last, :conditions => {:subject => issue_subject}, :order => :created_on) |
||||
watchers.raw.flatten.each {|w| issue.add_watcher User.find_by_login(w)} |
||||
issue.save |
||||
end |
||||
|
||||
Then /^the issue "(.*?)" should have (\d+) watchers$/ do |issue_subject, watcher_count| |
||||
Issue.find_by_subject(issue_subject).watchers.count.should == watcher_count.to_i |
||||
end |
@ -0,0 +1,10 @@ |
||||
When /^(?:|I )follow "([^\"]*)"(?: within "([^\"]*)")?$/ do |link, selector| |
||||
with_scope(selector) do |
||||
click_link(link) |
||||
end |
||||
end |
||||
|
||||
Then /^the "([^"]*)" link should point to "([^"]*)"$/ do |title, target| |
||||
node = page.find(:xpath, "//a[contains(.,'#{title}')]") |
||||
node['href'].should == target |
||||
end |
@ -0,0 +1,11 @@ |
||||
When /^I toggle the "([^"]+)" submenu$/ do |menu_name| |
||||
nodes = all(:css, ".menu_root a[title=\"#{menu_name}\"] .toggler") |
||||
|
||||
# w/o javascript, all menu elements are expanded by default. So the toggler |
||||
# might not be present. |
||||
nodes.first.click if nodes.present? |
||||
end |
||||
|
||||
Then /^there should be no menu item selected$/ do |
||||
page.should_not have_css("#main-menu .selected") |
||||
end |
@ -0,0 +1,13 @@ |
||||
Given /^the principal "(.+)" is a "(.+)" in the project "(.+)"$/ do |principal_name, role_name, project_identifier| |
||||
project = Project.find_by_identifier(project_identifier) |
||||
raise "No project with identifier '#{project_identifier}' found" if project.nil? |
||||
|
||||
role = Role.find_by_name(role_name) |
||||
raise "No role with name '#{role_name}' found" if role.nil? |
||||
|
||||
principal = InstanceFinder.find(Principal, principal_name) |
||||
|
||||
project.add_member!(principal, role) |
||||
end |
||||
|
||||
InstanceFinder.register(Principal, Proc.new{ |name| Principal.first(:conditions => ["lastname = ? OR login = ?", name, name]) }) |
@ -0,0 +1,78 @@ |
||||
def filter_user_by_login login |
||||
# method to be overridden by plugins |
||||
user = User.find_by_login(login) |
||||
|
||||
steps %Q{ When I fill in "principal_search" with "#{user.name}" |
||||
And I wait for the AJAX requests to finish } |
||||
end |
||||
|
||||
When /^I check the role "(.+?)" for the project member "(.+?)"$/ do |role_name, user_login| |
||||
role = Role.find_by_name(role_name) |
||||
|
||||
member = member_for_login user_login |
||||
|
||||
steps %Q{When I check "member_role_ids_#{role.id}" within "#member-#{member.id}"} |
||||
end |
||||
|
||||
Then /^the project member "(.+?)" should not be in edit mode$/ do |user_login| |
||||
member = member_for_login user_login |
||||
|
||||
page.find("#member-#{member.id}-roles-form").should_not be_visible |
||||
end |
||||
|
||||
Then /^the project member "(.+?)" should have the role "(.+?)"$/ do |user_login, role_name| |
||||
member = member_for_login user_login |
||||
|
||||
steps %Q{Then I should see "#{role_name}" within "#member-#{member.id}-roles"} |
||||
end |
||||
|
||||
When /^I follow the delete link of the project member "(.+?)"$/ do |login_name| |
||||
member = member_for_login login_name |
||||
|
||||
steps %Q{When I follow "Delete" within "#member-#{member.id}"} |
||||
end |
||||
|
||||
When /^I go to the project member settings of the project(?: called) "(.+?)"$/ do |project_name| |
||||
steps %Q{ |
||||
When I go to the settings page of the project called "#{project_name}" |
||||
And I click on "tab-members" |
||||
} |
||||
end |
||||
|
||||
When /^I add the principal "(.+)" as a member with the roles:$/ do |principal_name, roles_table| |
||||
steps %Q{ When I check "#{principal_name}" within "#tab-content-members" } |
||||
|
||||
roles_table.raw.flatten.each do |role_name| |
||||
steps %Q{ When I check "#{role_name}" within "#tab-content-members .splitcontentright" } |
||||
end |
||||
|
||||
steps %Q{ When I press "Add" within "#tab-content-members .splitcontentright" } |
||||
end |
||||
|
||||
Then /^I should see the principal "(.+)" as a member with the roles:$/ do |principal_name, roles_table| |
||||
principal = InstanceFinder.find(Principal, principal_name) |
||||
|
||||
steps %Q{ Then I should see "#{principal.name}" within "#tab-content-members .member" } |
||||
|
||||
found_roles = page.find(:xpath, "//tr[contains(concat(' ',normalize-space(@class),' '),' member ')][contains(.,'#{principal.name}')]").find(:css, "td.roles span").text.split(",").map(&:strip) |
||||
|
||||
found_roles.should =~ roles_table.raw.flatten |
||||
end |
||||
|
||||
Then /^I should not see the principal "(.+)" as a member$/ do |principal_name| |
||||
principal = InstanceFinder.find(Principal, principal_name) |
||||
|
||||
steps %Q{ Then I should not see "#{principal.name}" within "#tab-content-members .member" } |
||||
end |
||||
|
||||
When /^I filter for the user "(.+?)"$/ do |login| |
||||
filter_user_by_login login |
||||
end |
||||
|
||||
def member_for_login principal_name |
||||
principal = InstanceFinder.find(Principal, principal_name) |
||||
|
||||
#the assumption here is, that there is only one project |
||||
principal.members.first |
||||
end |
||||
|
@ -0,0 +1,30 @@ |
||||
Given /^the rest api is enabled$/ do |
||||
Setting.rest_api_enabled = "1" |
||||
end |
||||
|
||||
Given /^the following languages are available:$/ do |table| |
||||
Setting.available_languages += table.raw.map(&:first) |
||||
end |
||||
|
||||
#Given /^the "(.+?)" setting is set to (true|false)$/ do |name, trueish| |
||||
# Setting[name.to_sym] = (trueish == "true" ? "1" : "0") |
||||
#end |
||||
|
||||
Given /^the "(.+?)" setting is set to (.+)$/ do |name, value| |
||||
value = case value |
||||
when "true" |
||||
"1" |
||||
when "false" |
||||
"0" |
||||
else |
||||
value |
||||
end |
||||
|
||||
value = value.to_i if Setting.available_settings[name]["format"] == "int" |
||||
|
||||
Setting[name.to_sym] = value |
||||
end |
||||
|
||||
Then /^the "(.+?)" setting should be (true|false)$/ do |name, trueish| |
||||
Setting.send((name + "?").to_sym).should == (trueish == "true") |
||||
end |
@ -0,0 +1,11 @@ |
||||
Then /^there should be a user with the following:$/ do |table| |
||||
expected = table.rows_hash |
||||
|
||||
user = User.find_by_login(expected["login"]) |
||||
|
||||
user.should_not be_nil |
||||
|
||||
expected.each do |key, value| |
||||
user.send(key).should == value |
||||
end |
||||
end |
@ -0,0 +1,384 @@ |
||||
# IMPORTANT: This file is generated by cucumber-rails - edit at your own peril. |
||||
# It is recommended to regenerate this file in the future when you upgrade to a |
||||
# newer version of cucumber-rails. Consider adding your own code to a new file |
||||
# instead of editing this one. Cucumber will automatically load all features/**/*.rb |
||||
# files. |
||||
|
||||
|
||||
require 'uri' |
||||
require File.expand_path(File.join(File.dirname(__FILE__), "..", "support", "paths")) |
||||
|
||||
module WithinHelpers |
||||
def with_scope(locator) |
||||
locator ? within(locator) { yield } : yield |
||||
end |
||||
end |
||||
World(WithinHelpers) |
||||
|
||||
Given /^(?:|I )am on (.+)$/ do |page_name| |
||||
visit path_to(page_name) |
||||
|
||||
disable_warn_unsaved_popup |
||||
end |
||||
|
||||
When /^(?:|I )go to (.+)$/ do |page_name| |
||||
step %Q{I am on #{page_name}} |
||||
end |
||||
|
||||
When /^I click(?: on)? "(.+?)" within "([^\"]*)"$/ do |target, selector| |
||||
element = find_lowest_containing_element(target, selector).first |
||||
if element.nil? |
||||
with_scope(selector) do |
||||
element = find_button(target) |
||||
end |
||||
end |
||||
element.click |
||||
end |
||||
|
||||
When /^(?:|I )press "([^\"]*)"(?: within "([^\"]*)")?$/ do |button, selector| |
||||
with_scope(selector) do |
||||
begin |
||||
click_button(button) |
||||
rescue Capybara::ElementNotFound |
||||
page.find(:css, button).click |
||||
end |
||||
end |
||||
end |
||||
|
||||
When /^(?:|I )fill in "([^\"]*)" with "([^\"]*)"(?: within "([^\"]*)")?(?: if plugin "(.+)" is loaded)?$/ do |field, value, selector, plugin_name| |
||||
if plugin_name.nil? || Redmine::Plugin.installed?(plugin_name) |
||||
with_scope(selector) do |
||||
fill_in(field, :with => value) |
||||
end |
||||
end |
||||
end |
||||
|
||||
When /^(?:|I )fill in "([^\"]*)" for "([^\"]*)"(?: within "([^\"]*)")?$/ do |value, field, selector| |
||||
with_scope(selector) do |
||||
fill_in(field, :with => value) |
||||
end |
||||
end |
||||
|
||||
# Use this to fill in an entire form with data from a table. Example: |
||||
# |
||||
# When I fill in the following: |
||||
# | Account Number | 5002 | |
||||
# | Expiry date | 2009-11-01 | |
||||
# | Note | Nice guy | |
||||
# | Wants Email? | | |
||||
# |
||||
# TODO: Add support for checkbox, select og option |
||||
# based on naming conventions. |
||||
# |
||||
When /^(?:|I )fill in the following(?: within "([^\"]*)")?:$/ do |selector, fields| |
||||
with_scope(selector) do |
||||
fields.rows_hash.each do |name, value| |
||||
step %Q{I fill in "#{name}" with "#{value}"} |
||||
end |
||||
end |
||||
end |
||||
|
||||
When /^(?:|I )select "([^\"]*)" from "([^\"]*)"(?: within "([^\"]*)")?$/ do |value, field, selector| |
||||
with_scope(selector) do |
||||
select(value, :from => field) |
||||
end |
||||
end |
||||
|
||||
When /^(?:|I )unselect "([^\"]*)" from "([^\"]*)"(?: within "([^\"]*)")?$/ do |value, field, selector| |
||||
with_scope(selector) do |
||||
unselect(value, :from => field) |
||||
end |
||||
end |
||||
|
||||
When /^(?:|I )check "([^\"]*)"(?: within "([^\"]*)")?$/ do |field, selector| |
||||
with_scope(selector) do |
||||
check(field) |
||||
end |
||||
end |
||||
|
||||
When /^(?:|I )uncheck "([^\"]*)"(?: within "([^\"]*)")?$/ do |field, selector| |
||||
with_scope(selector) do |
||||
uncheck(field) |
||||
end |
||||
end |
||||
|
||||
When /^(?:|I )choose "([^\"]*)"(?: within "([^\"]*)")?$/ do |field, selector| |
||||
with_scope(selector) do |
||||
choose(field) |
||||
end |
||||
end |
||||
|
||||
When /^(?:|I )attach the file "([^\"]*)" to "([^\"]*)"(?: within "([^\"]*)")?$/ do |path, field, selector| |
||||
with_scope(selector) do |
||||
attach_file(field, path) |
||||
end |
||||
end |
||||
|
||||
When /^I wait(?: (\d+) seconds)? for(?: the)? [Aa][Jj][Aa][Xx](?: requests?(?: to finish)?)?$/ do |timeout| |
||||
ajax_done = lambda do |
||||
page.evaluate_script(%Q{ |
||||
(function (){ |
||||
var done = true; |
||||
|
||||
if (window.jQuery) { |
||||
if (window.jQuery.active != 0) { |
||||
done = false; |
||||
} |
||||
} |
||||
if (window.Prototype && window.Ajax) { |
||||
if (window.Ajax.activeRequestCount != 0) { |
||||
done = false; |
||||
} |
||||
} |
||||
|
||||
return done; |
||||
}()) |
||||
}.gsub("\n", '')) |
||||
end |
||||
|
||||
timeout = timeout.present? ? |
||||
timeout.to_f : |
||||
5.0 |
||||
|
||||
wait_until(timeout) do |
||||
ajax_done.call |
||||
end |
||||
end |
||||
|
||||
|
||||
Then /^(?:|I )should see "([^\"]*)"(?: within "([^\"]*)")?$/ do |text, selector| |
||||
if defined?(Spec::Rails::Matchers) |
||||
begin |
||||
wait_until(5) do |
||||
elements = find_lowest_containing_element text, selector |
||||
elements.length > 0 && elements[-1].visible? |
||||
end |
||||
rescue Capybara::TimeoutError |
||||
fail_msg = "#{text} did not become visible within #{selector} within 5 sec." |
||||
|
||||
unless page.has_content?(text) |
||||
fail_msg << " It might not even on the page at all!" |
||||
end |
||||
|
||||
fail fail_msg |
||||
end |
||||
|
||||
else |
||||
with_scope(selector) do |
||||
assert page.has_content?(text) |
||||
end |
||||
end |
||||
end |
||||
|
||||
Then /^(?:|I )should see \/([^\/]*)\/(?: within "([^\"]*)")?$/ do |regexp, selector| |
||||
|
||||
regexp = Regexp.new(regexp) |
||||
with_scope(selector) do |
||||
if defined?(Spec::Rails::Matchers) |
||||
page.should have_xpath('//*', :text => regexp) |
||||
else |
||||
assert page.has_xpath?('//*', :text => regexp) |
||||
end |
||||
end |
||||
end |
||||
|
||||
Then /^(?:|I )should not see "([^\"]*)"(?: within "([^\"]*)")?$/ do |text, selector| |
||||
if defined?(Spec::Rails::Matchers) |
||||
begin |
||||
wait_until(5) do |
||||
elements = find_lowest_containing_element text, selector |
||||
elements.length == 0 || !elements[-1].visible? |
||||
end |
||||
rescue Capybara::TimeoutError |
||||
fail "#{text} did not become in-visible within #{selector} within 5 sec" |
||||
rescue Selenium::WebDriver::Error::ObsoleteElementError |
||||
with_selector = selector.present? ? " within \"#{selector}\"" : "" |
||||
step %Q{I should not see "#{text}#{with_selector}"} |
||||
end |
||||
|
||||
else |
||||
with_scope(selector) do |
||||
assert page.has_no_content?(text) |
||||
end |
||||
end |
||||
end |
||||
|
||||
Then /^(?:|I )should not see \/([^\/]*)\/(?: within "([^\"]*)")?$/ do |regexp, selector| |
||||
regexp = Regexp.new(regexp) |
||||
with_scope(selector) do |
||||
if defined?(Spec::Rails::Matchers) |
||||
page.should have_no_xpath('//*', :text => regexp) |
||||
else |
||||
assert page.has_no_xpath?('//*', :text => regexp) |
||||
end |
||||
end |
||||
end |
||||
|
||||
Then /^the "([^\"]*)" field(?: within "([^\"]*)")? should contain "([^\"]*)"$/ do |field, selector, value| |
||||
with_scope(selector) do |
||||
if defined?(Spec::Rails::Matchers) |
||||
find_field(field).value.should =~ /#{value}/ |
||||
else |
||||
assert_match(/#{value}/, field_labeled(field).value) |
||||
end |
||||
end |
||||
end |
||||
|
||||
Then /^the "([^\"]*)" field(?: within "([^\"]*)")? should not contain "([^\"]*)"$/ do |field, selector, value| |
||||
with_scope(selector) do |
||||
if defined?(Spec::Rails::Matchers) |
||||
find_field(field).value.should_not =~ /#{value}/ |
||||
else |
||||
assert_no_match(/#{value}/, find_field(field).value) |
||||
end |
||||
end |
||||
end |
||||
|
||||
Then /^there should be a( disabled)? "(.+)" field( visible| invisible)?(?: within "([^\"]*)")?(?: if plugin "(.+)" is loaded)?$/ do |disabled, fieldname, visible, selector, plugin_name| |
||||
if plugin_name.nil? || Redmine::Plugin.installed?(plugin_name) |
||||
with_scope(selector) do |
||||
if defined?(Spec::Rails::Matchers) |
||||
find_field(fieldname).should_not be_nil |
||||
if visible && visible == " visible" |
||||
find_field(fieldname).should be_visible |
||||
elsif visible && visible == " invisible" |
||||
find_field(fieldname).should_not be_visible |
||||
end |
||||
|
||||
if disabled |
||||
find_field(fieldname)[:disabled].should == "disabled" |
||||
else |
||||
find_field(fieldname)[:disabled].should == nil |
||||
end |
||||
else |
||||
assert_not_nil find_field(fieldname) |
||||
end |
||||
end |
||||
end |
||||
end |
||||
|
||||
Then /^there should not be a "(.+)" field(?: within "([^\"]*)")?$/ do |fieldname, selector| |
||||
with_scope(selector) do |
||||
if defined?(Spec::Rails::Matchers) |
||||
lambda {find_field(fieldname)}.should raise_error(Capybara::ElementNotFound) |
||||
else |
||||
assert_nil find_field(fieldname) |
||||
end |
||||
end |
||||
end |
||||
|
||||
Then /^there should be a "(.+)" button$/ do |button_label| |
||||
if defined?(Spec::Rails::Matchers) |
||||
page.should have_xpath("//input[@value='#{button_label}']") |
||||
else |
||||
raise NotImplementedError, "Only Matcher implemented" |
||||
end |
||||
end |
||||
|
||||
Then /^the "([^\"]*)" select(?: within "([^\"]*)")? should have the following options:$/ do |field, selector, option_table| |
||||
options_expected = option_table.raw.collect(&:to_s) |
||||
|
||||
with_scope(selector) do |
||||
|
||||
field = find_field(field) |
||||
options_actual = field.all('option').collect(&:text) |
||||
|
||||
if defined?(Spec::Rails::Matchers) |
||||
options_actual.should =~ options_expected |
||||
else |
||||
raise NotImplementedError, "Only Matcher implemented" |
||||
end |
||||
end |
||||
end |
||||
|
||||
Then /^the "([^\"]*)" checkbox(?: within "([^\"]*)")? should be checked$/ do |label, selector| |
||||
with_scope(selector) do |
||||
if defined?(Spec::Rails::Matchers) |
||||
find_field(label)['checked'].should be_true |
||||
else |
||||
assert_equal 'checked', field_labeled(label)['checked'] |
||||
end |
||||
end |
||||
end |
||||
|
||||
Then /^the "([^\"]*)" checkbox(?: within "([^\"]*)")? should not be checked$/ do |label, selector| |
||||
with_scope(selector) do |
||||
if defined?(Spec::Rails::Matchers) |
||||
find_field(label)['checked'].should be_false |
||||
else |
||||
assert_not_equal 'checked', field_labeled(label)['checked'] |
||||
end |
||||
end |
||||
end |
||||
|
||||
Then /^(?:|I )should be on (.+)$/ do |page_name| |
||||
wait_for_page_load |
||||
|
||||
if defined?(Spec::Rails::Matchers) |
||||
URI.parse(current_url).path.should == path_to(page_name) |
||||
else |
||||
assert_equal path_to(page_name), URI.parse(current_url).path |
||||
end |
||||
end |
||||
|
||||
Then /^(?:|I )should have the following query string:$/ do |expected_pairs| |
||||
actual_params = CGI.parse(URI.parse(current_url).query) |
||||
expected_params = Hash[expected_pairs.rows_hash.map{|k,v| [k,[v]]}] |
||||
|
||||
if defined?(Spec::Rails::Matchers) |
||||
actual_params.should == expected_params |
||||
else |
||||
assert_equal expected_params, actual_params |
||||
end |
||||
end |
||||
|
||||
Then /^show me the page$/ do |
||||
save_and_open_page |
||||
end |
||||
|
||||
# This needs an active js driver to work properly |
||||
Given /^I (accept|dismiss) the alert dialog$/ do |method| |
||||
if Capybara.current_driver.to_s.include?("selenium") |
||||
page.driver.browser.switch_to.alert.send(method.to_s) |
||||
end |
||||
end |
||||
|
||||
def find_lowest_containing_element text, selector |
||||
elements = [] |
||||
|
||||
node_criteria = "[contains(., \"#{text}\") and not(self::script) and not(child::*[contains(., \"#{text}\")])]" |
||||
|
||||
if selector |
||||
search_string = Nokogiri::CSS.xpath_for(selector).first + "//*#{node_criteria}" |
||||
search_string += " | " + Nokogiri::CSS.xpath_for(selector).first + "#{node_criteria}" |
||||
else |
||||
search_string = "//*#{node_criteria}" |
||||
end |
||||
elements = all(:xpath, search_string) |
||||
|
||||
rescue Capybara::TimeoutError, Nokogiri::CSS::SyntaxError |
||||
elements |
||||
end |
||||
|
||||
def disable_warn_unsaved_popup |
||||
# disable WarnLeavingUnsaved function call when testing with selenium as this |
||||
# will freeze the server |
||||
|
||||
if defined?(ChiliProject::VERSION::MAJOR) && |
||||
ChiliProject::VERSION::MAJOR > 1 && |
||||
Capybara.current_driver.to_s.include?("selenium") |
||||
|
||||
page.execute_script("window.onbeforeunload = null") |
||||
end |
||||
end |
||||
|
||||
def wait_for_page_load(seconds = 5) |
||||
begin |
||||
wait_until(seconds) do |
||||
page.has_css?('body') |
||||
end |
||||
rescue Capybara::TimeoutError |
||||
fail "Page did not load within #{seconds} seconds" |
||||
end |
||||
end |
||||
|
@ -0,0 +1,32 @@ |
||||
Given /^the project "(.*?)" has (?:1|a) wiki menu item with the following:$/ do |project_name, table| |
||||
item = FactoryGirl.build(:wiki_menu_item) |
||||
send_table_to_object(item, table) |
||||
item.wiki = Project.find_by_name(project_name).wiki |
||||
item.save! |
||||
end |
||||
|
||||
Given /^the project "(.*?)" has a child wiki page of "(.*?)" with the following:$/ do |project_name, parent_page_title, table| |
||||
wiki = Project.find_by_name(project_name).wiki |
||||
wikipage = FactoryGirl.build(:wiki_page, :wiki => wiki) |
||||
|
||||
send_table_to_object(wikipage, table) |
||||
|
||||
FactoryGirl.create(:wiki_content, :page => wikipage) |
||||
|
||||
parent_page = WikiPage.find_by_wiki_id_and_title(wiki.id, parent_page_title) |
||||
wikipage.parent_id = parent_page.id |
||||
wikipage.save! |
||||
end |
||||
|
||||
Then /^the table of contents wiki menu item within the "(.*?)" menu item should be selected$/ do |parent_item_name| |
||||
parent_item = WikiMenuItem.find_by_title(parent_item_name) |
||||
|
||||
page.should have_css(".#{parent_item.item_class}-toc.selected") |
||||
end |
||||
|
||||
Then /^the child page wiki menu item within the "(.*?)" menu item should be selected$/ do |parent_item_name| |
||||
parent_item = WikiMenuItem.find_by_title(parent_item_name) |
||||
|
||||
page.should have_css(".#{parent_item.item_class}-new-page.selected") |
||||
end |
||||
|
@ -0,0 +1,2 @@ |
||||
require File.expand_path('../../../test/object_daddy_helpers', __FILE__) |
||||
World(ObjectDaddyHelpers) |
@ -0,0 +1,240 @@ |
||||
module NavigationHelpers |
||||
# Maps a name to a path. Used by the |
||||
# |
||||
# When /^I go to (.+)$/ do |page_name| |
||||
# |
||||
# step definition in web_steps.rb |
||||
# |
||||
def path_to(page_name) |
||||
case page_name |
||||
|
||||
when /the home\s?page/ |
||||
'/' |
||||
|
||||
when /the [wW]iki [pP]age "([^\"]+)" (?:for|of) the project called "([^\"]+)"$/ |
||||
wiki_page = Wiki.titleize($1) |
||||
project_identifier = $2.gsub("\"", "") |
||||
project = Project.find_by_name(project_identifier) |
||||
project_identifier = project.identifier.gsub(' ', '%20') |
||||
"/projects/#{project_identifier}/wiki/#{wiki_page}" |
||||
|
||||
when /the [cC]ost [rR]eports page (?:of|for) the project called "([^\"]+)" without filters or groups$/ |
||||
project_identifier = Project.find_by_name($1).identifier.gsub(' ', '%20') |
||||
"/projects/#{project_identifier}/cost_reports?set_filter=1" |
||||
|
||||
when /the [cC]ost [rR]eports page (?:of|for) the project called "([^\"]+)"$/ |
||||
project_identifier = Project.find_by_name($1).identifier.gsub(' ', '%20') |
||||
"/projects/#{project_identifier}/cost_reports" |
||||
|
||||
when /the overall [cC]ost [rR]eports page$/ |
||||
"/cost_reports" |
||||
|
||||
when /the overall [cC]ost [rR]eports page without filters or groups$/ |
||||
"/cost_reports?set_filter=1" |
||||
|
||||
when /the overall [cC]ost [rR]eports page with standard groups in debug mode$/ |
||||
"/cost_reports?set_filter=1&groups[columns][]=cost_type_id&groups[rows][]=user_id&debug=1" |
||||
|
||||
when /the overall [cC]ost [rR]eports page with standard groups/ |
||||
"/cost_reports?set_filter=1&groups[columns][]=cost_type_id&groups[rows][]=user_id" |
||||
|
||||
when /the overall [pP]rojects page/ |
||||
"/projects" |
||||
|
||||
when /^the (?:(?:overview |home ?))?page (?:for|of) the project(?: called)? "(.+)"$/ |
||||
project_identifier = $1.gsub("\"", "") |
||||
project_identifier = Project.find_by_name(project_identifier).identifier.gsub(' ', '%20') |
||||
"/projects/#{project_identifier}" |
||||
|
||||
when /^the activity page of the project(?: called)? "(.+)"$/ |
||||
project_identifier = $1.gsub("\"", "") |
||||
project_identifier = Project.find_by_name(project_identifier).identifier.gsub(' ', '%20') |
||||
"/projects/#{project_identifier}/activity" |
||||
|
||||
when /the page (?:for|of) the issue "([^\"]+)"/ |
||||
issue = Issue.find_by_subject($1) |
||||
"/issues/#{issue.id}" |
||||
|
||||
when /the edit page (?:for|of) the issue "([^\"]+)"/ |
||||
issue = Issue.find_by_subject($1) |
||||
"/issues/#{issue.id}/edit" |
||||
|
||||
when /the copy page (?:for|of) the issue "([^\"]+)"/ |
||||
issue = Issue.find_by_subject($1) |
||||
project = issue.project |
||||
"/projects/#{project.identifier}/issues/#{issue.id}/copy" |
||||
|
||||
when /the issues? index page (?:for|of) (the)? project(?: called)? (.+)/ |
||||
project_identifier = $2.gsub("\"", "") |
||||
project_identifier = Project.find_by_name(project_identifier).identifier.gsub(' ', '%20') |
||||
"/projects/#{project_identifier}/issues" |
||||
|
||||
when /the wiki index page(?: below the (.+) page)? (?:for|of) (?:the)? project(?: called)? (.+)/ |
||||
parent_page_title, project_identifier = $1, $2 |
||||
project_identifier.gsub!("\"", "") |
||||
project_identifier = Project.find_by_name(project_identifier).identifier.gsub(' ', '%20') |
||||
|
||||
if parent_page_title.present? |
||||
parent_page_title.gsub!("\"", "") |
||||
|
||||
"/projects/#{project_identifier}/wiki/#{parent_page_title}/toc" |
||||
else |
||||
"/projects/#{project_identifier}/wiki/index" |
||||
end |
||||
|
||||
when /the wiki new child page below the (.+) page (?:for|of) (?:the)? project(?: called)? (.+)/ |
||||
parent_page_title, project_identifier = $1, $2 |
||||
project_identifier.gsub!("\"", "") |
||||
parent_page_title.gsub!("\"", "") |
||||
project_identifier = Project.find_by_name(project_identifier).identifier.gsub(' ', '%20') |
||||
|
||||
"/projects/#{project_identifier}/wiki/#{parent_page_title}/new" |
||||
|
||||
when /the edit page (?:for |of )(the )?role(?: called)? (.+)/ |
||||
role_identifier = $2.gsub("\"", "") |
||||
role_identifier = Role.find_by_name(role_identifier).id |
||||
"/roles/edit/#{role_identifier}" |
||||
|
||||
when /the edit page (?:for |of )(the )?user(?: called)? (.+)/ |
||||
user_identifier = $2.gsub("\"", "") |
||||
user_identifier = User.find_by_login(user_identifier).id |
||||
"/users/#{user_identifier}/edit" |
||||
|
||||
when /the show page (?:for |of )(the )?user(?: called)? (.+)/ |
||||
user_identifier = $2.gsub("\"", "") |
||||
user_identifier = User.find_by_login(user_identifier).id |
||||
"/users/#{user_identifier}" |
||||
|
||||
when /the index page (?:for|of) users/ |
||||
"/users" |
||||
|
||||
when /^the global index page (?:for|of) (.+)$/ |
||||
"/#{$1}" |
||||
|
||||
when /the edit page (?:for |of )the version(?: called) (.+)/ |
||||
version_name = $1.gsub("\"", "") |
||||
version = Version.find_by_name(version_name) |
||||
"/versions/edit/#{version.id}" |
||||
|
||||
when /the new page (?:for|of) (.+)/ |
||||
model = $1.gsub!("\"", "").downcase |
||||
"/#{model.pluralize}/new" |
||||
|
||||
when /the edit page (?:for|of) (?:the )?([^\"]+?)(?: called)? "([^\"]+)"$/ |
||||
model, identifier = $1, $2 |
||||
identifier.gsub!("\"", "") |
||||
model = model.gsub("\"", "").gsub(/\s/, "_") |
||||
|
||||
begin |
||||
instance = InstanceFinder.find(model.camelize.constantize, identifier) |
||||
rescue NameError |
||||
instance = InstanceFinder.find(model.to_sym, identifier) |
||||
end |
||||
|
||||
root = RouteMap.route(instance.class) |
||||
|
||||
"#{root}/#{instance.id}/edit" |
||||
|
||||
when /the log\s?in page/ |
||||
'/login' |
||||
|
||||
when /^the log ?out page$/ |
||||
'/logout' |
||||
|
||||
when /the (register|registration) page/ |
||||
'/account/register' |
||||
|
||||
when /the activate registration page for the user called (.+) with (.+)/ |
||||
name = $1.dup |
||||
selection = $2.dup |
||||
name.gsub!("\"","") |
||||
selection.gsub!("\"","") |
||||
u = User.find_by_login(name) |
||||
"/account/#{u.id}/activate?#{selection}" |
||||
|
||||
when /the My page/ |
||||
'/my/page' |
||||
|
||||
when /the [mM]y account page/ |
||||
'/my/account' |
||||
|
||||
when /^the (administration|admin) page$/ |
||||
'/admin' |
||||
|
||||
when /the(?: (.+?) tab of the) settings page$/ |
||||
if $1.nil? |
||||
"/settings" |
||||
else |
||||
"/settings/edit?tab=#{$1}" |
||||
end |
||||
|
||||
when /the(?: (.+?) tab of the) settings page (?:of|for) the project "(.+?)"$/ |
||||
if $1.nil? |
||||
"/projects/#{$2}/settings" |
||||
else |
||||
"/projects/#{$2}/settings/#{$1}" |
||||
end |
||||
|
||||
when /the edit page of Announcement/ |
||||
'/announcements/1/edit' |
||||
|
||||
when /the index page of Roles/ |
||||
'/roles' |
||||
|
||||
when /the search page/ |
||||
'/search' |
||||
|
||||
when /the custom fields page/ |
||||
'/custom_fields' |
||||
|
||||
when /the enumerations page/ |
||||
'/enumerations' |
||||
|
||||
when /the authentication modes page/ |
||||
'/auth_sources' |
||||
|
||||
when /the page of the planning element "([^\"]+)" of the project called "([^\"]+)"$/ |
||||
planning_element_name = $1 |
||||
project_name = $2 |
||||
project_identifier = Project.find_by_name(project_name).identifier.gsub(' ', '%20') |
||||
planning_element = Timelines::PlanningElement.find_by_name(planning_element_name) |
||||
"/timelines/projects/#{project_identifier}/planning_elements/#{planning_element.id}" |
||||
|
||||
when /the (.+) page (?:for|of) the project called "([^\"]+)"$/ |
||||
project_page = $1 |
||||
project_identifier = $2.gsub("\"", "") |
||||
project_page = project_page.gsub(' ', '').underscore |
||||
project_identifier = Project.find_by_name(project_identifier).identifier.gsub(' ', '%20') |
||||
"/projects/#{project_identifier}/#{project_page}" |
||||
|
||||
when /^the quick reference for wiki syntax$/ |
||||
"/help/wiki_syntax" |
||||
|
||||
when /^the detailed wiki syntax help page$/ |
||||
"/help/wiki_syntax_detailed" |
||||
|
||||
when /^the configuration page of the "(.+)" plugin$/ |
||||
"/settings/plugin/#{$1}" |
||||
|
||||
when /^the admin page of the group called "([^"]*)"$/ |
||||
id = Group.find_by_lastname!($1).id |
||||
"/groups/#{id}/edit" |
||||
|
||||
# Add more mappings here. |
||||
# Here is an example that pulls values out of the Regexp: |
||||
# |
||||
# when /^(.*)'s profile page$/i |
||||
# user_profile_path(User.find_by_login($1)) |
||||
|
||||
else |
||||
begin |
||||
super |
||||
rescue |
||||
raise "Can't find mapping from \"#{page_name}\" to a path.\n" + |
||||
"Now, go and add a mapping in #{__FILE__}" |
||||
end |
||||
end |
||||
end |
||||
end |
||||
|
||||
World(NavigationHelpers) |
@ -0,0 +1,54 @@ |
||||
Feature: User deletion |
||||
|
||||
@javascript |
||||
Scenario: A user can delete himself if the setting permitts it |
||||
Given the "users_deletable_by_self" setting is set to true |
||||
And there is 1 user with the following: |
||||
| login | bob | |
||||
And I am already logged in as "bob" |
||||
And I go to the my account page |
||||
And I follow "Delete account" |
||||
And I press "Delete" |
||||
And I accept the alert dialog |
||||
Then I should see "Account successfully deleted" |
||||
And I should be on the login page |
||||
|
||||
Scenario: A user can not delete himself if the setting forbidds it |
||||
Given the "users_deletable_by_self" setting is set to false |
||||
And there is 1 user with the following: |
||||
| login | bob | |
||||
And I am already logged in as "bob" |
||||
And I go to the my account page |
||||
Then I should not see "Delete account" within "#main-menu" |
||||
|
||||
@javascript |
||||
Scenario: An admin can delete other users if the setting permitts it |
||||
Given the "users_deletable_by_admins" setting is set to true |
||||
And there is 1 user with the following: |
||||
| login | bob | |
||||
And I am already logged in as "admin" |
||||
When I go to the edit page of the user "bob" |
||||
And I follow "Delete" within ".contextual" |
||||
And I press "Delete" |
||||
And I accept the alert dialog |
||||
Then I should see "Account successfully deleted" |
||||
And I should be on the index page of users |
||||
|
||||
Scenario: An admin can not delete other users if the setting forbidds it |
||||
Given the "users_deletable_by_admins" setting is set to false |
||||
And there is 1 user with the following: |
||||
| login | bob | |
||||
And I am already logged in as "admin" |
||||
And I go to the edit page of the user "bob" |
||||
Then I should not see "Delete" within ".contextual" |
||||
|
||||
Scenario: Deletablilty settings can be set in the users tab of the settings |
||||
Given I am already logged in as "admin" |
||||
And the "users_deletable_by_admins" setting is set to false |
||||
And the "users_deletable_by_self" setting is set to false |
||||
And I go to the users tab of the settings page |
||||
And I check "settings_users_deletable_by_admins" |
||||
And I check "settings_users_deletable_by_self" |
||||
And I press "Save" |
||||
Then the "users_deletable_by_admins" setting should be true |
||||
Then the "users_deletable_by_self" setting should be true |
@ -0,0 +1,32 @@ |
||||
Feature: Wiki menu items |
||||
Background: |
||||
Given there is 1 project with the following: |
||||
| name | Awesome Project | |
||||
| identifier | awesome-project | |
||||
And there is a role "member" |
||||
And the role "member" may have the following rights: |
||||
| view_wiki_pages | |
||||
| edit_wiki_pages | |
||||
And there is 1 user with the following: |
||||
| login | bob | |
||||
And the user "bob" is a "member" in the project "Awesome Project" |
||||
And the project "Awesome Project" has 1 wiki page with the following: |
||||
| Title | Wiki | |
||||
And the project "Awesome Project" has 1 wiki page with the following: |
||||
| Title | Level1 | |
||||
And the project "Awesome Project" has a child wiki page of "Level1" with the following: |
||||
| Title | Level2 | |
||||
And the project "Awesome Project" has a child wiki page of "Level2" with the following: |
||||
| Title | Level3 | |
||||
And I am already logged in as "bob" |
||||
|
||||
Scenario: Breadcrumb with wiki hierarchy and a different menu item name |
||||
Given the project "Awesome Project" has a wiki menu item with the following: |
||||
| title | Level3 | |
||||
| name | SomethingCompletelyDifferent | |
||||
When I go to the wiki page "Level3" for the project called "Awesome Project" |
||||
Then I should see "Level1" within ".breadcrumb" |
||||
And I should see "Level2" within ".breadcrumb" |
||||
And I should not see "Level3" within ".breadcrumb" |
||||
And I should see "SomethingCompletelyDifferent" within ".breadcrumb" |
||||
|
@ -0,0 +1,41 @@ |
||||
Feature: Viewing the wiki index page |
||||
|
||||
Background: |
||||
Given there is 1 user with the following: |
||||
| login | bob | |
||||
And there is a role "member" |
||||
And the role "member" may have the following rights: |
||||
| view_wiki_pages | |
||||
And there is 1 project with the following: |
||||
| name | project1 | |
||||
| identifier | project1 | |
||||
And the user "bob" is a "member" in the project "project1" |
||||
And I am already logged in as "bob" |
||||
|
||||
Scenario: Visiting the wiki index page without a related page should show the overall index page and select no menu item |
||||
When I go to the wiki index page of the project called "project1" |
||||
Then I should see "Index by title" within "#content" |
||||
And there should be no menu item selected |
||||
|
||||
Scenario: Visiting the wiki index page with a related page that has the index page option enabled on it's menu item should show the page and select the toc menu entry within the wiki menu item |
||||
Given the project "project1" has 1 wiki page with the following: |
||||
| title | ParentWikiPage | |
||||
And the project "project1" has 1 wiki menu item with the following: |
||||
| title | ParentWikiPage | |
||||
| index_page | true | |
||||
When I go to the wiki index page below the "ParentWikiPage" page of the project called "project1" |
||||
Then I should see "Index by title" within "#content" |
||||
And the table of contents wiki menu item within the "ParentWikiPage" menu item should be selected |
||||
|
||||
Scenario: Visiting the wiki index page with a related page that has the index page option disabled on it's menu item should show the page and select no menu item |
||||
Given the project "project1" has 1 wiki page with the following: |
||||
| title | ParentWikiPage | |
||||
And the project "project1" has 1 wiki menu item with the following: |
||||
| title | ParentWikiPage | |
||||
When I go to the wiki index page below the "ParentWikiPage" page of the project called "project1" |
||||
Then I should see "Index by title" within "#content" |
||||
And there should be no menu item selected |
||||
|
||||
|
||||
|
||||
|
@ -0,0 +1,38 @@ |
||||
Feature: Viewing the wiki new child page |
||||
|
||||
Background: |
||||
Given there is 1 user with the following: |
||||
| login | bob | |
||||
And there is a role "member" |
||||
And the role "member" may have the following rights: |
||||
| view_wiki_pages | |
||||
| edit_wiki_pages | |
||||
And there is 1 project with the following: |
||||
| name | project1 | |
||||
| identifier | project1 | |
||||
And the user "bob" is a "member" in the project "project1" |
||||
And I am already logged in as "bob" |
||||
|
||||
Scenario: Visiting the wiki new child page with a parent page that has the new child page option enabled on it's menu item should show the page and select the toc menu entry within the wiki menu item |
||||
Given the project "project1" has 1 wiki page with the following: |
||||
| title | ParentWikiPage | |
||||
And the project "project1" has 1 wiki menu item with the following: |
||||
| title | ParentWikiPage | |
||||
| new_wiki_page | true | |
||||
When I go to the wiki new child page below the "ParentWikiPage" page of the project called "project1" |
||||
Then I should see "Create new child page" within "#content" |
||||
And the child page wiki menu item within the "ParentWikiPage" menu item should be selected |
||||
|
||||
Scenario: Visiting the wiki new child page with a related page that has the new child page option disabled on it's menu item should show the page and select no menu item |
||||
Given the project "project1" has 1 wiki page with the following: |
||||
| title | ParentWikiPage | |
||||
And the project "project1" has 1 wiki menu item with the following: |
||||
| title | ParentWikiPage | |
||||
When I go to the wiki new child page below the "ParentWikiPage" page of the project called "project1" |
||||
Then I should see "Create new child page" within "#content" |
||||
And there should be no menu item selected |
||||
|
||||
Scenario: Visiting the wiki new child page with an invalid parent page |
||||
When I go to the wiki new child page below the "InvalidPage" page of the project called "project1" |
||||
Then I should see "404" within "#content" |
||||
Then I should see "The page you were trying to access doesn't exist or has been removed." |
@ -0,0 +1,90 @@ |
||||
Feature: Wiki menu items |
||||
Background: |
||||
Given there is 1 project with the following: |
||||
| name | Awesome Project | |
||||
| identifier | awesome-project | |
||||
And there is a role "member" |
||||
And the role "member" may have the following rights: |
||||
| view_wiki_pages | |
||||
| edit_wiki_pages | |
||||
| manage_wiki_menu | |
||||
And there is 1 user with the following: |
||||
| login | bob | |
||||
And the user "bob" is a "member" in the project "Awesome Project" |
||||
And the project "Awesome Project" has 1 wiki page with the following: |
||||
| Title | Wiki | |
||||
And the project "Awesome Project" has 1 wiki page with the following: |
||||
| Title | AwesomePage | |
||||
And I am already logged in as "bob" |
||||
|
||||
@javascript |
||||
Scenario: Adding a main menu entry without index and toc links |
||||
When I go to the wiki page "AwesomePage" for the project called "Awesome Project" |
||||
And I click on "More functions" |
||||
And I click on "Configure menu item" |
||||
And I fill in "Aioli Wuaärst" for "wiki_menu_item_name" |
||||
And I choose "Show as menu item in project navigation" |
||||
And I press "Save" |
||||
And I should see "Aioli Wuaärst" within "#main-menu" |
||||
|
||||
@javascript |
||||
Scenario: Adding a main menu entry with index and toc links |
||||
When I go to the wiki page "AwesomePage" for the project called "Awesome Project" |
||||
And I click on "More functions" |
||||
And I click on "Configure menu item" |
||||
And I fill in "Aioli Wuaärst" for "wiki_menu_item_name" |
||||
And I choose "Show as menu item in project navigation" |
||||
And I check "Show submenu item 'Create new child page'" |
||||
And I check "Show submenu item 'Table of Contents'" |
||||
And I press "Save" |
||||
When I go to the wiki page "AwesomePage" for the project called "Awesome Project" |
||||
Then I should see "Aioli Wuaärst" within "#main-menu" |
||||
Then I should see "Table of Contents" within "#main-menu" |
||||
Then I should see "Create new child page" within "#main-menu" |
||||
|
||||
@javascript |
||||
Scenario: Change existing entry |
||||
When I go to the wiki page "Wiki" for the project called "Awesome Project" |
||||
Then I should see "Table of Contents" within "#main-menu" |
||||
Then I should see "Create new child page" within "#main-menu" |
||||
When I click on "More functions" |
||||
And I click on "Configure menu item" |
||||
And I fill in "Wikikiki" for "wiki_menu_item_name" |
||||
And I uncheck "Show submenu item 'Table of Contents'" |
||||
And I uncheck "Show submenu item 'Create new child page'" |
||||
And I press "Save" |
||||
When I go to the wiki page "Wiki" for the project called "Awesome Project" |
||||
Then I should see "Wikikiki" within "#main-menu" |
||||
Then I should not see "Table of Contents" within "#main-menu" |
||||
Then I should not see "Create new child page" within "#main-menu" |
||||
|
||||
|
||||
@javascript |
||||
Scenario: Adding a sub menu entry |
||||
Given the project "Awesome Project" has a wiki menu item with the following: |
||||
| title | SelectMe | |
||||
| name | SelectMe | |
||||
Given the project "Awesome Project" has a wiki menu item with the following: |
||||
| title | AwesomePage | |
||||
| name | RichtigGeil | |
||||
When I go to the wiki page "Wiki" for the project called "Awesome Project" |
||||
When I click on "More functions" |
||||
And I click on "Configure menu item" |
||||
And I choose "Show as submenu item of" |
||||
When I select "SelectMe" from "parent_wiki_menu_item" |
||||
When I select "RichtigGeil" from "parent_wiki_menu_item" |
||||
And I press "Save" |
||||
When I go to the wiki page "Wiki" for the project called "Awesome Project" |
||||
Then I should see "Wiki" within ".menu-children" |
||||
|
||||
@javascript |
||||
Scenario: Removing a menu item |
||||
Given the project "Awesome Project" has a wiki menu item with the following: |
||||
| title | DontKillMe | |
||||
| name | DontKillMe | |
||||
When I go to the wiki page "Wiki" for the project called "Awesome Project" |
||||
When I click on "More functions" |
||||
And I click on "Configure menu item" |
||||
And I choose "Do not show this wikipage in project navigation" |
||||
And I press "Save" |
||||
Then I should not see "Wiki" within "#main-menu" |
@ -0,0 +1,11 @@ |
||||
class InstanceFinder |
||||
def self.register(model, method) |
||||
@model_method_map ||= {} |
||||
|
||||
@model_method_map[model] = method |
||||
end |
||||
|
||||
def self.find(model, identifier) |
||||
instance = @model_method_map[model].call(identifier) |
||||
end |
||||
end |
@ -0,0 +1,42 @@ |
||||
require 'active_support/core_ext/class/attribute_accessors' |
||||
|
||||
module Redmine |
||||
module Themes |
||||
class Theme < Struct.new(:name) |
||||
cattr_accessor :default_theme, instance_reader: false |
||||
|
||||
def self.from(theme) |
||||
theme.kind_of?(Theme) ? theme : new(theme) |
||||
end |
||||
|
||||
def self.default |
||||
self.default_theme ||= new default_theme_name |
||||
end |
||||
|
||||
def favicon_path |
||||
@favicon_path ||= default? ? '/favicon.ico' : "/#{name}/favicon.ico" |
||||
end |
||||
|
||||
def main_stylesheet_path |
||||
name.to_s |
||||
end |
||||
|
||||
def default? |
||||
self === self.class.default |
||||
end |
||||
|
||||
def <=>(other) |
||||
name.to_s <=> other.name.to_s |
||||
end |
||||
include Comparable |
||||
|
||||
def self.default_theme_name |
||||
:default |
||||
end |
||||
|
||||
def self.forget_default_theme |
||||
self.default_theme = nil |
||||
end |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,25 @@ |
||||
class ScenarioDisabler |
||||
def self.empty_if_disabled(scenario) |
||||
if self.disabled?(scenario) |
||||
step_collection = scenario.instance_variable_get(:@steps) |
||||
step_collection.instance_variable_set(:@steps, []) |
||||
|
||||
true |
||||
else |
||||
false |
||||
end |
||||
end |
||||
|
||||
def self.disable(options) |
||||
@disabled_scenarios ||= [] |
||||
|
||||
@disabled_scenarios << options |
||||
end |
||||
|
||||
def self.disabled?(scenario) |
||||
@disabled_scenarios.present? && @disabled_scenarios.any? do |disabled_scenario| |
||||
disabled_scenario[:feature] == scenario.feature.name && disabled_scenario[:scenario] == scenario.name |
||||
end |
||||
end |
||||
|
||||
end |
@ -0,0 +1,82 @@ |
||||
require 'spec_helper' |
||||
|
||||
describe CustomFieldsController do |
||||
let(:custom_field) { FactoryGirl.stub(:custom_field) } |
||||
|
||||
before do |
||||
@controller.stub!(:authorize) |
||||
@controller.stub!(:check_if_login_required) |
||||
@controller.stub!(:require_admin) |
||||
end |
||||
|
||||
describe "POST edit" do |
||||
before do |
||||
Setting.available_languages = ["de", "en"] |
||||
CustomField.stub!(:find).and_return(custom_field) |
||||
end |
||||
|
||||
describe "WITH all ok params" do |
||||
let(:de_name) { "Ticket Feld" } |
||||
let(:en_name) { "Issue Field" } |
||||
let(:params) { { "custom_field" => { "translations_attributes" => { "0" => { "name" => de_name, "locale" => "de" }, "1" => { "name" => en_name, "locale" => "en" } } } } } |
||||
|
||||
before do |
||||
post :edit, params |
||||
end |
||||
|
||||
it { response.should be_success } |
||||
it { custom_field.name(:de).should == de_name } |
||||
it { custom_field.name(:en).should == en_name } |
||||
end |
||||
|
||||
describe "WITH one empty name params" do |
||||
let(:en_name) { "Issue Field" } |
||||
let(:de_name) { "" } |
||||
let(:params) { { "custom_field" => { "translations_attributes" => { "0" => { "name" => de_name, "locale" => "de" }, "1" => { "name" => en_name, "locale" => "en" } } } } } |
||||
|
||||
before do |
||||
post :edit, params |
||||
end |
||||
|
||||
it { response.should be_success } |
||||
it { custom_field.name(:de).should == en_name } |
||||
it { custom_field.name(:en).should == en_name } |
||||
end |
||||
end |
||||
|
||||
describe "POST new" do |
||||
before do |
||||
Setting.available_languages = ["de", "en"] |
||||
end |
||||
|
||||
describe "WITH all ok params" do |
||||
let(:de_name) { "Ticket Feld" } |
||||
let(:en_name) { "Issue Field" } |
||||
let(:params) { { "type" => "IssueCustomField", |
||||
"custom_field" => { "translations_attributes" => { "0" => { "name" => de_name, "locale" => "de" }, "1" => { "name" => en_name, "locale" => "en" } } } } } |
||||
|
||||
before do |
||||
post :new, params |
||||
end |
||||
|
||||
it { response.should be_success } |
||||
it { assigns(:custom_field).name(:de).should == de_name } |
||||
it { assigns(:custom_field).name(:en).should == en_name } |
||||
end |
||||
|
||||
describe "WITH one empty name params" do |
||||
let(:en_name) { "Issue Field" } |
||||
let(:de_name) { "" } |
||||
let(:params) { { "type" => "IssueCustomField", |
||||
"custom_field" => { "translations_attributes" => { "0" => { "name" => de_name, "locale" => "de" }, "1" => { "name" => en_name, "locale" => "en" } } } } } |
||||
|
||||
before do |
||||
post :new, params |
||||
end |
||||
|
||||
it { response.should be_success } |
||||
it { assigns(:custom_field).name(:de).should == en_name } |
||||
it { assigns(:custom_field).name(:en).should == en_name } |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,94 @@ |
||||
require 'spec_helper' |
||||
|
||||
describe ProjectsController do |
||||
before :each do |
||||
@controller.stub!(:set_localization) |
||||
|
||||
@role = FactoryGirl.create(:non_member) |
||||
@user = FactoryGirl.create(:admin) |
||||
User.stub!(:current).and_return @user |
||||
|
||||
@params = {} |
||||
end |
||||
|
||||
describe 'show' do |
||||
integrate_views |
||||
|
||||
describe 'without wiki' do |
||||
before do |
||||
@project = FactoryGirl.create(:project) |
||||
@project.reload # project contains wiki by default |
||||
@project.wiki.destroy |
||||
@project.reload |
||||
@params[:id] = @project.id |
||||
end |
||||
|
||||
it 'renders show' do |
||||
get 'show', @params |
||||
response.should be_success |
||||
response.should render_template 'show' |
||||
end |
||||
|
||||
it 'renders main menu without wiki menu item' do |
||||
get 'show', @params |
||||
|
||||
response.should have_tag('#main-menu') do |
||||
without_tag 'a.Wiki' |
||||
end |
||||
end |
||||
end |
||||
|
||||
describe 'with wiki' do |
||||
before do |
||||
@project = FactoryGirl.create(:project) |
||||
@project.reload # project contains wiki by default |
||||
@params[:id] = @project.id |
||||
end |
||||
|
||||
describe 'without custom wiki menu items' do |
||||
it 'renders show' do |
||||
get 'show', @params |
||||
response.should be_success |
||||
response.should render_template 'show' |
||||
end |
||||
|
||||
it 'renders main menu with wiki menu item' do |
||||
get 'show', @params |
||||
|
||||
response.should have_tag('#main-menu') do |
||||
with_tag 'a.Wiki', :content => 'Wiki' |
||||
end |
||||
end |
||||
end |
||||
|
||||
describe 'with custom wiki menu item' do |
||||
before do |
||||
main_item = FactoryGirl.create(:wiki_menu_item, :wiki_id => @project.wiki.id, :name => 'Example', :title => 'Example') |
||||
sub_item = FactoryGirl.create(:wiki_menu_item, :wiki_id => @project.wiki.id, :name => 'Sub', :title => 'Sub', :parent_id => main_item.id) |
||||
end |
||||
|
||||
it 'renders show' do |
||||
get 'show', @params |
||||
response.should be_success |
||||
response.should render_template 'show' |
||||
end |
||||
|
||||
it 'renders main menu with wiki menu item' do |
||||
get 'show', @params |
||||
|
||||
response.should have_tag('#main-menu') do |
||||
with_tag 'a.Example', :content => 'Example' |
||||
end |
||||
end |
||||
|
||||
it 'renders main menu with sub wiki menu item' do |
||||
get 'show', @params |
||||
|
||||
response.should have_tag('#main-menu') do |
||||
with_tag 'a.Sub', :content => 'Sub' |
||||
end |
||||
end |
||||
end |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,187 @@ |
||||
require 'spec_helper' |
||||
|
||||
describe UsersController do |
||||
let(:user) { FactoryGirl.create(:user) } |
||||
let(:admin) { FactoryGirl.create(:admin) } |
||||
|
||||
describe :routes do |
||||
describe "users" do |
||||
it { params_from(:get, "/users/1/deletion_info").should == { :controller => 'users', |
||||
:action => 'deletion_info', |
||||
:id => "1" } } |
||||
it { params_from(:delete, "/users/1").should == { :controller => 'users', |
||||
:action => 'destroy', |
||||
:id => "1" } } |
||||
end |
||||
|
||||
describe "my" do |
||||
before do |
||||
User.stub!(:current).and_return(user) |
||||
end |
||||
|
||||
it { params_from(:get, "/my/deletion_info").should == { :controller => 'users', |
||||
:action => 'deletion_info' } } |
||||
end |
||||
end |
||||
|
||||
describe "GET deletion_info" do |
||||
|
||||
describe "WHEN the current user is the requested user |
||||
WHEN the setting users_deletable_by_self is set to true" do |
||||
let(:params) { { "id" => user.id.to_s } } |
||||
|
||||
before do |
||||
@controller.stub!(:find_current_user).and_return(user) |
||||
Setting.stub!(:users_deletable_by_self?).and_return(true) |
||||
|
||||
get :deletion_info, params |
||||
end |
||||
|
||||
it { response.should be_success } |
||||
it { assigns(:user).should == user } |
||||
it { response.should render_template("deletion_info") } |
||||
end |
||||
|
||||
describe "WHEN the current user is the requested user |
||||
WHEN the setting users_deletable_by_self is set to false" do |
||||
let(:params) { { "id" => user.id.to_s } } |
||||
|
||||
before do |
||||
@controller.stub!(:find_current_user).and_return(user) |
||||
Setting.stub!(:users_deletable_by_self?).and_return(false) |
||||
|
||||
get :deletion_info, params |
||||
end |
||||
|
||||
it { response.response_code.should == 404 } |
||||
end |
||||
|
||||
describe "WHEN the current user is the anonymous user" do |
||||
let(:params) { { "id" => User.anonymous.id.to_s } } |
||||
|
||||
before do |
||||
@controller.stub!(:find_current_user).and_return(User.anonymous) |
||||
|
||||
get :deletion_info, params |
||||
end |
||||
|
||||
it { response.should redirect_to({ :controller => 'account', |
||||
:action => 'login', |
||||
:back_url => @controller.url_for({ :controller => 'users', |
||||
:action => 'deletion_info' }) }) } |
||||
end |
||||
|
||||
describe "WHEN the current user is admin |
||||
WHEN the setting users_deletable_by_admins is set to true" do |
||||
let(:admin) { FactoryGirl.create(:admin) } |
||||
let(:params) { { "id" => user.id.to_s } } |
||||
|
||||
before do |
||||
@controller.stub!(:find_current_user).and_return(admin) |
||||
Setting.stub!(:users_deletable_by_admins?).and_return(true) |
||||
|
||||
get :deletion_info, params |
||||
end |
||||
|
||||
it { response.should be_success } |
||||
it { assigns(:user).should == user } |
||||
it { response.should render_template("deletion_info") } |
||||
end |
||||
|
||||
describe "WHEN the current user is admin |
||||
WHEN the setting users_deletable_by_admins is set to false" do |
||||
let(:admin) { FactoryGirl.create(:admin) } |
||||
let(:params) { { "id" => user.id.to_s } } |
||||
|
||||
before do |
||||
@controller.stub!(:find_current_user).and_return(admin) |
||||
Setting.stub!(:users_deletable_by_admins?).and_return(false) |
||||
|
||||
get :deletion_info, params |
||||
end |
||||
|
||||
it { response.response_code.should == 404 } |
||||
end |
||||
end |
||||
|
||||
describe "POST destroy" do |
||||
describe "WHEN the current user is the requested one |
||||
WHEN the setting users_deletable_by_self is set to true" do |
||||
let(:params) { { "id" => user.id.to_s } } |
||||
|
||||
before do |
||||
@controller.instance_eval{ flash.stub!(:sweep) } |
||||
@controller.stub!(:find_current_user).and_return(user) |
||||
Setting.stub!(:users_deletable_by_self?).and_return(true) |
||||
|
||||
post :destroy, params |
||||
end |
||||
|
||||
it { response.should redirect_to({ :controller => 'account', :action => 'login' }) } |
||||
it { flash[:notice].should == I18n.t('account.deleted') } |
||||
end |
||||
|
||||
describe "WHEN the current user is the requested one |
||||
WHEN the setting users_deletable_by_self is set to false" do |
||||
let(:params) { { "id" => user.id.to_s } } |
||||
|
||||
before do |
||||
@controller.instance_eval{ flash.stub!(:sweep) } |
||||
@controller.stub!(:find_current_user).and_return(user) |
||||
Setting.stub!(:users_deletable_by_self?).and_return(false) |
||||
|
||||
post :destroy, params |
||||
end |
||||
|
||||
it { response.response_code.should == 404 } |
||||
end |
||||
|
||||
describe "WHEN the current user is the anonymous user |
||||
EVEN when the setting login_required is set to false" do |
||||
let(:params) { { "id" => User.anonymous.id.to_s } } |
||||
|
||||
before do |
||||
@controller.stub!(:find_current_user).and_return(User.anonymous) |
||||
Setting.stub!(:login_required?).and_return(false) |
||||
|
||||
post :destroy, params |
||||
end |
||||
|
||||
# redirecting post is not possible for now |
||||
it { response.response_code.should == 403 } |
||||
end |
||||
|
||||
describe "WHEN the current user is the admin |
||||
WHEN the setting users_deletable_by_admins is set to true" do |
||||
let(:admin) { FactoryGirl.create(:admin) } |
||||
let(:params) { { "id" => user.id.to_s } } |
||||
|
||||
before do |
||||
@controller.instance_eval{ flash.stub!(:sweep) } |
||||
@controller.stub!(:find_current_user).and_return(admin) |
||||
Setting.stub!(:users_deletable_by_admins?).and_return(true) |
||||
|
||||
post :destroy, params |
||||
end |
||||
|
||||
it { response.should redirect_to({ :controller => 'users', :action => 'index' }) } |
||||
it { flash[:notice].should == I18n.t('account.deleted') } |
||||
end |
||||
|
||||
describe "WHEN the current user is the admin |
||||
WHEN the setting users_deletable_by_admins is set to false" do |
||||
let(:admin) { FactoryGirl.create(:admin) } |
||||
let(:params) { { "id" => user.id.to_s } } |
||||
|
||||
before do |
||||
@controller.instance_eval{ flash.stub!(:sweep) } |
||||
@controller.stub!(:find_current_user).and_return(admin) |
||||
Setting.stub!(:users_deletable_by_admins).and_return(false) |
||||
|
||||
post :destroy, params |
||||
end |
||||
|
||||
it { response.response_code.should == 404 } |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,563 @@ |
||||
require 'spec_helper' |
||||
|
||||
describe WikiController do |
||||
describe 'routes' do |
||||
it 'should connect GET /projects/:project_id/wiki/new to wiki/new' do |
||||
params_from(:get, '/projects/abc/wiki/new').should == { |
||||
:controller => 'wiki', |
||||
:action => 'new', |
||||
:project_id => 'abc' |
||||
} |
||||
end |
||||
|
||||
it 'should connect GET /projects/:project_id/wiki/:id/new to wiki/new_child' do |
||||
params_from(:get, '/projects/abc/wiki/def/new').should == { |
||||
:controller => 'wiki', |
||||
:action => 'new_child', |
||||
:project_id => 'abc', |
||||
:id => 'def' |
||||
} |
||||
end |
||||
|
||||
it 'should connect POST /projects/:project_id/wiki/new to wiki/create' do |
||||
params_from(:post, '/projects/abc/wiki/new').should == { |
||||
:controller => 'wiki', |
||||
:action => 'create', |
||||
:project_id => 'abc' |
||||
} |
||||
end |
||||
|
||||
it 'should contect POST /projects/:project_id/wiki/:id/preview to wiki/preview' do |
||||
params_from(:post, '/projects/abc/wiki/def/preview').should == { |
||||
:controller => 'wiki', |
||||
:action => 'preview', |
||||
:project_id => 'abc', |
||||
:id => 'def' |
||||
} |
||||
end |
||||
|
||||
it 'should contect POST /projects/:project_id/wiki/preview to wiki/preview' do |
||||
params_from(:post, '/projects/abc/wiki/preview').should == { |
||||
:controller => 'wiki', |
||||
:action => 'preview', |
||||
:project_id => 'abc' |
||||
} |
||||
end |
||||
end |
||||
|
||||
describe 'actions' do |
||||
before do |
||||
@controller.stub!(:set_localization) |
||||
|
||||
@role = FactoryGirl.create(:non_member) |
||||
@user = FactoryGirl.create(:admin) |
||||
|
||||
User.stub!(:current).and_return @user |
||||
|
||||
@project = FactoryGirl.create(:project) |
||||
@project.reload # to get the wiki into the proxy |
||||
|
||||
|
||||
|
||||
# creating pages |
||||
@existing_page = FactoryGirl.create(:wiki_page, :wiki_id => @project.wiki.id, |
||||
:title => 'ExisitingPage') |
||||
|
||||
# creating page contents |
||||
FactoryGirl.create(:wiki_content, :page_id => @existing_page.id, |
||||
:author_id => @user.id) |
||||
end |
||||
|
||||
shared_examples_for "a 'new' action" do |
||||
it 'assigns @project to the current project' do |
||||
get_page |
||||
|
||||
assigns[:project].should == @project |
||||
end |
||||
|
||||
it 'assigns @page to a newly created wiki page' do |
||||
get_page |
||||
|
||||
assigns[:page].should be_new_record |
||||
assigns[:page].should be_kind_of WikiPage |
||||
assigns[:page].wiki.should == @project.wiki |
||||
end |
||||
|
||||
it 'assigns @content to a newly created wiki content' do |
||||
get_page |
||||
|
||||
assigns[:content].should be_new_record |
||||
assigns[:content].should be_kind_of WikiContent |
||||
assigns[:content].page.should == assigns[:page] |
||||
end |
||||
|
||||
it 'renders the new action' do |
||||
get_page |
||||
|
||||
response.should render_template 'new' |
||||
end |
||||
end |
||||
|
||||
describe 'new' do |
||||
let(:get_page) { get 'new', :project_id => @project } |
||||
|
||||
it_should_behave_like "a 'new' action" |
||||
end |
||||
|
||||
describe 'new_child' do |
||||
let(:get_page) { get 'new_child', :project_id => @project, :id => @existing_page.title } |
||||
|
||||
it_should_behave_like "a 'new' action" |
||||
|
||||
it 'sets the parent page for the new page' do |
||||
get_page |
||||
|
||||
assigns[:page].parent.should == @existing_page |
||||
end |
||||
|
||||
it 'renders 404 if used with an unknown page title' do |
||||
get 'new_child', :project_id => @project, :id => "foobar" |
||||
|
||||
response.status.should == "404 Not Found" |
||||
end |
||||
end |
||||
|
||||
describe 'create' do |
||||
describe 'successful action' do |
||||
it 'redirects to the show action' do |
||||
post 'create', |
||||
:project_id => @project, |
||||
:page => {:title => "abc"}, |
||||
:content => {:text => "h1. abc"} |
||||
|
||||
response.should redirect_to :action => 'show', :project_id => @project, :id => 'Abc' |
||||
end |
||||
|
||||
it 'saves a new WikiPage with proper content' do |
||||
post 'create', |
||||
:project_id => @project, |
||||
:page => {:title => "abc"}, |
||||
:content => {:text => "h1. abc"} |
||||
|
||||
page = @project.wiki.pages.find_by_title 'Abc' |
||||
page.should_not be_nil |
||||
page.title.should == 'Abc' |
||||
page.content.text.should == 'h1. abc' |
||||
end |
||||
end |
||||
|
||||
describe 'unsuccessful action' do |
||||
it 'renders "wiki/new"' do |
||||
post 'create', |
||||
:project_id => @project, |
||||
:page => {:title => ""}, |
||||
:content => {:text => "h1. abc"} |
||||
|
||||
response.should render_template('new') |
||||
end |
||||
|
||||
it 'assigns project to work with new template' do |
||||
post 'create', |
||||
:project_id => @project, |
||||
:page => {:title => ""}, |
||||
:content => {:text => "h1. abc"} |
||||
|
||||
assigns[:project].should == @project |
||||
end |
||||
|
||||
it 'assigns wiki to work with new template' do |
||||
post 'create', |
||||
:project_id => @project, |
||||
:page => {:title => ""}, |
||||
:content => {:text => "h1. abc"} |
||||
|
||||
assigns[:wiki].should == @project.wiki |
||||
assigns[:wiki].should_not be_new_record |
||||
end |
||||
|
||||
it 'assigns page to work with new template' do |
||||
post 'create', |
||||
:project_id => @project, |
||||
:page => {:title => ""}, |
||||
:content => {:text => "h1. abc"} |
||||
|
||||
assigns[:page].should be_new_record |
||||
assigns[:page].wiki.project.should == @project |
||||
assigns[:page].title.should == "" |
||||
assigns[:page].should_not be_valid |
||||
end |
||||
|
||||
it 'assigns content to work with new template' do |
||||
post 'create', |
||||
:project_id => @project, |
||||
:page => {:title => ""}, |
||||
:content => {:text => "h1. abc"} |
||||
|
||||
assigns[:content].should be_new_record |
||||
assigns[:content].page.wiki.project.should == @project |
||||
assigns[:content].text.should == 'h1. abc' |
||||
end |
||||
end |
||||
end |
||||
end |
||||
|
||||
describe 'view related stuff' do |
||||
integrate_views |
||||
|
||||
before :each do |
||||
@controller.stub!(:set_localization) |
||||
Setting.stub!(:login_required?).and_return(false) |
||||
|
||||
@role = FactoryGirl.create(:non_member) |
||||
@user = FactoryGirl.create(:admin) |
||||
|
||||
|
||||
@anon = User.anonymous.nil? ? FactoryGirl.create(:anonymous) : User.anonymous |
||||
|
||||
Role.anonymous.update_attributes :name => I18n.t(:default_role_anonymous), |
||||
:permissions => [:view_wiki_pages] |
||||
|
||||
User.stub!(:current).and_return @user |
||||
|
||||
@project = FactoryGirl.create(:project) |
||||
@project.reload # to get the wiki into the proxy |
||||
|
||||
# creating pages |
||||
@page_default = FactoryGirl.create(:wiki_page, :wiki_id => @project.wiki.id, |
||||
:title => 'Wiki') |
||||
@page_with_content = FactoryGirl.create(:wiki_page, :wiki_id => @project.wiki.id, |
||||
:title => 'PagewithContent') |
||||
@page_without_content = FactoryGirl.create(:wiki_page, :wiki_id => @project.wiki.id, |
||||
:title => 'PagewithoutContent') |
||||
@unrelated_page = FactoryGirl.create(:wiki_page, :wiki_id => @project.wiki.id, |
||||
:title => 'UnrelatedPage') |
||||
|
||||
# creating page contents |
||||
FactoryGirl.create(:wiki_content, :page_id => @page_default.id, |
||||
:author_id => @user.id) |
||||
FactoryGirl.create(:wiki_content, :page_id => @page_with_content.id, |
||||
:author_id => @user.id) |
||||
FactoryGirl.create(:wiki_content, :page_id => @unrelated_page.id, |
||||
:author_id => @user.id) |
||||
|
||||
# creating some child pages |
||||
@children = {} |
||||
[@page_with_content].each do |page| |
||||
child_page = FactoryGirl.create(:wiki_page, :wiki_id => @project.wiki.id, |
||||
:parent_id => page.id, |
||||
:title => page.title + " child") |
||||
FactoryGirl.create(:wiki_content, :page_id => child_page.id, |
||||
:author_id => @user.id) |
||||
|
||||
@children[page] = child_page |
||||
end |
||||
end |
||||
|
||||
describe '- main menu links' do |
||||
before do |
||||
@main_menu_item_for_page_with_content = FactoryGirl.create(:wiki_menu_item, :wiki_id => @project.wiki.id, |
||||
:name => 'Item for Page with Content', |
||||
:title => @page_with_content.title) |
||||
|
||||
@main_menu_item_for_new_wiki_page = FactoryGirl.create(:wiki_menu_item, :wiki_id => @project.wiki.id, |
||||
:name => 'Item for new WikiPage', |
||||
:title => 'NewWikiPage') |
||||
|
||||
@other_menu_item = FactoryGirl.create(:wiki_menu_item, :wiki_id => @project.wiki.id, |
||||
:name => 'Item for other page', |
||||
:title => @unrelated_page.title) |
||||
|
||||
end |
||||
|
||||
shared_examples_for 'all wiki menu items' do |
||||
it "is inactive, when an unrelated page is shown" do |
||||
get 'show', :id => @unrelated_page.title, :project_id => @project.id |
||||
|
||||
response.should be_success |
||||
response.should have_exactly_one_selected_menu_item_in(:project_menu) |
||||
|
||||
response.should have_tag('#main-menu') do |
||||
with_tag "a.#{@wiki_menu_item.item_class}" |
||||
without_tag "a.#{@wiki_menu_item.item_class}.selected" |
||||
end |
||||
end |
||||
|
||||
it "is inactive, when another wiki menu item's page is shown" do |
||||
get 'show', :id => @other_wiki_menu_item.title, :project_id => @project.id |
||||
|
||||
response.should be_success |
||||
response.should have_exactly_one_selected_menu_item_in(:project_menu) |
||||
|
||||
response.should have_tag('#main-menu') do |
||||
with_tag "a.#{@wiki_menu_item.item_class}" |
||||
without_tag "a.#{@wiki_menu_item.item_class}.selected" |
||||
end |
||||
end |
||||
|
||||
it 'is active, when the given wiki menu item is shown' do |
||||
get 'show', :id => @wiki_menu_item.title, :project_id => @project.id |
||||
|
||||
response.should be_success |
||||
response.should have_exactly_one_selected_menu_item_in(:project_menu) |
||||
|
||||
response.should have_tag('#main-menu') do |
||||
with_tag "a.#{@wiki_menu_item.item_class}.selected" |
||||
end |
||||
end |
||||
end |
||||
|
||||
|
||||
shared_examples_for 'all existing wiki menu items' do |
||||
#TODO: Add tests for new and toc options within menu item |
||||
it "is active on parents item, when new page is shown" do |
||||
get 'new_child', :id => @wiki_menu_item.title, :project_id => @project.identifier |
||||
|
||||
response.should be_success |
||||
response.should have_no_selected_menu_item_in(:project_menu) |
||||
|
||||
response.should have_tag '#main-menu' do |
||||
with_tag "a.#{@wiki_menu_item.item_class}" |
||||
without_tag "a.#{@wiki_menu_item.item_class}.selected" |
||||
end |
||||
end |
||||
|
||||
it 'is inactive, when a toc page is shown' do |
||||
get 'index', :id => @wiki_menu_item.title, :project_id => @project.id |
||||
|
||||
response.should be_success |
||||
response.should have_no_selected_menu_item_in(:project_menu) |
||||
|
||||
response.should have_tag('#main-menu') do |
||||
with_tag "a.#{@wiki_menu_item.item_class}" |
||||
without_tag "a.#{@wiki_menu_item.item_class}.selected" |
||||
end |
||||
end |
||||
end |
||||
|
||||
shared_examples_for 'all wiki menu items with child pages' do |
||||
it 'is active, when the given wiki menu item is an ancestor of the shown page' do |
||||
get 'show', :id => @child_page.title, :project_id => @project.id |
||||
|
||||
response.should be_success |
||||
response.should have_exactly_one_selected_menu_item_in(:project_menu) |
||||
|
||||
response.should have_tag('#main-menu') do |
||||
with_tag "a.#{@wiki_menu_item.item_class}.selected" |
||||
end |
||||
end |
||||
end |
||||
|
||||
|
||||
describe '- wiki menu item pointing to a saved wiki page' do |
||||
before do |
||||
@wiki_menu_item = @main_menu_item_for_page_with_content |
||||
@other_wiki_menu_item = @other_menu_item |
||||
@child_page = @children[@page_with_content] |
||||
end |
||||
|
||||
it_should_behave_like 'all wiki menu items' |
||||
it_should_behave_like 'all existing wiki menu items' |
||||
it_should_behave_like 'all wiki menu items with child pages' |
||||
end |
||||
|
||||
describe '- wiki menu item pointing to a new wiki page' do |
||||
before do |
||||
@wiki_menu_item = @main_menu_item_for_new_wiki_page |
||||
@other_wiki_menu_item = @other_menu_item |
||||
end |
||||
|
||||
it_should_behave_like 'all wiki menu items' |
||||
end |
||||
|
||||
describe '- wiki_menu_item containing special chars only' do |
||||
before do |
||||
@wiki_menu_item = FactoryGirl.create(:wiki_menu_item, :wiki_id => @project.wiki.id, |
||||
:name => '?', |
||||
:title => 'Help') |
||||
@other_wiki_menu_item = @other_menu_item |
||||
end |
||||
|
||||
it_should_behave_like 'all wiki menu items' |
||||
end |
||||
end |
||||
|
||||
describe '- wiki sidebar' do |
||||
include ActionView::Helpers |
||||
|
||||
describe 'configure menu items link' do |
||||
describe 'on a show page' do |
||||
describe "being authorized to configure menu items" do |
||||
it 'is visible' do |
||||
get 'show', :project_id => @project.id |
||||
|
||||
response.should be_success |
||||
|
||||
response.should have_tag '#content' do |
||||
with_tag "a", "Configure menu item" |
||||
end |
||||
end |
||||
end |
||||
|
||||
describe "being unauthorized to configure menu items" do |
||||
before do |
||||
User.stub!(:current).and_return @anon |
||||
end |
||||
|
||||
it 'is invisible' do |
||||
get 'show', :project_id => @project.id |
||||
|
||||
response.should be_success |
||||
|
||||
response.should have_tag '#content' do |
||||
without_tag "a", "Configure menu item" |
||||
end |
||||
end |
||||
end |
||||
end |
||||
end |
||||
|
||||
describe 'new child page link' do |
||||
describe 'on an index page' do |
||||
describe "being authorized to edit wiki pages" do |
||||
it 'is invisible' do |
||||
get 'index', :project_id => @project.id |
||||
|
||||
response.should be_success |
||||
|
||||
response.should have_tag '#content' do |
||||
without_tag "a", "Create new child page" |
||||
end |
||||
end |
||||
end |
||||
|
||||
describe "being unauthorized to edit wiki pages" do |
||||
before do |
||||
User.stub!(:current).and_return @anon |
||||
end |
||||
|
||||
it 'is invisible' do |
||||
get 'index', :project_id => @project.id |
||||
|
||||
response.should be_success |
||||
|
||||
response.should have_tag '#content' do |
||||
without_tag "a", "Create new child page" |
||||
end |
||||
end |
||||
end |
||||
end |
||||
|
||||
describe 'on a wiki page' do |
||||
describe "being authorized to edit wiki pages" do |
||||
describe "with a wiki page present" do |
||||
it "is visible" do |
||||
get 'show', :id => @page_with_content.title, :project_id => @project.identifier |
||||
|
||||
response.should be_success |
||||
|
||||
response.should have_tag '#content' do |
||||
with_tag "a[href=#{wiki_new_child_path(:project_id => @project, :id => @page_with_content.title)}]", |
||||
"Create new child page" |
||||
end |
||||
end |
||||
end |
||||
|
||||
|
||||
describe "with no wiki page present" do |
||||
it 'is invisible' do |
||||
get 'show', :id => 'i-am-a-ghostpage', :project_id => @project.identifier |
||||
|
||||
response.should be_success |
||||
|
||||
response.should have_tag '#content' do |
||||
without_tag "a[href=#{wiki_new_child_path(:project_id => @project, :id => 'i-am-a-ghostpage')}]", |
||||
"Create new child page" |
||||
end |
||||
end |
||||
end |
||||
end |
||||
|
||||
describe "being unauthorized to edit wiki pages" do |
||||
before do |
||||
User.stub!(:current).and_return @anon |
||||
end |
||||
|
||||
it 'is invisible' do |
||||
get 'show', :id => @page_with_content.title, :project_id => @project.identifier |
||||
|
||||
response.should be_success |
||||
|
||||
response.should have_tag '#content' do |
||||
without_tag "a", "Create new child page" |
||||
end |
||||
end |
||||
end |
||||
end |
||||
end |
||||
|
||||
describe 'new page link' do |
||||
describe 'on an index page' do |
||||
describe "being authorized to edit wiki pages" do |
||||
it 'is visible' do |
||||
get 'index', :project_id => @project.id |
||||
|
||||
response.should be_success |
||||
|
||||
response.should have_tag '.menu_root' do |
||||
with_tag "a[href=#{wiki_new_child_path(:project_id => @project, :id => 'Wiki')}]", |
||||
"Create new child page" |
||||
end |
||||
end |
||||
end |
||||
|
||||
describe "being unauthorized to edit wiki pages" do |
||||
before do |
||||
User.stub!(:current).and_return @anon |
||||
end |
||||
|
||||
it 'is invisible' do |
||||
get 'index', :project_id => @project.id |
||||
|
||||
response.should be_success |
||||
|
||||
response.should have_tag '.menu_root' do |
||||
without_tag "a", "Create new child page" |
||||
end |
||||
end |
||||
end |
||||
end |
||||
|
||||
describe 'on a wiki page' do |
||||
describe "being authorized to edit wiki pages" do |
||||
it 'is visible' do |
||||
get 'show', :id => @page_with_content.title, :project_id => @project.identifier |
||||
|
||||
response.should be_success |
||||
|
||||
response.should have_tag '.menu_root' do |
||||
with_tag "a[href=#{wiki_new_child_path(:project_id => @project, :id => 'Wiki')}]", |
||||
"Create new child page" |
||||
end |
||||
end |
||||
end |
||||
|
||||
describe "being unauthorized to edit wiki pages" do |
||||
before do |
||||
User.stub!(:current).and_return @anon |
||||
end |
||||
|
||||
it 'is invisible' do |
||||
get 'show', :id => @page_with_content.title, :project_id => @project.identifier |
||||
|
||||
response.should be_success |
||||
|
||||
response.should have_tag '.menu_root' do |
||||
without_tag "a", "Create new child page" |
||||
end |
||||
end |
||||
end |
||||
end |
||||
end |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,42 @@ |
||||
require 'spec_helper' |
||||
|
||||
describe WikiMenuItemsController do |
||||
before do |
||||
User.destroy_all |
||||
Role.destroy_all |
||||
|
||||
@project = FactoryGirl.create(:project) |
||||
@project.reload # project contains wiki by default |
||||
|
||||
|
||||
@params = {} |
||||
@params[:project_id] = @project.id |
||||
page = FactoryGirl.create(:wiki_page, :wiki => @project.wiki) |
||||
@params[:id] = page.title |
||||
end |
||||
|
||||
describe 'w/ valid auth' do |
||||
it 'renders the edit action' do |
||||
admin_user = FactoryGirl.create(:admin) |
||||
|
||||
User.stub!(:current).and_return admin_user |
||||
permission_role = FactoryGirl.create(:role, :name => "accessgranted", :permissions => [:manage_wiki_menu]) |
||||
member = FactoryGirl.create(:member, :principal => admin_user, :user => admin_user, :project => @project, :roles => [permission_role]) |
||||
|
||||
get 'edit', @params |
||||
|
||||
response.should be_success |
||||
end |
||||
end |
||||
|
||||
describe 'w/o valid auth' do |
||||
|
||||
it 'be forbidden' do |
||||
User.stub!(:current).and_return FactoryGirl.create(:user) |
||||
|
||||
get 'edit', @params |
||||
|
||||
response.status.should == "403 Forbidden" |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,7 @@ |
||||
FactoryGirl.define do |
||||
factory :attachment do |
||||
container :factory => :document |
||||
sequence(:filename) { |n| "test#{n}.test" } |
||||
sequence(:disk_filename) { |n| "test#{n}.test" } |
||||
end |
||||
end |
@ -0,0 +1,8 @@ |
||||
FactoryGirl.define do |
||||
factory :board do |
||||
project |
||||
sequence(:name) { |n| "Board No. #{n}" } |
||||
sequence(:description) { |n| "I am the Board No. #{n}" } |
||||
end |
||||
end |
||||
|
@ -0,0 +1,8 @@ |
||||
FactoryGirl.define do |
||||
factory :changeset do |
||||
sequence(:revision) { |n| "#{n}" } |
||||
committed_on Time.now |
||||
commit_date Date.today |
||||
end |
||||
end |
||||
|
@ -0,0 +1,6 @@ |
||||
FactoryGirl.define do |
||||
factory :document_category do |
||||
project |
||||
sequence(:name) { |n| "I am Category No. #{n}" } |
||||
end |
||||
end |
@ -0,0 +1,8 @@ |
||||
FactoryGirl.define do |
||||
factory :document do |
||||
project |
||||
category :factory => :document_category |
||||
sequence(:description) { |n| "I am a document's description No. #{n}" } |
||||
sequence(:title) { |n| "I am the document No. #{n}" } |
||||
end |
||||
end |
@ -0,0 +1,6 @@ |
||||
FactoryGirl.define do |
||||
factory :group do |
||||
# groups have lastnames? hmm... |
||||
sequence(:lastname) { |g| "Group #{g}" } |
||||
end |
||||
end |
@ -0,0 +1,8 @@ |
||||
FactoryGirl.define do |
||||
factory :issue_category do |
||||
project |
||||
assigned_to :factory => :user |
||||
sequence(:name) { |n| "Issue category #{n}" } |
||||
end |
||||
end |
||||
|
@ -0,0 +1,16 @@ |
||||
FactoryGirl.define do |
||||
factory :issue do |
||||
priority |
||||
sequence(:subject) { |n| "Issue No. #{n}" } |
||||
description { |i| "Description for '#{i.subject}'" } |
||||
tracker :factory => :tracker_feature |
||||
author :factory => :user |
||||
|
||||
factory :valid_issue do |
||||
after :build do |issue| |
||||
issue.project = Factory.build(:valid_project) |
||||
issue.tracker = issue.project.trackers.first |
||||
end |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,27 @@ |
||||
FactoryGirl.define do |
||||
factory :priority, :class => IssuePriority do |
||||
sequence(:name) { |i| "Priority #{i}" } |
||||
active true |
||||
|
||||
factory :priority_low do |
||||
name "Low" |
||||
end |
||||
|
||||
factory :priority_normal do |
||||
name "Normal" |
||||
end |
||||
|
||||
factory :priority_high do |
||||
name "High" |
||||
end |
||||
|
||||
factory :priority_urgent do |
||||
name "Urgent" |
||||
end |
||||
|
||||
factory :priority_immediate do |
||||
name "Immediate" |
||||
end |
||||
end |
||||
end |
||||
|
@ -0,0 +1,11 @@ |
||||
FactoryGirl.define do |
||||
factory :issue_status do |
||||
sequence(:name) { |n| "status #{n}" } |
||||
is_closed false |
||||
|
||||
factory :default_issue_status do |
||||
is_default true |
||||
end |
||||
end |
||||
end |
||||
|
@ -0,0 +1,18 @@ |
||||
# Create memberships like this: |
||||
# |
||||
# project = FactoryGirl.create(:project) |
||||
# user = FactoryGirl.create(:user) |
||||
# role = FactoryGirl.create(:role, :permissions => [:view_wiki_pages, :edit_wiki_pages]) |
||||
# |
||||
# member = FactoryGirl.create(:member, :user => user, :project => project) |
||||
# member.role_ids = [role.id] |
||||
# member.save! |
||||
# |
||||
# It looks like you cannot create member_role models directly. |
||||
|
||||
FactoryGirl.define do |
||||
factory :member do |
||||
user |
||||
project |
||||
end |
||||
end |
@ -0,0 +1,7 @@ |
||||
FactoryGirl.define do |
||||
factory :message do |
||||
board |
||||
sequence(:content) { |n| "Message content {n}" } |
||||
sequence(:subject) { |n| "Message subject {n}" } |
||||
end |
||||
end |
@ -0,0 +1,9 @@ |
||||
FactoryGirl.define do |
||||
factory :news do |
||||
sequence(:title) { |n| "News title#{n}" } |
||||
sequence(:summary) { |n| "News summary#{n}" } |
||||
sequence(:description) { |n| "News description#{n}" } |
||||
author :factory => :user |
||||
project |
||||
end |
||||
end |
@ -0,0 +1,26 @@ |
||||
FactoryGirl.define do |
||||
factory :project do |
||||
sequence(:name) { |n| "My Project No. #{n}" } |
||||
sequence(:identifier) { |n| "myproject_no_#{n}" } |
||||
enabled_module_names Redmine::AccessControl.available_project_modules |
||||
|
||||
factory :public_project do |
||||
is_public true |
||||
end |
||||
|
||||
factory :project_with_trackers do |
||||
after :build do |project| |
||||
project.trackers << FactoryGirl.build(:tracker) |
||||
end |
||||
after :create do |project| |
||||
project.trackers.each { |tracker| tracker.save! } |
||||
end |
||||
|
||||
factory :valid_project do |
||||
after :build do |project| |
||||
project.trackers << FactoryGirl.build(:tracker_with_workflow) |
||||
end |
||||
end |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,17 @@ |
||||
FactoryGirl.define do |
||||
factory :query do |
||||
project |
||||
user :factory => :user |
||||
sequence(:name) { |n| "Query {n}" } |
||||
|
||||
factory :public_query do |
||||
is_public true |
||||
sequence(:name) { |n| "Public query {n}" } |
||||
end |
||||
|
||||
factory :private_query do |
||||
is_public false |
||||
sequence(:name) { |n| "Private query {n}" } |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,7 @@ |
||||
FactoryGirl.define do |
||||
factory :repository, :class => Repository::Filesystem do |
||||
url 'file:///tmp/test_repo' |
||||
project |
||||
end |
||||
end |
||||
|
@ -0,0 +1,20 @@ |
||||
FactoryGirl.define do |
||||
factory :role do |
||||
permissions [] |
||||
sequence(:name) { |n| "role_#{n}"} |
||||
assignable true |
||||
|
||||
factory :non_member do |
||||
name "Non member" |
||||
builtin Role::BUILTIN_NON_MEMBER |
||||
assignable false |
||||
end |
||||
|
||||
factory :anonymous_role do |
||||
name "Anonymous" |
||||
builtin Role::BUILTIN_ANONYMOUS |
||||
assignable false |
||||
end |
||||
end |
||||
end |
||||
|
@ -0,0 +1,7 @@ |
||||
FactoryGirl.define do |
||||
factory :time_entry_activity do |
||||
sequence(:name) { |n| "Time Entry Activity No. #{n}" } |
||||
end |
||||
end |
||||
|
||||
|
@ -0,0 +1,9 @@ |
||||
FactoryGirl.define do |
||||
factory :time_entry do |
||||
project |
||||
user |
||||
issue |
||||
spent_on Date.today |
||||
end |
||||
end |
||||
|
@ -0,0 +1,35 @@ |
||||
FactoryGirl.define do |
||||
factory :tracker do |
||||
sequence(:position) { |p| p } |
||||
name { |a| "Tracker No. #{a.position}" } |
||||
end |
||||
|
||||
factory :tracker_bug, :class => Tracker do |
||||
name "Bug" |
||||
is_in_chlog true |
||||
position 1 |
||||
|
||||
factory :tracker_feature do |
||||
name "Feature" |
||||
position 2 |
||||
end |
||||
|
||||
factory :tracker_support do |
||||
name "Support" |
||||
position 3 |
||||
end |
||||
|
||||
factory :tracker_task do |
||||
name "Task" |
||||
position 4 |
||||
end |
||||
|
||||
factory :tracker_with_workflow do |
||||
sequence(:name) { |n| "Tracker #{n}" } |
||||
sequence(:position) { |n| n } |
||||
after :build do |t| |
||||
t.workflows = [FactoryGirl.build(:workflow_with_default_status)] |
||||
end |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,63 @@ |
||||
FactoryGirl.define do |
||||
factory :custom_field do |
||||
name "Custom Field" |
||||
regexp "" |
||||
is_required "0" |
||||
min_length "0" |
||||
default_value "" |
||||
max_length "0" |
||||
editable "1" |
||||
possible_values "" |
||||
visible "1" |
||||
field_format "bool" |
||||
|
||||
factory :user_custom_field do |
||||
sequence(:name) { |n| "User Custom Field #{n}" } |
||||
|
||||
factory :boolean_user_custom_field do |
||||
name "BooleanUserCustomField" |
||||
field_format "bool" |
||||
end |
||||
|
||||
factory :integer_user_custom_field do |
||||
name "IntegerUserCustomField" |
||||
field_format "int" |
||||
end |
||||
|
||||
factory :text_user_custom_field do |
||||
name "TextUserCustomField" |
||||
field_format "text" |
||||
end |
||||
|
||||
factory :string_user_custom_field do |
||||
name "StringUserCustomField" |
||||
field_format "string" |
||||
end |
||||
|
||||
factory :float_user_custom_field do |
||||
name "FloatUserCustomField" |
||||
field_format "float" |
||||
end |
||||
|
||||
factory :list_user_custom_field do |
||||
name "ListUserCustomField" |
||||
field_format "list" |
||||
possible_values ["1", "2", "3", "4", "5", "6", "7"] |
||||
end |
||||
|
||||
factory :date_user_custom_field do |
||||
name "DateUserCustomField" |
||||
field_format "date" |
||||
end |
||||
end |
||||
|
||||
factory :issue_custom_field do |
||||
sequence(:name) { |n| "Issue Custom Field #{n}" } |
||||
|
||||
factory :user_issue_custom_field do |
||||
field_format "user" |
||||
sequence(:name) { |n| "UserIssueCustomField #{n}" } |
||||
end |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,38 @@ |
||||
FactoryGirl.define do |
||||
factory :user do |
||||
firstname 'Bob' |
||||
lastname 'Bobbit' |
||||
sequence(:login) { |n| "bob#{n}" } |
||||
sequence(:mail) {|n| "bob#{n}.bobbit@bob.com" } |
||||
password 'admin' |
||||
password_confirmation 'admin' |
||||
|
||||
mail_notification(Redmine::VERSION::MAJOR > 0 ? 'all' : true) |
||||
|
||||
language 'en' |
||||
status User::STATUS_ACTIVE |
||||
admin false |
||||
first_login false if User.columns.map(&:name).include? 'first_login' |
||||
end |
||||
|
||||
factory :admin, :class => User do |
||||
firstname 'Redmine' |
||||
lastname 'Admin' |
||||
login 'admin' |
||||
password 'admin' |
||||
password_confirmation 'admin' |
||||
mail 'admin@example.com' |
||||
admin true |
||||
first_login false if User.columns.map(&:name).include? 'first_login' |
||||
end |
||||
|
||||
factory :anonymous, :class => AnonymousUser do |
||||
lastname "Anonymous" |
||||
firstname "" |
||||
status User::STATUS_BUILTIN |
||||
end |
||||
|
||||
factory :deleted_user do |
||||
status User::STATUS_BUILTIN |
||||
end |
||||
end |
@ -0,0 +1,7 @@ |
||||
FactoryGirl.define do |
||||
factory :version do |
||||
sequence(:name) { |i| "Version #{i}" } |
||||
effective_date Date.today + 14.days |
||||
project |
||||
end |
||||
end |
@ -0,0 +1,9 @@ |
||||
FactoryGirl.define do |
||||
factory :wiki_content do |
||||
page :factory => :wiki_page |
||||
author :factory => :user |
||||
|
||||
text { |a| "h1. #{a.page.title}\n\nPage Content Version #{a.version}." } |
||||
end |
||||
end |
||||
|
@ -0,0 +1,7 @@ |
||||
FactoryGirl.define do |
||||
factory :wiki do |
||||
start_page 'Wiki' |
||||
project |
||||
end |
||||
end |
||||
|
@ -0,0 +1,8 @@ |
||||
FactoryGirl.define do |
||||
factory :wiki_menu_item do |
||||
wiki |
||||
|
||||
sequence(:name) {|n| "Item No. #{n}" } |
||||
sequence(:title) {|n| "Wiki Title #{n}" } |
||||
end |
||||
end |
@ -0,0 +1,10 @@ |
||||
FactoryGirl.define do |
||||
factory :wiki_page do |
||||
wiki |
||||
sequence(:title) { |n| "Wiki Page No. #{n}" } |
||||
|
||||
factory :wiki_page_with_content do |
||||
content :factory => :wiki_content |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,8 @@ |
||||
FactoryGirl.define do |
||||
factory :wiki_redirect do |
||||
wiki |
||||
|
||||
title 'Source' |
||||
redirects_to 'Target' |
||||
end |
||||
end |
@ -0,0 +1,11 @@ |
||||
FactoryGirl.define do |
||||
factory :workflow do |
||||
old_status :factory => :issue_status |
||||
new_status :factory => :issue_status |
||||
role |
||||
|
||||
factory :workflow_with_default_status do |
||||
old_status :factory => :default_issue_status |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,92 @@ |
||||
require 'spec_helper' |
||||
|
||||
describe "Journalized Objects" do |
||||
before(:each) do |
||||
@tracker ||= FactoryGirl.create(:tracker_feature) |
||||
@project ||= FactoryGirl.create(:project_with_trackers) |
||||
@current = FactoryGirl.create(:user, :login => "user1", :mail => "user1@users.com") |
||||
User.stub!(:current).and_return(@current) |
||||
end |
||||
|
||||
|
||||
it 'should work with issues' do |
||||
@status_open ||= FactoryGirl.create(:issue_status, :name => "Open", :is_default => true) |
||||
@issue ||= FactoryGirl.create(:issue, :project => @project, :status => @status_open, :tracker => @tracker, :author => @current) |
||||
|
||||
|
||||
initial_journal = @issue.journals.first |
||||
recreated_journal = @issue.recreate_initial_journal! |
||||
|
||||
initial_journal.should be_identical(recreated_journal) |
||||
end |
||||
|
||||
it 'should work with news' do |
||||
@news ||= FactoryGirl.create(:news, :project => @project, :author => @current, :title => "Test", :summary => "Test", :description => "Test") |
||||
|
||||
initial_journal = @news.journals.first |
||||
recreated_journal = @news.recreate_initial_journal! |
||||
|
||||
initial_journal.should be_identical(recreated_journal) |
||||
end |
||||
|
||||
|
||||
it 'should work with wiki content' do |
||||
@wiki_content ||= FactoryGirl.create(:wiki_content, :author => @current) |
||||
|
||||
initial_journal = @wiki_content.journals.first |
||||
recreated_journal = @wiki_content.recreate_initial_journal! |
||||
|
||||
initial_journal.should be_identical(recreated_journal) |
||||
end |
||||
|
||||
it 'should work with messages' do |
||||
@message ||= FactoryGirl.create(:message, :content => "Test", :subject => "Test", :author => @current) |
||||
|
||||
initial_journal = @message.journals.first |
||||
recreated_journal = @message.recreate_initial_journal! |
||||
|
||||
initial_journal.should be_identical(recreated_journal) |
||||
end |
||||
|
||||
it 'should work with time entries' do |
||||
@status_open ||= FactoryGirl.create(:issue_status, :name => "Open", :is_default => true) |
||||
@issue ||= FactoryGirl.create(:issue, :project => @project, :status => @status_open, :tracker => @tracker, :author => @current) |
||||
|
||||
@time_entry ||= FactoryGirl.create(:time_entry, :issue => @issue, :project => @project, :spent_on => Time.now, :hours => 5, :user => @current, :activity => FactoryGirl.create(:time_entry_activity)) |
||||
|
||||
initial_journal = @time_entry.journals.first |
||||
recreated_journal = @time_entry.recreate_initial_journal! |
||||
|
||||
initial_journal.should be_identical(recreated_journal) |
||||
end |
||||
|
||||
it 'should work with documents' do |
||||
@document ||= FactoryGirl.create(:document) |
||||
|
||||
initial_journal = @document.journals.first |
||||
recreated_journal = @document.recreate_initial_journal! |
||||
|
||||
initial_journal.should be_identical(recreated_journal) |
||||
end |
||||
|
||||
it 'should work with attachments' do |
||||
@attachment ||= FactoryGirl.create(:attachment, :container => FactoryGirl.create(:document), :author => @current) |
||||
|
||||
initial_journal = @attachment.journals.first |
||||
recreated_journal = @attachment.recreate_initial_journal! |
||||
|
||||
initial_journal.should be_identical(recreated_journal) |
||||
end |
||||
|
||||
it 'should work with changesets' do |
||||
Setting.enabled_scm = ["Subversion"] |
||||
@repository ||= Repository.factory("Subversion", :url => "http://svn.test.com") |
||||
@repository.save! |
||||
@changeset ||= FactoryGirl.create(:changeset, :committer => @current.login, :repository => @repository) |
||||
|
||||
initial_journal = @changeset.journals.first |
||||
recreated_journal = @changeset.recreate_initial_journal! |
||||
|
||||
initial_journal.should be_identical(recreated_journal) |
||||
end |
||||
end |
@ -0,0 +1,107 @@ |
||||
require 'spec_helper' |
||||
|
||||
module Redmine |
||||
module Themes |
||||
describe Theme do |
||||
before do |
||||
Theme.forget_default_theme |
||||
end |
||||
|
||||
describe '#initialize' do |
||||
it 'stores the name' do |
||||
theme = Theme.new(:some_name) |
||||
expect(theme.name).to eq :some_name |
||||
end |
||||
end |
||||
|
||||
describe '#from' do |
||||
it 'returns self when given a theme' do |
||||
theme = Theme.new(:some_name) |
||||
expect(Theme.from(theme)).to eq theme |
||||
end |
||||
|
||||
it 'returns a new theme given a name' do |
||||
expected = Theme.new(:some_name) |
||||
|
||||
theme = Theme.from(:some_name) |
||||
expect(theme).to eq expected |
||||
end |
||||
end |
||||
|
||||
describe '#default' do |
||||
it 'returns the default theme' do |
||||
expect(Theme.default).to be_default |
||||
end |
||||
|
||||
it 'always returns the same theme' do |
||||
expect(Theme.default).to be Theme.default |
||||
end |
||||
end |
||||
|
||||
describe '#default?' do |
||||
it 'returns true if it really is the default theme' do |
||||
theme = Theme.new(:some_name) |
||||
Theme.stub(:default).and_return(theme) |
||||
expect(theme).to be_default |
||||
end |
||||
|
||||
it 'returns false if it is not the default theme' do |
||||
theme = Theme.new(:some_name) |
||||
expect(theme).to_not be_default |
||||
end |
||||
end |
||||
|
||||
describe '#default_theme_name' do |
||||
it 'has a default theme name of :default' do |
||||
expect(Theme.default.name).to eq :default |
||||
end |
||||
|
||||
it 'defines the name of the default theme' do |
||||
Theme.stub(:default_theme_name).and_return(:some_default_name) |
||||
expect(Theme.default.name).to eq :some_default_name |
||||
end |
||||
end |
||||
|
||||
describe '#forget_default_theme' do |
||||
it 'will clear the old default theme' do |
||||
theme = Theme.default |
||||
Theme.forget_default_theme |
||||
expect(Theme.default).to_not be theme |
||||
end |
||||
end |
||||
|
||||
describe '#main_stylesheet_path' do |
||||
it 'equals the name of the theme' do |
||||
theme = Theme.new(:some_name) |
||||
expect(theme.main_stylesheet_path).to eq 'some_name' |
||||
end |
||||
end |
||||
|
||||
describe '#favicon_path' do |
||||
it 'is on the root level for the default theme' do |
||||
theme = Theme.default |
||||
expect(theme.favicon_path).to eq '/favicon.ico' |
||||
end |
||||
|
||||
it "prepends the theme name unless it's the default theme" do |
||||
theme = Theme.new(:some_name) |
||||
expect(theme.favicon_path).to eq '/some_name/favicon.ico' |
||||
end |
||||
end |
||||
|
||||
describe '#<=>' do |
||||
it "is equal when the names match" do |
||||
expect(Theme.new(:some_name)).to eq Theme.new(:some_name) |
||||
end |
||||
|
||||
it "is not equal when the names don't match" do |
||||
expect(Theme.new(:some_name)).to_not eq Theme.new(:some_other_name) |
||||
end |
||||
|
||||
it "doesn't make a difference between strings and symbols" do |
||||
expect(Theme.new(:some_name)).to eq Theme.new('some_name') |
||||
end |
||||
end |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,152 @@ |
||||
require 'spec_helper' |
||||
|
||||
module Redmine |
||||
describe Themes do |
||||
before do |
||||
Themes.clear |
||||
end |
||||
|
||||
describe '#new_theme' do |
||||
it "returns a new theme" do |
||||
expected = Themes::Theme.new(:some_name) |
||||
|
||||
theme = Themes.new_theme :some_name |
||||
expect(theme).to eq expected |
||||
end |
||||
end |
||||
|
||||
describe '#register' do |
||||
it "registers themes" do |
||||
theme = Themes.new_theme :some_name |
||||
Themes.register theme |
||||
expect(Themes.themes).to include theme |
||||
end |
||||
|
||||
it "registers themes by name" do |
||||
expected = Themes.new_theme :some_name |
||||
|
||||
Themes.register :some_name |
||||
expect(Themes.themes).to include expected |
||||
end |
||||
|
||||
it "registers multiple themes at once" do |
||||
theme_1 = Themes.new_theme :some_name |
||||
theme_2 = Themes.new_theme :some_other_name |
||||
|
||||
Themes.register :some_name, :some_other_name |
||||
expect(Themes.themes).to include theme_1 |
||||
expect(Themes.themes).to include theme_2 |
||||
end |
||||
|
||||
it "does not store duplicate themes" do |
||||
Themes.register :some_name, :some_name |
||||
expect(Themes.themes.size).to eq 1 |
||||
end |
||||
|
||||
it "returns registered themes" do |
||||
expected = Themes.new_theme :some_name |
||||
|
||||
themes = Themes.register :some_name |
||||
expect(themes).to eq [expected] |
||||
end |
||||
end |
||||
|
||||
describe '#themes' do |
||||
it "returns all themes as an array" do |
||||
expected = Themes.new_theme :some_name |
||||
|
||||
Themes.register :some_name |
||||
expect(Themes.themes).to eq [expected] |
||||
end |
||||
end |
||||
|
||||
describe '#all' do |
||||
it "returns all themes as an array (same as #themes)" do |
||||
expected = Themes.new_theme :some_name |
||||
|
||||
Themes.register :some_name |
||||
expect(Themes.themes).to eq [expected] |
||||
end |
||||
end |
||||
|
||||
describe '#theme' do |
||||
it "returns a theme by name" do |
||||
theme = Themes.new_theme :some_name |
||||
Themes.register theme |
||||
expect(Themes.theme(:some_name)).to eq theme |
||||
end |
||||
|
||||
it "returns a default theme if not found" do |
||||
theme = Themes.new_theme :some_name |
||||
Themes.stub(:default_theme).and_return(theme) |
||||
expect(Themes.theme(:some_missing_name)).to be theme |
||||
end |
||||
end |
||||
|
||||
describe '#find_theme' do |
||||
it "returns a theme by name" do |
||||
theme = Themes.new_theme :some_name |
||||
Themes.register theme |
||||
expect(Themes.find_theme(:some_name)).to eq theme |
||||
end |
||||
|
||||
it "returns nil if no theme found" do |
||||
theme = Themes.new_theme :some_name |
||||
Themes.register theme |
||||
expect(Themes.find_theme(:some_other_name)).to be_nil |
||||
end |
||||
end |
||||
|
||||
describe '#clear' do |
||||
it "clears out the themes list" do |
||||
Themes.register :some_name |
||||
Themes.clear |
||||
expect(Themes.themes).to be_empty |
||||
end |
||||
end |
||||
|
||||
describe '#default_theme' do |
||||
it 'delagates to Theme class' do |
||||
expect(Themes.default_theme).to be Themes::Theme.default |
||||
end |
||||
|
||||
it 'defines the default theme' do |
||||
theme = Themes.new_theme :some_name |
||||
Themes.stub(:default_theme).and_return(theme) |
||||
expect(Themes.default_theme).to be theme |
||||
end |
||||
end |
||||
|
||||
describe '#register_default_theme' do |
||||
it "registers the default theme" do |
||||
Themes.register_default_theme |
||||
expect(Themes.all).to include Themes.default_theme |
||||
end |
||||
|
||||
it 'always registers the default theme on load' do |
||||
# reload the file because we called Themes.clear in the before block |
||||
# maybe better: skip the before block for this example?! |
||||
load File.expand_path('../../../lib/redmine/themes.rb', __FILE__) |
||||
|
||||
expect(Themes.all).to include Themes.default_theme |
||||
end |
||||
end |
||||
|
||||
describe '#each' do |
||||
it 'iterates over the registered themes' do |
||||
Themes.register :some_name |
||||
themes = [] |
||||
Themes.each { |theme| themes << theme.name } |
||||
expect(themes).to eq [:some_name] |
||||
end |
||||
end |
||||
|
||||
describe '#inject' do |
||||
it 'iterates over the registered themes' do |
||||
Themes.register :some_name |
||||
names = Themes.inject [] { |themes, theme| themes << theme.name } |
||||
expect(names).to eq [:some_name] |
||||
end |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,304 @@ |
||||
require 'spec_helper.rb' |
||||
|
||||
describe CustomField do |
||||
let(:field) { FactoryGirl.build :custom_field } |
||||
let(:field2) { FactoryGirl.build :custom_field } |
||||
|
||||
describe :name do |
||||
describe "uniqueness" do |
||||
|
||||
describe "WHEN value, locale and type are identical" do |
||||
before do |
||||
field.name = field2.name = "taken name" |
||||
field2.save! |
||||
end |
||||
|
||||
it { field.should_not be_valid } |
||||
end |
||||
|
||||
describe "WHEN value and locale are identical and type is different" do |
||||
before do |
||||
field.name = field2.name = "taken name" |
||||
field2.save! |
||||
field.type = "TestCustomField" |
||||
end |
||||
|
||||
it { field.should be_valid } |
||||
end |
||||
|
||||
describe "WHEN type and locale are identical and value is different" do |
||||
before do |
||||
field.name = "new name" |
||||
field2.name = "taken name" |
||||
field2.save! |
||||
end |
||||
|
||||
it { field.should be_valid } |
||||
end |
||||
|
||||
describe "WHEN value and type are identical and locale is different" do |
||||
before do |
||||
I18n.locale = :de |
||||
field2.name = "taken_name" |
||||
field2.save! |
||||
I18n.locale = :en |
||||
field.name = "taken_name" |
||||
end |
||||
|
||||
it { field.should be_valid } |
||||
end |
||||
end |
||||
|
||||
describe "localization" do |
||||
before do |
||||
I18n.locale = :de |
||||
field.name = "Feld" |
||||
|
||||
I18n.locale = :en |
||||
field.name = "Field" |
||||
end |
||||
|
||||
after do |
||||
I18n.locale = nil |
||||
end |
||||
|
||||
it "should return english name when in locale en" do |
||||
I18n.locale = :en |
||||
field.name.should == "Field" |
||||
end |
||||
|
||||
it "should return german name when in locale de" do |
||||
I18n.locale = :de |
||||
field.name.should == "Feld" |
||||
end |
||||
end |
||||
end |
||||
|
||||
describe :translations_attributes do |
||||
describe "WHEN providing a hash with locale and values" do |
||||
before do |
||||
field.translations_attributes = [ { "name" => "Feld", |
||||
"default_value" => "zwei", |
||||
"possible_values" => ["eins", "zwei", "drei"], |
||||
"locale" => "de" } ] |
||||
end |
||||
|
||||
it { field.should have(1).translations } |
||||
it { field.name(:de).should == "Feld" } |
||||
it { field.default_value(:de).should == "zwei" } |
||||
it { field.possible_values(:de).should == ["eins", "zwei", "drei"] } |
||||
end |
||||
|
||||
describe "WHEN providing a hash with only a locale" do |
||||
before do |
||||
field.translations_attributes = [ { "locale" => "de" } ] |
||||
end |
||||
|
||||
it { field.should have(0).translations } |
||||
end |
||||
|
||||
describe "WHEN providing a hash with a locale and blank values" do |
||||
before do |
||||
field.translations_attributes = [ { "name" => "", |
||||
"default_value" => "", |
||||
"possible_values" => "", |
||||
"locale" => "de" } ] |
||||
end |
||||
|
||||
it { field.should have(0).translations } |
||||
end |
||||
|
||||
describe "WHEN providing a hash with a locale and only one values" do |
||||
before do |
||||
field.translations_attributes = [ { "name" => "Feld", |
||||
"locale" => "de" } ] |
||||
end |
||||
|
||||
it { field.should have(1).translations } |
||||
it { field.name(:de).should == "Feld" } |
||||
end |
||||
|
||||
describe "WHEN providing a hash without a locale but with values" do |
||||
before do |
||||
field.translations_attributes = [ { "name" => "Feld", |
||||
"default_value" => "zwei", |
||||
"possible_values" => ["eins", "zwei", "drei"], |
||||
"locale" => "" } ] |
||||
end |
||||
|
||||
it { field.should have(0).translations } |
||||
end |
||||
|
||||
describe "WHEN already having a translation and wishing to delete it" do |
||||
before do |
||||
I18n.locale = :de |
||||
field.name = "Feld" |
||||
|
||||
I18n.locale = :en |
||||
field.name = "Field" |
||||
|
||||
field.save |
||||
field.reload |
||||
|
||||
field.translations_attributes = [ { "id" => field.translations.first.id.to_s, |
||||
"_destroy" => "1" } ] |
||||
|
||||
field.save |
||||
end |
||||
|
||||
it { field.should have(1).translation } |
||||
end |
||||
end |
||||
|
||||
|
||||
describe :default_value do |
||||
describe "localization" do |
||||
before do |
||||
I18n.locale = :de |
||||
field.default_value = "Standard" |
||||
|
||||
I18n.locale = :en |
||||
field.default_value = "default" |
||||
end |
||||
|
||||
it { field.default_value(:en).should == "default" } |
||||
it { field.default_value(:de).should == "Standard" } |
||||
end |
||||
end |
||||
|
||||
describe :possible_values do |
||||
describe "localization" do |
||||
before do |
||||
I18n.locale = :de |
||||
field.possible_values = ["eins", "zwei", "drei"] |
||||
|
||||
I18n.locale = :en |
||||
field.possible_values = ["one", "two", "three"] |
||||
|
||||
I18n.locale = :fr |
||||
field.possible_values = "un\ndeux\ntrois" |
||||
|
||||
I18n.locale = :de |
||||
field.save! |
||||
field.reload |
||||
end |
||||
|
||||
it { field.possible_values(:en).should == ["one", "two", "three"] } |
||||
it { field.possible_values(:de).should == ["eins", "zwei", "drei"] } |
||||
it { field.possible_values(:fr).should == ["un", "deux", "trois"] } |
||||
end |
||||
end |
||||
|
||||
describe :valid? do |
||||
describe "WITH a list field |
||||
WITH two translations |
||||
WITH default_value not included in possible_values in the non current locale translation" do |
||||
|
||||
before do |
||||
field.field_format = 'list' |
||||
field.translations_attributes = [ { "name" => "Feld", |
||||
"default_value" => "vier", |
||||
"possible_values" => ["eins", "zwei", "drei"], |
||||
"locale" => "de" }, |
||||
{ "name" => "Field", |
||||
"locale" => "en", |
||||
"possible_values" => "one\ntwo\nthree\n", |
||||
"default_value" => "two" } ] |
||||
end |
||||
|
||||
it { field.should_not be_valid } |
||||
end |
||||
|
||||
describe "WITH a list field |
||||
WITH two translations |
||||
WITH default_value included in possible_values" do |
||||
|
||||
before do |
||||
field.field_format = 'list' |
||||
field.translations_attributes = [ { "name" => "Feld", |
||||
"default_value" => "zwei", |
||||
"possible_values" => ["eins", "zwei", "drei"], |
||||
"locale" => "de" }, |
||||
{ "name" => "Field", |
||||
"locale" => "en", |
||||
"possible_values" => "one\ntwo\nthree\n", |
||||
"default_value" => "two" } ] |
||||
end |
||||
|
||||
it { field.should be_valid } |
||||
end |
||||
|
||||
|
||||
describe "WITH a list field |
||||
WITH two translations |
||||
WITH default_value not included in possible_values in the current locale translation" do |
||||
|
||||
before do |
||||
field.field_format = 'list' |
||||
field.translations_attributes = [ { "name" => "Feld", |
||||
"default_value" => "zwei", |
||||
"possible_values" => ["eins", "zwei", "drei"], |
||||
"locale" => "de" }, |
||||
{ "name" => "Field", |
||||
"locale" => "en", |
||||
"possible_values" => "one\ntwo\nthree\n", |
||||
"default_value" => "four" } ] |
||||
end |
||||
|
||||
it { field.should_not be_valid } |
||||
end |
||||
|
||||
describe "WITH a list field |
||||
WITH two translations |
||||
WITH possible_values beeing empty in a fallbacked translation" do |
||||
|
||||
before do |
||||
field.field_format = 'list' |
||||
field.translations_attributes = [ { "name" => "Feld", |
||||
"locale" => "de" }, |
||||
{ "name" => "Field", |
||||
"locale" => "en", |
||||
"possible_values" => "one\ntwo\nthree\n", |
||||
"default_value" => "two" } ] |
||||
end |
||||
|
||||
it { field.should be_valid } |
||||
end |
||||
|
||||
describe "WITH a list field |
||||
WITH the field beeing required |
||||
WITH two translations |
||||
WITH neither translation defining a default_value" do |
||||
|
||||
before do |
||||
field.field_format = 'list' |
||||
field.is_required = true |
||||
field.translations_attributes = [ { "name" => "Feld", |
||||
"locale" => "de" }, |
||||
{ "name" => "Field", |
||||
"possible_values" => "one\ntwo\nthree\n", |
||||
"locale" => "en" } ] |
||||
end |
||||
|
||||
it { field.should be_valid } |
||||
end |
||||
|
||||
describe "WITH a boolean field |
||||
WITH the field beeing required |
||||
WITH two translations beeing provided |
||||
WITH only one translation specifying a default value" do |
||||
|
||||
before do |
||||
field.field_format = 'bool' |
||||
field.translations_attributes = { "0" => { "name" => "name_en", |
||||
"default_value" => "1", |
||||
"locale" => "en" }, |
||||
"1" => { "name" => "name_es", |
||||
"locale" => "es" } } |
||||
field.is_required = true |
||||
end |
||||
|
||||
it { field.should be_valid } |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,82 @@ |
||||
require 'spec_helper.rb' |
||||
|
||||
describe DeletedUser do |
||||
let(:user) { DeletedUser.new } |
||||
|
||||
describe :admin do |
||||
it { user.admin.should be_false } |
||||
end |
||||
|
||||
describe :logged? do |
||||
it { user.should_not be_logged } |
||||
end |
||||
|
||||
describe :name do |
||||
it { user.name.should == I18n.t('user.deleted') } |
||||
end |
||||
|
||||
describe :mail do |
||||
it { user.mail.should be_nil } |
||||
end |
||||
|
||||
describe :time_zone do |
||||
it { user.time_zone.should be_nil } |
||||
end |
||||
|
||||
describe :rss_key do |
||||
it { user.rss_key.should be_nil } |
||||
end |
||||
|
||||
describe :destroy do |
||||
it { user.destroy.should be_false } |
||||
end |
||||
|
||||
describe :available_custom_fields do |
||||
before do |
||||
FactoryGirl.create(:user_custom_field) |
||||
end |
||||
|
||||
it { user.available_custom_fields.should == [] } |
||||
end |
||||
|
||||
describe :create do |
||||
describe "WHEN creating a second deleted user" do |
||||
let(:u1) { FactoryGirl.build(:deleted_user) } |
||||
let(:u2) { FactoryGirl.build(:deleted_user) } |
||||
|
||||
before do |
||||
u1.save |
||||
u2.save |
||||
end |
||||
|
||||
it { u1.should_not be_new_record } |
||||
it { u2.should be_new_record } |
||||
it { u2.errors[:base].should == 'A DeletedUser already exists.' } |
||||
end |
||||
end |
||||
|
||||
describe :valid do |
||||
describe "WHEN no login, first-, lastname and mail is provided" do |
||||
let(:user) { DeletedUser.new } |
||||
|
||||
it { user.should be_valid } |
||||
end |
||||
end |
||||
|
||||
describe :first do |
||||
describe "WHEN a deleted user already exists" do |
||||
let(:user) { FactoryGirl.build(:deleted_user) } |
||||
|
||||
before do |
||||
user.save! |
||||
end |
||||
|
||||
it { DeletedUser.first.should == user } |
||||
end |
||||
|
||||
describe "WHEN no deleted user exists" do |
||||
it { DeletedUser.first.is_a?(DeletedUser).should be_true } |
||||
it { DeletedUser.first.should_not be_new_record } |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,83 @@ |
||||
require 'spec_helper' |
||||
|
||||
describe Issue do |
||||
describe 'Acts as journalized' do |
||||
before(:each) do |
||||
IssueStatus.delete_all |
||||
IssuePriority.delete_all |
||||
|
||||
@status_resolved ||= FactoryGirl.create(:issue_status, :name => "Resolved", :is_default => false) |
||||
@status_open ||= FactoryGirl.create(:issue_status, :name => "Open", :is_default => true) |
||||
@status_rejected ||= FactoryGirl.create(:issue_status, :name => "Rejected", :is_default => false) |
||||
|
||||
@priority_low ||= FactoryGirl.create(:priority_low, :is_default => true) |
||||
@priority_high ||= FactoryGirl.create(:priority_high) |
||||
@tracker ||= FactoryGirl.create(:tracker_feature) |
||||
@project ||= FactoryGirl.create(:project_with_trackers) |
||||
|
||||
@current = FactoryGirl.create(:user, :login => "user1", :mail => "user1@users.com") |
||||
User.stub!(:current).and_return(@current) |
||||
|
||||
@user2 = FactoryGirl.create(:user, :login => "user2", :mail => "user2@users.com") |
||||
|
||||
|
||||
@issue ||= FactoryGirl.create(:issue, :project => @project, :status => @status_open, :tracker => @tracker, :author => @current) |
||||
end |
||||
|
||||
describe 'ignore blank to blank transitions' do |
||||
it 'should not include the "nil to empty string"-transition' do |
||||
@issue.description = nil |
||||
@issue.save! |
||||
|
||||
@issue.description = "" |
||||
@issue.send(:incremental_journal_changes).should be_empty |
||||
end |
||||
end |
||||
|
||||
describe 'Acts as journalized recreate initial journal' do |
||||
it 'should not include certain attributes' do |
||||
recreated_journal = @issue.recreate_initial_journal! |
||||
|
||||
recreated_journal.attributes["changed_data"].include?('rgt').should == false |
||||
recreated_journal.attributes["changed_data"].include?('lft').should == false |
||||
recreated_journal.attributes["changed_data"].include?('lock_version').should == false |
||||
recreated_journal.attributes["changed_data"].include?('updated_at').should == false |
||||
recreated_journal.attributes["changed_data"].include?('updated_on').should == false |
||||
recreated_journal.attributes["changed_data"].include?('id').should == false |
||||
recreated_journal.attributes["changed_data"].include?('type').should == false |
||||
recreated_journal.attributes["changed_data"].include?('root_id').should == false |
||||
end |
||||
|
||||
it 'should not include useless transitions' do |
||||
recreated_journal = @issue.recreate_initial_journal! |
||||
|
||||
recreated_journal.attributes["changed_data"].values.each do |change| |
||||
change.first.should_not == change.last |
||||
end |
||||
end |
||||
|
||||
it 'should not be different from the initially created journal by aaj' do |
||||
# Creating four journals total |
||||
@issue.status = @status_resolved |
||||
@issue.assigned_to = @user2 |
||||
@issue.save! |
||||
@issue.reload |
||||
|
||||
@issue.priority = @priority_high |
||||
@issue.save! |
||||
@issue.reload |
||||
|
||||
@issue.status = @status_rejected |
||||
@issue.priority = @priority_low |
||||
@issue.estimated_hours = 3 |
||||
@issue.remaining_hours = 43 if Redmine::Plugin.all.collect(&:id).include?(:backlogs) |
||||
@issue.save! |
||||
|
||||
initial_journal = @issue.journals.first |
||||
recreated_journal = @issue.recreate_initial_journal! |
||||
|
||||
initial_journal.should be_identical(recreated_journal) |
||||
end |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,12 @@ |
||||
require 'spec_helper' |
||||
|
||||
describe Role do |
||||
describe '#by_permission' do |
||||
it "returns roles with given permission" do |
||||
edit_project_role = FactoryGirl.create :role, :permissions => [:edit_project] |
||||
|
||||
expect( Role.by_permission(:edit_project) ).to eq [edit_project_role] |
||||
expect( Role.by_permission(:some_other) ).to eq [] |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,418 @@ |
||||
require File.expand_path(File.dirname(__FILE__) + "/../spec_helper.rb") |
||||
|
||||
describe User, 'deletion' do |
||||
let(:user) { FactoryGirl.build(:user) } |
||||
let(:user2) { FactoryGirl.build(:user) } |
||||
let(:project) { FactoryGirl.create(:project_with_trackers) } |
||||
let(:role) { FactoryGirl.create(:role) } |
||||
let(:member) { FactoryGirl.create(:member, :project => project, |
||||
:roles => [role], |
||||
:principal => user) } |
||||
let(:issue_status) { FactoryGirl.create(:issue_status) } |
||||
let(:issue) { FactoryGirl.create(:issue, :tracker => project.trackers.first, |
||||
:author => user, |
||||
:project => project, |
||||
:status => issue_status, |
||||
:assigned_to => user) } |
||||
let(:issue2) { FactoryGirl.create(:issue, :tracker => project.trackers.first, |
||||
:author => user2, |
||||
:project => project, |
||||
:status => issue_status, |
||||
:assigned_to => user2) } |
||||
|
||||
let(:substitute_user) { DeletedUser.first } |
||||
|
||||
before do |
||||
# for some reason there seem to be users in the db |
||||
User.delete_all |
||||
user.save! |
||||
user2.save! |
||||
end |
||||
|
||||
describe "WHEN there is the user" do |
||||
before do |
||||
user.destroy |
||||
end |
||||
|
||||
it { User.find_by_id(user.id).should be_nil } |
||||
end |
||||
|
||||
shared_examples_for "updated journalized associated object" do |
||||
before do |
||||
User.current = user2 |
||||
associations.each do |association| |
||||
associated_instance.send(association.to_s + "=", user2) |
||||
end |
||||
associated_instance.save! |
||||
|
||||
User.current = user # in order to have the content journal created by the user |
||||
associated_instance.reload |
||||
associations.each do |association| |
||||
associated_instance.send(association.to_s + "=", user) |
||||
end |
||||
associated_instance.save! |
||||
|
||||
user.destroy |
||||
associated_instance.reload |
||||
end |
||||
|
||||
it { associated_class.find_by_id(associated_instance.id).should == associated_instance } |
||||
it "should replace the user on all associations" do |
||||
associations.each do |association| |
||||
associated_instance.send(association).should == substitute_user |
||||
end |
||||
end |
||||
it { associated_instance.journals.first.user.should == user2 } |
||||
it "should update first journal changes" do |
||||
associations.each do |association| |
||||
associated_instance.journals.first.changes[association.to_s + "_id"].last.should == user2.id |
||||
end |
||||
end |
||||
it { associated_instance.journals.last.user.should == substitute_user } |
||||
it "should update second journal changes" do |
||||
associations.each do |association| |
||||
associated_instance.journals.last.changes[association.to_s + "_id"].last.should == substitute_user.id |
||||
end |
||||
end |
||||
end |
||||
|
||||
shared_examples_for "created associated object" do |
||||
before do |
||||
associations.each do |association| |
||||
associated_instance.send(association.to_s + "=", user) |
||||
end |
||||
associated_instance.save! |
||||
|
||||
user.destroy |
||||
associated_instance.reload |
||||
end |
||||
|
||||
it { associated_class.find_by_id(associated_instance.id).should == associated_instance } |
||||
it "should replace the user on all associations" do |
||||
associations.each do |association| |
||||
associated_instance.send(association).should == substitute_user |
||||
end |
||||
end |
||||
end |
||||
|
||||
shared_examples_for "created journalized associated object" do |
||||
before do |
||||
User.current = user # in order to have the content journal created by the user |
||||
associations.each do |association| |
||||
associated_instance.send(association.to_s + "=", user) |
||||
end |
||||
associated_instance.save! |
||||
|
||||
User.current = user2 |
||||
associated_instance.reload |
||||
associations.each do |association| |
||||
associated_instance.send(association.to_s + "=", user2) |
||||
end |
||||
associated_instance.save! |
||||
|
||||
user.destroy |
||||
associated_instance.reload |
||||
end |
||||
|
||||
it { associated_class.find_by_id(associated_instance.id).should == associated_instance } |
||||
it "should keep the current user on all associations" do |
||||
associations.each do |association| |
||||
associated_instance.send(association).should == user2 |
||||
end |
||||
end |
||||
it { associated_instance.journals.first.user.should == substitute_user } |
||||
it "should update the first journal" do |
||||
associations.each do |association| |
||||
associated_instance.journals.first.changes[association.to_s + "_id"].last.should == substitute_user.id |
||||
end |
||||
end |
||||
it { associated_instance.journals.last.user.should == user2 } |
||||
it "should update the last journal" do |
||||
associations.each do |association| |
||||
associated_instance.journals.last.changes[association.to_s + "_id"].first.should == substitute_user.id |
||||
associated_instance.journals.last.changes[association.to_s + "_id"].last.should == user2.id |
||||
end |
||||
end |
||||
end |
||||
|
||||
describe "WHEN the user has created one attachment" do |
||||
let(:associated_instance) { FactoryGirl.build(:attachment) } |
||||
let(:associated_class) { Attachment } |
||||
let(:associations) { [:author] } |
||||
|
||||
it_should_behave_like "created journalized associated object" |
||||
end |
||||
|
||||
describe "WHEN the user has updated one attachment" do |
||||
let(:associated_instance) { FactoryGirl.build(:attachment) } |
||||
let(:associated_class) { Attachment } |
||||
let(:associations) { [:author] } |
||||
|
||||
it_should_behave_like "updated journalized associated object" |
||||
end |
||||
|
||||
describe "WHEN the user has an issue created and assigned" do |
||||
let(:associated_instance) { FactoryGirl.build(:issue, :tracker => project.trackers.first, |
||||
:project => project, |
||||
:status => issue_status) } |
||||
let(:associated_class) { Issue } |
||||
let(:associations) { [:author, :assigned_to] } |
||||
|
||||
it_should_behave_like "created journalized associated object" |
||||
end |
||||
|
||||
describe "WHEN the user has an issue updated and assigned" do |
||||
let(:associated_instance) { FactoryGirl.build(:issue, :tracker => project.trackers.first, |
||||
:project => project, |
||||
:status => issue_status) } |
||||
let(:associated_class) { Issue } |
||||
let(:associations) { [:author, :assigned_to] } |
||||
|
||||
before do |
||||
User.current = user2 |
||||
associated_instance.author = user2 |
||||
associated_instance.assigned_to = user2 |
||||
associated_instance.save! |
||||
|
||||
User.current = user # in order to have the content journal created by the user |
||||
associated_instance.reload |
||||
associated_instance.author = user |
||||
associated_instance.assigned_to = user |
||||
associated_instance.save! |
||||
|
||||
user.destroy |
||||
associated_instance.reload |
||||
end |
||||
|
||||
it { associated_class.find_by_id(associated_instance.id).should == associated_instance } |
||||
it "should replace the user on all associations" do |
||||
associated_instance.author.should == substitute_user |
||||
associated_instance.assigned_to.should be_nil |
||||
end |
||||
it { associated_instance.journals.first.user.should == user2 } |
||||
it "should update first journal changes" do |
||||
associations.each do |association| |
||||
associated_instance.journals.first.changes[association.to_s + "_id"].last.should == user2.id |
||||
end |
||||
end |
||||
it { associated_instance.journals.last.user.should == substitute_user } |
||||
it "should update second journal changes" do |
||||
associations.each do |association| |
||||
associated_instance.journals.last.changes[association.to_s + "_id"].last.should == substitute_user.id |
||||
end |
||||
end |
||||
end |
||||
|
||||
describe "WHEN the user has updated a wiki content" do |
||||
let(:associated_instance) { FactoryGirl.build(:wiki_content) } |
||||
let(:associated_class) { WikiContent} |
||||
let(:associations) { [:author] } |
||||
|
||||
it_should_behave_like "updated journalized associated object" |
||||
end |
||||
|
||||
describe "WHEN the user has created a wiki content" do |
||||
let(:associated_instance) { FactoryGirl.build(:wiki_content) } |
||||
let(:associated_class) { WikiContent } |
||||
let(:associations) { [:author] } |
||||
|
||||
it_should_behave_like "created journalized associated object" |
||||
end |
||||
|
||||
describe "WHEN the user has created a news" do |
||||
let(:associated_instance) { FactoryGirl.build(:news) } |
||||
let(:associated_class) { News } |
||||
let(:associations) { [:author] } |
||||
|
||||
it_should_behave_like "created journalized associated object" |
||||
end |
||||
|
||||
describe "WHEN the user has worked on news" do |
||||
let(:associated_instance) { FactoryGirl.build(:news) } |
||||
let(:associated_class) { News } |
||||
let(:associations) { [:author] } |
||||
|
||||
it_should_behave_like "updated journalized associated object" |
||||
end |
||||
|
||||
describe "WHEN the user has created a message" do |
||||
let(:associated_instance) { FactoryGirl.build(:message) } |
||||
let(:associated_class) { Message } |
||||
let(:associations) { [:author] } |
||||
|
||||
it_should_behave_like "created journalized associated object" |
||||
end |
||||
|
||||
describe "WHEN the user has worked on message" do |
||||
let(:associated_instance) { FactoryGirl.build(:message) } |
||||
let(:associated_class) { Message } |
||||
let(:associations) { [:author] } |
||||
|
||||
it_should_behave_like "updated journalized associated object" |
||||
end |
||||
|
||||
describe "WHEN the user has created a time entry" do |
||||
let(:associated_instance) { FactoryGirl.build(:time_entry, :project => project, |
||||
:issue => issue, |
||||
:hours => 2, |
||||
:activity => FactoryGirl.create(:time_entry_activity)) } |
||||
let(:associated_class) { TimeEntry } |
||||
let(:associations) { [:user] } |
||||
|
||||
it_should_behave_like "created journalized associated object" |
||||
end |
||||
|
||||
describe "WHEN the user has worked on time_entry" do |
||||
let(:associated_instance) { FactoryGirl.build(:time_entry, :project => project, |
||||
:issue => issue, |
||||
:hours => 2, |
||||
:activity => FactoryGirl.create(:time_entry_activity)) } |
||||
let(:associated_class) { TimeEntry } |
||||
let(:associations) { [:user] } |
||||
|
||||
it_should_behave_like "updated journalized associated object" |
||||
end |
||||
|
||||
describe "WHEN the user has commented" do |
||||
let(:news) { FactoryGirl.create(:news, :author => user) } |
||||
|
||||
let(:associated_instance) { Comment.new(:commented => news, |
||||
:comments => "lorem") } |
||||
|
||||
let(:associated_class) { Comment } |
||||
let(:associations) { [:author] } |
||||
|
||||
it_should_behave_like "created associated object" |
||||
end |
||||
|
||||
describe "WHEN the user is a member of a project" do |
||||
before do |
||||
member #saving |
||||
|
||||
user.destroy |
||||
end |
||||
|
||||
it { Member.find_by_id(member.id).should be_nil } |
||||
it { Role.find_by_id(role.id).should == role } |
||||
it { Project.find_by_id(project.id).should == project } |
||||
end |
||||
|
||||
describe "WHEN the user is watching something" do |
||||
let(:watched) { FactoryGirl.create(:wiki_content) } |
||||
let(:watch) { Watcher.new(:user => user, |
||||
:watchable => watched) } |
||||
|
||||
before do |
||||
watch.save |
||||
|
||||
user.destroy |
||||
end |
||||
|
||||
it { Watcher.find_by_id(watch.id).should be_nil } |
||||
end |
||||
|
||||
describe "WHEN the user has a token created" do |
||||
let(:token) { Token.new(:user => user, |
||||
:action => "feeds", |
||||
:value => "loremipsum") } |
||||
|
||||
before do |
||||
token.save! |
||||
|
||||
user.destroy |
||||
end |
||||
|
||||
it { Token.find_by_id(token.id).should be_nil } |
||||
end |
||||
|
||||
describe "WHEN the user has created a private query" do |
||||
let(:query) { FactoryGirl.build(:private_query, :user => user) } |
||||
|
||||
before do |
||||
query.save! |
||||
|
||||
user.destroy |
||||
end |
||||
|
||||
it { Query.find_by_id(query.id).should be_nil } |
||||
end |
||||
|
||||
describe "WHEN the user has created a public query" do |
||||
let(:associated_instance) { FactoryGirl.build(:public_query) } |
||||
|
||||
let(:associated_class) { Query } |
||||
let(:associations) { [:user] } |
||||
|
||||
it_should_behave_like "created associated object" |
||||
end |
||||
|
||||
describe "WHEN the user has created a changeset" do |
||||
let(:repository) { FactoryGirl.create(:repository) } |
||||
let(:associated_instance) { FactoryGirl.build(:changeset, :repository_id => repository.id, |
||||
:committer => user.login) } |
||||
|
||||
let(:associated_class) { Changeset } |
||||
let(:associations) { [:user] } |
||||
|
||||
before do |
||||
Setting.enabled_scm = Setting.enabled_scm << "Filesystem" |
||||
end |
||||
|
||||
it_should_behave_like "created journalized associated object" |
||||
end |
||||
|
||||
describe "WHEN the user has updated a changeset" do |
||||
let(:repository) { FactoryGirl.create(:repository) } |
||||
let(:associated_instance) { FactoryGirl.build(:changeset, :repository_id => repository.id, |
||||
:committer => user2.login) } |
||||
|
||||
let(:associated_class) { Changeset } |
||||
let(:associations) { [:user] } |
||||
|
||||
before do |
||||
Setting.enabled_scm = Setting.enabled_scm << "Filesystem" |
||||
User.current = user2 |
||||
associated_instance.user = user2 |
||||
associated_instance.save! |
||||
|
||||
User.current = user # in order to have the content journal created by the user |
||||
associated_instance.reload |
||||
associated_instance.user = user |
||||
associated_instance.save! |
||||
|
||||
user.destroy |
||||
associated_instance.reload |
||||
end |
||||
|
||||
it { associated_class.find_by_id(associated_instance.id).should == associated_instance } |
||||
it "should replace the user on all associations" do |
||||
associated_instance.user.should be_nil |
||||
end |
||||
it { associated_instance.journals.first.user.should == user2 } |
||||
it "should update first journal changes" do |
||||
associated_instance.journals.first.changes["user_id"].last.should == user2.id |
||||
end |
||||
it { associated_instance.journals.last.user.should == substitute_user } |
||||
it "should update second journal changes" do |
||||
associated_instance.journals.last.changes["user_id"].last.should == substitute_user.id |
||||
end |
||||
end |
||||
|
||||
describe "WHEN the user is assigned an issue category" do |
||||
let(:issue_category) { FactoryGirl.build(:issue_category, :assigned_to => user, |
||||
:project => project) } |
||||
|
||||
before do |
||||
FactoryGirl.create(:member, :principal => user, |
||||
:project => project, |
||||
:roles => [role]) |
||||
issue_category.save! |
||||
|
||||
user.destroy |
||||
issue_category.reload |
||||
end |
||||
|
||||
it { IssueCategory.find_by_id(issue_category.id).should == issue_category } |
||||
it { issue_category.assigned_to.should be_nil } |
||||
end |
||||
end |
@ -0,0 +1,69 @@ |
||||
require File.expand_path(File.dirname(__FILE__) + "/../spec_helper.rb") |
||||
|
||||
describe User do |
||||
let(:user) { FactoryGirl.build(:user) } |
||||
let(:project) { FactoryGirl.create(:project_with_trackers) } |
||||
let(:role) { FactoryGirl.create(:role) } |
||||
let(:member) { FactoryGirl.build(:member, :project => project, |
||||
:roles => [role], |
||||
:principal => user) } |
||||
let(:issue_status) { FactoryGirl.create(:issue_status) } |
||||
let(:issue) { FactoryGirl.build(:issue, :tracker => project.trackers.first, |
||||
:author => user, |
||||
:project => project, |
||||
:status => issue_status) } |
||||
|
||||
describe :assigned_issues do |
||||
before do |
||||
user.save! |
||||
end |
||||
|
||||
describe "WHEN the user has an issue assigned" do |
||||
before do |
||||
member.save! |
||||
|
||||
issue.assigned_to = user |
||||
issue.save |
||||
end |
||||
|
||||
it { user.assigned_issues.should == [issue] } |
||||
end |
||||
|
||||
describe "WHEN the user has no issue assigned" do |
||||
before do |
||||
member.save |
||||
|
||||
issue.save |
||||
end |
||||
|
||||
it { user.assigned_issues.should == [] } |
||||
end |
||||
end |
||||
|
||||
describe :watches do |
||||
before do |
||||
user.save! |
||||
end |
||||
|
||||
describe "WHEN the user is watching" do |
||||
let(:watcher) { Watcher.new(:watchable => issue, |
||||
:user => user) } |
||||
|
||||
before do |
||||
issue.save |
||||
|
||||
watcher.save |
||||
end |
||||
|
||||
it { user.watches.should == [watcher] } |
||||
end |
||||
|
||||
describe "WHEN the user isn't watching" do |
||||
before do |
||||
issue.save |
||||
end |
||||
|
||||
it { user.watches.should == [] } |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,72 @@ |
||||
require File.dirname(__FILE__) + '/../spec_helper' |
||||
|
||||
describe WikiMenuItem do |
||||
before(:each) do |
||||
@project = FactoryGirl.create(:project, :enabled_module_names => %w[activity]) |
||||
@current = FactoryGirl.create(:user, :login => "user1", :mail => "user1@users.com") |
||||
|
||||
User.stub!(:current).and_return(@current) |
||||
end |
||||
|
||||
it 'should create a default wiki menu item when enabling the wiki' do |
||||
WikiMenuItem.all.should_not be_any |
||||
|
||||
@project.enabled_modules << EnabledModule.new(:name => 'wiki') |
||||
@project.reload |
||||
|
||||
wiki_item = @project.wiki.wiki_menu_items.first |
||||
wiki_item.name.should eql 'Wiki' |
||||
wiki_item.title.should eql 'Wiki' |
||||
wiki_item.options[:index_page].should eql true |
||||
wiki_item.options[:new_wiki_page].should eql true |
||||
end |
||||
|
||||
it 'should change title when a wikipage is renamed' do |
||||
wikipage = FactoryGirl.create(:wiki_page, :title => 'Oldtitle') |
||||
|
||||
menu_item_1 = FactoryGirl.create(:wiki_menu_item, :wiki_id => wikipage.wiki.id, |
||||
:name => 'Item 1', |
||||
:title => 'Oldtitle') |
||||
|
||||
wikipage.title = 'Newtitle' |
||||
wikipage.save |
||||
|
||||
menu_item_1.reload |
||||
menu_item_1.title.should == wikipage.title |
||||
end |
||||
|
||||
describe 'it should destroy' do |
||||
before(:each) do |
||||
@project.enabled_modules << EnabledModule.new(:name => 'wiki') |
||||
@project.reload |
||||
|
||||
@menu_item_1 = FactoryGirl.create(:wiki_menu_item, :wiki_id => @project.wiki.id, |
||||
:name => 'Item 1', |
||||
:title => 'Item 1') |
||||
|
||||
@menu_item_2 = FactoryGirl.create(:wiki_menu_item, :wiki_id => @project.wiki.id, |
||||
:name => 'Item 2', |
||||
:parent_id => @menu_item_1.id, |
||||
:title => 'Item 2') |
||||
end |
||||
|
||||
it 'all children when deleting the parent' do |
||||
@menu_item_1.destroy |
||||
|
||||
expect {WikiMenuItem.find(@menu_item_1.id)}.to raise_error(ActiveRecord::RecordNotFound) |
||||
expect {WikiMenuItem.find(@menu_item_2.id)}.to raise_error(ActiveRecord::RecordNotFound) |
||||
end |
||||
|
||||
describe 'all items when destroying' do |
||||
it 'the associated project' do |
||||
@project.destroy |
||||
WikiMenuItem.all.should_not be_any |
||||
end |
||||
|
||||
it 'the associated wiki' do |
||||
@project.wiki.destroy |
||||
WikiMenuItem.all.should_not be_any |
||||
end |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,39 @@ |
||||
Journal.class_eval do |
||||
def identical?(o) |
||||
return false unless self.class === o |
||||
|
||||
original = self.attributes |
||||
recreated = o.attributes |
||||
|
||||
original.except!("created_at") |
||||
original["changed_data"].except!("created_on") |
||||
recreated.except!("created_at") |
||||
recreated["changed_data"].except!("created_on") |
||||
|
||||
original.identical?(recreated) |
||||
end |
||||
end |
||||
|
||||
Hash.class_eval do |
||||
def identical?(o) |
||||
return false unless self.class === o |
||||
(o.keys + keys).uniq.all? do |key| |
||||
(o[key].identical?(self[key])) |
||||
end |
||||
end |
||||
end |
||||
|
||||
Array.class_eval do |
||||
def identical?(o) |
||||
return false unless self.class === o |
||||
all? do |ea| |
||||
(o.any? {|other_each| other_each.identical?(ea) }) |
||||
end |
||||
end |
||||
end |
||||
|
||||
Object.class_eval do |
||||
def identical?(o) |
||||
self == o |
||||
end |
||||
end |
@ -0,0 +1,48 @@ |
||||
Spec::Matchers.define :have_exactly_one_selected_menu_item_in do |menu| |
||||
match do |actual| |
||||
failure_message(menu, actual) == nil |
||||
end |
||||
|
||||
failure_message_for_should do |actual| |
||||
failure_message(menu, actual) |
||||
end |
||||
|
||||
description do |
||||
"have exactly one selected menu item in #{menu}" |
||||
end |
||||
|
||||
failure_message_for_should_not do |actual| |
||||
raise "You should not use this matcher for should_not matches" |
||||
end |
||||
|
||||
def failure_message(menu, actual) |
||||
menu_selector = HTML::Selector.new(selector_for_menu(menu)) |
||||
menu_item_selector = HTML::Selector.new('a.selected') |
||||
|
||||
html = HTML::Document.new(actual.is_a?(String) ? actual : actual.body) |
||||
|
||||
menu_matches = menu_selector.select(html.root) |
||||
if menu_matches.size == 1 |
||||
menu_item_matches = menu_item_selector.select(menu_matches.first) |
||||
|
||||
if menu_item_matches.size == 0 |
||||
"Expected to find exactly one selected menu item in #{menu}, but found none." |
||||
elsif menu_item_matches.size > 1 |
||||
"Expected to find exactly one selected menu item in #{menu}, but found #{menu_item_matches.size}." |
||||
else |
||||
nil |
||||
end |
||||
else |
||||
"Expected to find #{menu.inspect} in document, but didn't." |
||||
end |
||||
end |
||||
|
||||
def selector_for_menu(menu) |
||||
case menu |
||||
when :project_menu |
||||
'#main-menu' |
||||
else |
||||
raise ArgumentError, 'Unknown menu identifier' |
||||
end |
||||
end |
||||
end |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue