Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

issues_controller_test.rb 258KB

Converted routing and urls to follow the Rails REST convention. Patch supplied by commits from Gerrit Kaiser on Github. Existing routes will still work (backwards compatible) but any new urls will be generated using the new routing rules. Changes listed below: * made the URLs for some project tabs and project settings follow the new rails RESTful conventions of /collection/:id/subcollection/:sub_id * prettier URL for project roadmap * more nice project URLs * use GET for filtering form * prettified URLs used on issues tab * custom route for activity atom feeds * prettier repository urls * fixed broken route definition * fixed failing tests for issuecontroller that were hardcoding the url string * more RESTful routes for boards and messages * RESTful routes for wiki pages * RESTful routes for documents * moved old routes that are retained for compatibility to the bottom and grouped them together * added RESTful URIs for issues * RESTfulness for the news section * fixed route order * changed hardcoded URLs in tests * fixed badly written tests * fixed forgotten parameter in routes * changed hardcoded URLS to new scheme * changed project add url to the standard POST to collection * create new issue by POSTing to collection * changed hardcoded URLs in integrations tests * made project add form work again * restful routes for project deletion * prettier routes for project (un)archival * made routes table more readable * fixed note quoting * user routing * fixed bug * always sort by GET * Fixed: cross-project issue list should not show issues of projects for which the issue tracking module was disabled. * prettified URLs used on issues tab * urls for time log * fixed reply routing * eliminate revision query paremeter for diff and entry actions * fixed test failures with hard-coded urls * ensure ajax links always use get * refactored ajax link generation into separate method #1901 git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2317 e93f8b46-1217-0410-a6f0-8f06a7374b81
15 år sedan
Converted routing and urls to follow the Rails REST convention. Patch supplied by commits from Gerrit Kaiser on Github. Existing routes will still work (backwards compatible) but any new urls will be generated using the new routing rules. Changes listed below: * made the URLs for some project tabs and project settings follow the new rails RESTful conventions of /collection/:id/subcollection/:sub_id * prettier URL for project roadmap * more nice project URLs * use GET for filtering form * prettified URLs used on issues tab * custom route for activity atom feeds * prettier repository urls * fixed broken route definition * fixed failing tests for issuecontroller that were hardcoding the url string * more RESTful routes for boards and messages * RESTful routes for wiki pages * RESTful routes for documents * moved old routes that are retained for compatibility to the bottom and grouped them together * added RESTful URIs for issues * RESTfulness for the news section * fixed route order * changed hardcoded URLs in tests * fixed badly written tests * fixed forgotten parameter in routes * changed hardcoded URLS to new scheme * changed project add url to the standard POST to collection * create new issue by POSTing to collection * changed hardcoded URLs in integrations tests * made project add form work again * restful routes for project deletion * prettier routes for project (un)archival * made routes table more readable * fixed note quoting * user routing * fixed bug * always sort by GET * Fixed: cross-project issue list should not show issues of projects for which the issue tracking module was disabled. * prettified URLs used on issues tab * urls for time log * fixed reply routing * eliminate revision query paremeter for diff and entry actions * fixed test failures with hard-coded urls * ensure ajax links always use get * refactored ajax link generation into separate method #1901 git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2317 e93f8b46-1217-0410-a6f0-8f06a7374b81
15 år sedan
Converted routing and urls to follow the Rails REST convention. Patch supplied by commits from Gerrit Kaiser on Github. Existing routes will still work (backwards compatible) but any new urls will be generated using the new routing rules. Changes listed below: * made the URLs for some project tabs and project settings follow the new rails RESTful conventions of /collection/:id/subcollection/:sub_id * prettier URL for project roadmap * more nice project URLs * use GET for filtering form * prettified URLs used on issues tab * custom route for activity atom feeds * prettier repository urls * fixed broken route definition * fixed failing tests for issuecontroller that were hardcoding the url string * more RESTful routes for boards and messages * RESTful routes for wiki pages * RESTful routes for documents * moved old routes that are retained for compatibility to the bottom and grouped them together * added RESTful URIs for issues * RESTfulness for the news section * fixed route order * changed hardcoded URLs in tests * fixed badly written tests * fixed forgotten parameter in routes * changed hardcoded URLS to new scheme * changed project add url to the standard POST to collection * create new issue by POSTing to collection * changed hardcoded URLs in integrations tests * made project add form work again * restful routes for project deletion * prettier routes for project (un)archival * made routes table more readable * fixed note quoting * user routing * fixed bug * always sort by GET * Fixed: cross-project issue list should not show issues of projects for which the issue tracking module was disabled. * prettified URLs used on issues tab * urls for time log * fixed reply routing * eliminate revision query paremeter for diff and entry actions * fixed test failures with hard-coded urls * ensure ajax links always use get * refactored ajax link generation into separate method #1901 git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2317 e93f8b46-1217-0410-a6f0-8f06a7374b81
15 år sedan
Converted routing and urls to follow the Rails REST convention. Patch supplied by commits from Gerrit Kaiser on Github. Existing routes will still work (backwards compatible) but any new urls will be generated using the new routing rules. Changes listed below: * made the URLs for some project tabs and project settings follow the new rails RESTful conventions of /collection/:id/subcollection/:sub_id * prettier URL for project roadmap * more nice project URLs * use GET for filtering form * prettified URLs used on issues tab * custom route for activity atom feeds * prettier repository urls * fixed broken route definition * fixed failing tests for issuecontroller that were hardcoding the url string * more RESTful routes for boards and messages * RESTful routes for wiki pages * RESTful routes for documents * moved old routes that are retained for compatibility to the bottom and grouped them together * added RESTful URIs for issues * RESTfulness for the news section * fixed route order * changed hardcoded URLs in tests * fixed badly written tests * fixed forgotten parameter in routes * changed hardcoded URLS to new scheme * changed project add url to the standard POST to collection * create new issue by POSTing to collection * changed hardcoded URLs in integrations tests * made project add form work again * restful routes for project deletion * prettier routes for project (un)archival * made routes table more readable * fixed note quoting * user routing * fixed bug * always sort by GET * Fixed: cross-project issue list should not show issues of projects for which the issue tracking module was disabled. * prettified URLs used on issues tab * urls for time log * fixed reply routing * eliminate revision query paremeter for diff and entry actions * fixed test failures with hard-coded urls * ensure ajax links always use get * refactored ajax link generation into separate method #1901 git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2317 e93f8b46-1217-0410-a6f0-8f06a7374b81
15 år sedan
Converted routing and urls to follow the Rails REST convention. Patch supplied by commits from Gerrit Kaiser on Github. Existing routes will still work (backwards compatible) but any new urls will be generated using the new routing rules. Changes listed below: * made the URLs for some project tabs and project settings follow the new rails RESTful conventions of /collection/:id/subcollection/:sub_id * prettier URL for project roadmap * more nice project URLs * use GET for filtering form * prettified URLs used on issues tab * custom route for activity atom feeds * prettier repository urls * fixed broken route definition * fixed failing tests for issuecontroller that were hardcoding the url string * more RESTful routes for boards and messages * RESTful routes for wiki pages * RESTful routes for documents * moved old routes that are retained for compatibility to the bottom and grouped them together * added RESTful URIs for issues * RESTfulness for the news section * fixed route order * changed hardcoded URLs in tests * fixed badly written tests * fixed forgotten parameter in routes * changed hardcoded URLS to new scheme * changed project add url to the standard POST to collection * create new issue by POSTing to collection * changed hardcoded URLs in integrations tests * made project add form work again * restful routes for project deletion * prettier routes for project (un)archival * made routes table more readable * fixed note quoting * user routing * fixed bug * always sort by GET * Fixed: cross-project issue list should not show issues of projects for which the issue tracking module was disabled. * prettified URLs used on issues tab * urls for time log * fixed reply routing * eliminate revision query paremeter for diff and entry actions * fixed test failures with hard-coded urls * ensure ajax links always use get * refactored ajax link generation into separate method #1901 git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2317 e93f8b46-1217-0410-a6f0-8f06a7374b81
15 år sedan
Converted routing and urls to follow the Rails REST convention. Patch supplied by commits from Gerrit Kaiser on Github. Existing routes will still work (backwards compatible) but any new urls will be generated using the new routing rules. Changes listed below: * made the URLs for some project tabs and project settings follow the new rails RESTful conventions of /collection/:id/subcollection/:sub_id * prettier URL for project roadmap * more nice project URLs * use GET for filtering form * prettified URLs used on issues tab * custom route for activity atom feeds * prettier repository urls * fixed broken route definition * fixed failing tests for issuecontroller that were hardcoding the url string * more RESTful routes for boards and messages * RESTful routes for wiki pages * RESTful routes for documents * moved old routes that are retained for compatibility to the bottom and grouped them together * added RESTful URIs for issues * RESTfulness for the news section * fixed route order * changed hardcoded URLs in tests * fixed badly written tests * fixed forgotten parameter in routes * changed hardcoded URLS to new scheme * changed project add url to the standard POST to collection * create new issue by POSTing to collection * changed hardcoded URLs in integrations tests * made project add form work again * restful routes for project deletion * prettier routes for project (un)archival * made routes table more readable * fixed note quoting * user routing * fixed bug * always sort by GET * Fixed: cross-project issue list should not show issues of projects for which the issue tracking module was disabled. * prettified URLs used on issues tab * urls for time log * fixed reply routing * eliminate revision query paremeter for diff and entry actions * fixed test failures with hard-coded urls * ensure ajax links always use get * refactored ajax link generation into separate method #1901 git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2317 e93f8b46-1217-0410-a6f0-8f06a7374b81
15 år sedan
Converted routing and urls to follow the Rails REST convention. Patch supplied by commits from Gerrit Kaiser on Github. Existing routes will still work (backwards compatible) but any new urls will be generated using the new routing rules. Changes listed below: * made the URLs for some project tabs and project settings follow the new rails RESTful conventions of /collection/:id/subcollection/:sub_id * prettier URL for project roadmap * more nice project URLs * use GET for filtering form * prettified URLs used on issues tab * custom route for activity atom feeds * prettier repository urls * fixed broken route definition * fixed failing tests for issuecontroller that were hardcoding the url string * more RESTful routes for boards and messages * RESTful routes for wiki pages * RESTful routes for documents * moved old routes that are retained for compatibility to the bottom and grouped them together * added RESTful URIs for issues * RESTfulness for the news section * fixed route order * changed hardcoded URLs in tests * fixed badly written tests * fixed forgotten parameter in routes * changed hardcoded URLS to new scheme * changed project add url to the standard POST to collection * create new issue by POSTing to collection * changed hardcoded URLs in integrations tests * made project add form work again * restful routes for project deletion * prettier routes for project (un)archival * made routes table more readable * fixed note quoting * user routing * fixed bug * always sort by GET * Fixed: cross-project issue list should not show issues of projects for which the issue tracking module was disabled. * prettified URLs used on issues tab * urls for time log * fixed reply routing * eliminate revision query paremeter for diff and entry actions * fixed test failures with hard-coded urls * ensure ajax links always use get * refactored ajax link generation into separate method #1901 git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2317 e93f8b46-1217-0410-a6f0-8f06a7374b81
15 år sedan
Converted routing and urls to follow the Rails REST convention. Patch supplied by commits from Gerrit Kaiser on Github. Existing routes will still work (backwards compatible) but any new urls will be generated using the new routing rules. Changes listed below: * made the URLs for some project tabs and project settings follow the new rails RESTful conventions of /collection/:id/subcollection/:sub_id * prettier URL for project roadmap * more nice project URLs * use GET for filtering form * prettified URLs used on issues tab * custom route for activity atom feeds * prettier repository urls * fixed broken route definition * fixed failing tests for issuecontroller that were hardcoding the url string * more RESTful routes for boards and messages * RESTful routes for wiki pages * RESTful routes for documents * moved old routes that are retained for compatibility to the bottom and grouped them together * added RESTful URIs for issues * RESTfulness for the news section * fixed route order * changed hardcoded URLs in tests * fixed badly written tests * fixed forgotten parameter in routes * changed hardcoded URLS to new scheme * changed project add url to the standard POST to collection * create new issue by POSTing to collection * changed hardcoded URLs in integrations tests * made project add form work again * restful routes for project deletion * prettier routes for project (un)archival * made routes table more readable * fixed note quoting * user routing * fixed bug * always sort by GET * Fixed: cross-project issue list should not show issues of projects for which the issue tracking module was disabled. * prettified URLs used on issues tab * urls for time log * fixed reply routing * eliminate revision query paremeter for diff and entry actions * fixed test failures with hard-coded urls * ensure ajax links always use get * refactored ajax link generation into separate method #1901 git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2317 e93f8b46-1217-0410-a6f0-8f06a7374b81
15 år sedan
Converted routing and urls to follow the Rails REST convention. Patch supplied by commits from Gerrit Kaiser on Github. Existing routes will still work (backwards compatible) but any new urls will be generated using the new routing rules. Changes listed below: * made the URLs for some project tabs and project settings follow the new rails RESTful conventions of /collection/:id/subcollection/:sub_id * prettier URL for project roadmap * more nice project URLs * use GET for filtering form * prettified URLs used on issues tab * custom route for activity atom feeds * prettier repository urls * fixed broken route definition * fixed failing tests for issuecontroller that were hardcoding the url string * more RESTful routes for boards and messages * RESTful routes for wiki pages * RESTful routes for documents * moved old routes that are retained for compatibility to the bottom and grouped them together * added RESTful URIs for issues * RESTfulness for the news section * fixed route order * changed hardcoded URLs in tests * fixed badly written tests * fixed forgotten parameter in routes * changed hardcoded URLS to new scheme * changed project add url to the standard POST to collection * create new issue by POSTing to collection * changed hardcoded URLs in integrations tests * made project add form work again * restful routes for project deletion * prettier routes for project (un)archival * made routes table more readable * fixed note quoting * user routing * fixed bug * always sort by GET * Fixed: cross-project issue list should not show issues of projects for which the issue tracking module was disabled. * prettified URLs used on issues tab * urls for time log * fixed reply routing * eliminate revision query paremeter for diff and entry actions * fixed test failures with hard-coded urls * ensure ajax links always use get * refactored ajax link generation into separate method #1901 git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2317 e93f8b46-1217-0410-a6f0-8f06a7374b81
15 år sedan
Converted routing and urls to follow the Rails REST convention. Patch supplied by commits from Gerrit Kaiser on Github. Existing routes will still work (backwards compatible) but any new urls will be generated using the new routing rules. Changes listed below: * made the URLs for some project tabs and project settings follow the new rails RESTful conventions of /collection/:id/subcollection/:sub_id * prettier URL for project roadmap * more nice project URLs * use GET for filtering form * prettified URLs used on issues tab * custom route for activity atom feeds * prettier repository urls * fixed broken route definition * fixed failing tests for issuecontroller that were hardcoding the url string * more RESTful routes for boards and messages * RESTful routes for wiki pages * RESTful routes for documents * moved old routes that are retained for compatibility to the bottom and grouped them together * added RESTful URIs for issues * RESTfulness for the news section * fixed route order * changed hardcoded URLs in tests * fixed badly written tests * fixed forgotten parameter in routes * changed hardcoded URLS to new scheme * changed project add url to the standard POST to collection * create new issue by POSTing to collection * changed hardcoded URLs in integrations tests * made project add form work again * restful routes for project deletion * prettier routes for project (un)archival * made routes table more readable * fixed note quoting * user routing * fixed bug * always sort by GET * Fixed: cross-project issue list should not show issues of projects for which the issue tracking module was disabled. * prettified URLs used on issues tab * urls for time log * fixed reply routing * eliminate revision query paremeter for diff and entry actions * fixed test failures with hard-coded urls * ensure ajax links always use get * refactored ajax link generation into separate method #1901 git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2317 e93f8b46-1217-0410-a6f0-8f06a7374b81
15 år sedan
Converted routing and urls to follow the Rails REST convention. Patch supplied by commits from Gerrit Kaiser on Github. Existing routes will still work (backwards compatible) but any new urls will be generated using the new routing rules. Changes listed below: * made the URLs for some project tabs and project settings follow the new rails RESTful conventions of /collection/:id/subcollection/:sub_id * prettier URL for project roadmap * more nice project URLs * use GET for filtering form * prettified URLs used on issues tab * custom route for activity atom feeds * prettier repository urls * fixed broken route definition * fixed failing tests for issuecontroller that were hardcoding the url string * more RESTful routes for boards and messages * RESTful routes for wiki pages * RESTful routes for documents * moved old routes that are retained for compatibility to the bottom and grouped them together * added RESTful URIs for issues * RESTfulness for the news section * fixed route order * changed hardcoded URLs in tests * fixed badly written tests * fixed forgotten parameter in routes * changed hardcoded URLS to new scheme * changed project add url to the standard POST to collection * create new issue by POSTing to collection * changed hardcoded URLs in integrations tests * made project add form work again * restful routes for project deletion * prettier routes for project (un)archival * made routes table more readable * fixed note quoting * user routing * fixed bug * always sort by GET * Fixed: cross-project issue list should not show issues of projects for which the issue tracking module was disabled. * prettified URLs used on issues tab * urls for time log * fixed reply routing * eliminate revision query paremeter for diff and entry actions * fixed test failures with hard-coded urls * ensure ajax links always use get * refactored ajax link generation into separate method #1901 git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2317 e93f8b46-1217-0410-a6f0-8f06a7374b81
15 år sedan
Converted routing and urls to follow the Rails REST convention. Patch supplied by commits from Gerrit Kaiser on Github. Existing routes will still work (backwards compatible) but any new urls will be generated using the new routing rules. Changes listed below: * made the URLs for some project tabs and project settings follow the new rails RESTful conventions of /collection/:id/subcollection/:sub_id * prettier URL for project roadmap * more nice project URLs * use GET for filtering form * prettified URLs used on issues tab * custom route for activity atom feeds * prettier repository urls * fixed broken route definition * fixed failing tests for issuecontroller that were hardcoding the url string * more RESTful routes for boards and messages * RESTful routes for wiki pages * RESTful routes for documents * moved old routes that are retained for compatibility to the bottom and grouped them together * added RESTful URIs for issues * RESTfulness for the news section * fixed route order * changed hardcoded URLs in tests * fixed badly written tests * fixed forgotten parameter in routes * changed hardcoded URLS to new scheme * changed project add url to the standard POST to collection * create new issue by POSTing to collection * changed hardcoded URLs in integrations tests * made project add form work again * restful routes for project deletion * prettier routes for project (un)archival * made routes table more readable * fixed note quoting * user routing * fixed bug * always sort by GET * Fixed: cross-project issue list should not show issues of projects for which the issue tracking module was disabled. * prettified URLs used on issues tab * urls for time log * fixed reply routing * eliminate revision query paremeter for diff and entry actions * fixed test failures with hard-coded urls * ensure ajax links always use get * refactored ajax link generation into separate method #1901 git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2317 e93f8b46-1217-0410-a6f0-8f06a7374b81
15 år sedan
Converted routing and urls to follow the Rails REST convention. Patch supplied by commits from Gerrit Kaiser on Github. Existing routes will still work (backwards compatible) but any new urls will be generated using the new routing rules. Changes listed below: * made the URLs for some project tabs and project settings follow the new rails RESTful conventions of /collection/:id/subcollection/:sub_id * prettier URL for project roadmap * more nice project URLs * use GET for filtering form * prettified URLs used on issues tab * custom route for activity atom feeds * prettier repository urls * fixed broken route definition * fixed failing tests for issuecontroller that were hardcoding the url string * more RESTful routes for boards and messages * RESTful routes for wiki pages * RESTful routes for documents * moved old routes that are retained for compatibility to the bottom and grouped them together * added RESTful URIs for issues * RESTfulness for the news section * fixed route order * changed hardcoded URLs in tests * fixed badly written tests * fixed forgotten parameter in routes * changed hardcoded URLS to new scheme * changed project add url to the standard POST to collection * create new issue by POSTing to collection * changed hardcoded URLs in integrations tests * made project add form work again * restful routes for project deletion * prettier routes for project (un)archival * made routes table more readable * fixed note quoting * user routing * fixed bug * always sort by GET * Fixed: cross-project issue list should not show issues of projects for which the issue tracking module was disabled. * prettified URLs used on issues tab * urls for time log * fixed reply routing * eliminate revision query paremeter for diff and entry actions * fixed test failures with hard-coded urls * ensure ajax links always use get * refactored ajax link generation into separate method #1901 git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2317 e93f8b46-1217-0410-a6f0-8f06a7374b81
15 år sedan
Converted routing and urls to follow the Rails REST convention. Patch supplied by commits from Gerrit Kaiser on Github. Existing routes will still work (backwards compatible) but any new urls will be generated using the new routing rules. Changes listed below: * made the URLs for some project tabs and project settings follow the new rails RESTful conventions of /collection/:id/subcollection/:sub_id * prettier URL for project roadmap * more nice project URLs * use GET for filtering form * prettified URLs used on issues tab * custom route for activity atom feeds * prettier repository urls * fixed broken route definition * fixed failing tests for issuecontroller that were hardcoding the url string * more RESTful routes for boards and messages * RESTful routes for wiki pages * RESTful routes for documents * moved old routes that are retained for compatibility to the bottom and grouped them together * added RESTful URIs for issues * RESTfulness for the news section * fixed route order * changed hardcoded URLs in tests * fixed badly written tests * fixed forgotten parameter in routes * changed hardcoded URLS to new scheme * changed project add url to the standard POST to collection * create new issue by POSTing to collection * changed hardcoded URLs in integrations tests * made project add form work again * restful routes for project deletion * prettier routes for project (un)archival * made routes table more readable * fixed note quoting * user routing * fixed bug * always sort by GET * Fixed: cross-project issue list should not show issues of projects for which the issue tracking module was disabled. * prettified URLs used on issues tab * urls for time log * fixed reply routing * eliminate revision query paremeter for diff and entry actions * fixed test failures with hard-coded urls * ensure ajax links always use get * refactored ajax link generation into separate method #1901 git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@2317 e93f8b46-1217-0410-a6f0-8f06a7374b81
15 år sedan
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733
  1. # frozen_string_literal: true
  2. # Redmine - project management software
  3. # Copyright (C) 2006- Jean-Philippe Lang
  4. #
  5. # This program is free software; you can redistribute it and/or
  6. # modify it under the terms of the GNU General Public License
  7. # as published by the Free Software Foundation; either version 2
  8. # of the License, or (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with this program; if not, write to the Free Software
  17. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  18. require_relative '../test_helper'
  19. class IssuesControllerTest < Redmine::ControllerTest
  20. fixtures :projects,
  21. :users, :email_addresses, :user_preferences,
  22. :roles,
  23. :members,
  24. :member_roles,
  25. :issues,
  26. :issue_statuses,
  27. :issue_relations,
  28. :versions,
  29. :trackers,
  30. :projects_trackers,
  31. :issue_categories,
  32. :enabled_modules,
  33. :enumerations,
  34. :attachments,
  35. :workflows,
  36. :custom_fields,
  37. :custom_values,
  38. :custom_fields_projects,
  39. :custom_fields_trackers,
  40. :time_entries,
  41. :journals,
  42. :journal_details,
  43. :queries,
  44. :repositories,
  45. :changesets,
  46. :watchers, :groups_users
  47. include Redmine::I18n
  48. def setup
  49. User.current = nil
  50. end
  51. def test_index
  52. with_settings :default_language => "en" do
  53. get :index
  54. assert_response :success
  55. # links to visible issues
  56. assert_select 'a[href="/issues/1"]', :text => /Cannot print recipes/
  57. assert_select 'a[href="/issues/5"]', :text => /Subproject issue/
  58. # private projects hidden
  59. assert_select 'a[href="/issues/6"]', 0
  60. assert_select 'a[href="/issues/4"]', 0
  61. # project column
  62. assert_select 'th', :text => /Project/
  63. end
  64. end
  65. def test_index_should_not_list_issues_when_module_disabled
  66. EnabledModule.where("name = 'issue_tracking' AND project_id = 1").delete_all
  67. get :index
  68. assert_response :success
  69. assert_select 'a[href="/issues/1"]', 0
  70. assert_select 'a[href="/issues/5"]', :text => /Subproject issue/
  71. end
  72. def test_index_should_list_visible_issues_only
  73. get(:index, :params => {:per_page => 100})
  74. assert_response :success
  75. Issue.open.each do |issue|
  76. assert_select "tr#issue-#{issue.id}", issue.visible? ? 1 : 0
  77. end
  78. end
  79. def test_index_with_project
  80. with_settings :display_subprojects_issues => '0' do
  81. get(:index, :params => {:project_id => 1})
  82. assert_response :success
  83. # query form
  84. assert_select 'form#query_form' do
  85. assert_select 'div#query_form_with_buttons.hide-when-print' do
  86. assert_select 'div#query_form_content' do
  87. assert_select 'fieldset#filters.collapsible'
  88. assert_select 'fieldset#options'
  89. end
  90. assert_select 'p.buttons'
  91. end
  92. end
  93. assert_select 'a[href="/issues/1"]', :text => /Cannot print recipes/
  94. assert_select 'a[href="/issues/5"]', 0
  95. end
  96. end
  97. def test_index_with_project_and_subprojects
  98. with_settings :display_subprojects_issues => '1' do
  99. get(:index, :params => {:project_id => 1})
  100. assert_response :success
  101. assert_select 'a[href="/issues/1"]', :text => /Cannot print recipes/
  102. assert_select 'a[href="/issues/5"]', :text => /Subproject issue/
  103. assert_select 'a[href="/issues/6"]', 0
  104. end
  105. end
  106. def test_index_should_list_issues_of_closed_subprojects
  107. @request.session[:user_id] = 1
  108. project = Project.find(1)
  109. with_settings :display_subprojects_issues => '1' do
  110. # One of subprojects is closed
  111. Project.find_by(:identifier => 'subproject1').close
  112. get(:index, :params => {:project_id => project.id})
  113. assert_response :success
  114. assert_equal 10, issues_in_list.count
  115. # All subprojects are closed
  116. project.descendants.each(&:close)
  117. get(:index, :params => {:project_id => project.id})
  118. assert_response :success
  119. assert_equal 10, issues_in_list.count
  120. end
  121. end
  122. def test_index_with_subproject_filter_should_not_exclude_closed_subprojects_issues
  123. subproject1 = Project.find(3)
  124. subproject2 = Project.find(4)
  125. subproject1.close
  126. with_settings :display_subprojects_issues => '1' do
  127. get(
  128. :index,
  129. :params => {
  130. :project_id => 1,
  131. :set_filter => 1,
  132. :f => ['subproject_id'],
  133. :op => {'subproject_id' => '!'},
  134. :v => {'subproject_id' => [subproject2.id.to_s]},
  135. :c => ['project']
  136. }
  137. )
  138. end
  139. assert_response :success
  140. column_values = columns_values_in_list('project')
  141. assert_includes column_values, subproject1.name
  142. assert_equal 9, column_values.size
  143. end
  144. def test_index_with_project_and_subprojects_should_show_private_subprojects_with_permission
  145. @request.session[:user_id] = 2
  146. with_settings :display_subprojects_issues => '1' do
  147. get(:index, :params => {:project_id => 1})
  148. assert_response :success
  149. assert_select 'a[href="/issues/1"]', :text => /Cannot print recipes/
  150. assert_select 'a[href="/issues/5"]', :text => /Subproject issue/
  151. assert_select 'a[href="/issues/6"]', :text => /Issue of a private subproject/
  152. end
  153. end
  154. def test_index_with_project_and_default_filter
  155. get(
  156. :index,
  157. :params => {
  158. :project_id => 1,
  159. :set_filter => 1
  160. }
  161. )
  162. assert_response :success
  163. # default filter
  164. assert_query_filters [['status_id', 'o', '']]
  165. end
  166. def test_index_with_project_and_filter
  167. get(
  168. :index,
  169. :params => {
  170. :project_id => 1,
  171. :set_filter => 1,
  172. :f => ['tracker_id'],
  173. :op => {
  174. 'tracker_id' => '='
  175. },
  176. :v => {
  177. 'tracker_id' => ['1']
  178. }
  179. }
  180. )
  181. assert_response :success
  182. assert_query_filters [['tracker_id', '=', '1']]
  183. end
  184. def test_index_with_short_filters
  185. to_test = {
  186. 'status_id' => {
  187. 'o' => {:op => 'o', :values => ['']},
  188. 'c' => {:op => 'c', :values => ['']},
  189. '7' => {:op => '=', :values => ['7']},
  190. '7|3|4' => {:op => '=', :values => ['7', '3', '4']},
  191. '=7' => {:op => '=', :values => ['7']},
  192. '!3' => {:op => '!', :values => ['3']},
  193. '!7|3|4' => {:op => '!', :values => ['7', '3', '4']}
  194. },
  195. 'subject' => {
  196. 'This is a subject' => {:op => '=', :values => ['This is a subject']},
  197. 'o' => {:op => '=', :values => ['o']},
  198. '~This is part of a subject' => {:op => '~', :values => ['This is part of a subject']},
  199. '!~This is part of a subject' => {:op => '!~', :values => ['This is part of a subject']}
  200. },
  201. 'tracker_id' => {
  202. '3' => {:op => '=', :values => ['3']},
  203. '=3' => {:op => '=', :values => ['3']}
  204. },
  205. 'start_date' => {
  206. '2011-10-12' => {:op => '=', :values => ['2011-10-12']},
  207. '=2011-10-12' => {:op => '=', :values => ['2011-10-12']},
  208. '>=2011-10-12' => {:op => '>=', :values => ['2011-10-12']},
  209. '<=2011-10-12' => {:op => '<=', :values => ['2011-10-12']},
  210. '><2011-10-01|2011-10-30' => {:op => '><', :values => ['2011-10-01', '2011-10-30']},
  211. '<t+2' => {:op => '<t+', :values => ['2']},
  212. '>t+2' => {:op => '>t+', :values => ['2']},
  213. 't+2' => {:op => 't+', :values => ['2']},
  214. 't' => {:op => 't', :values => ['']},
  215. 'w' => {:op => 'w', :values => ['']},
  216. '>t-2' => {:op => '>t-', :values => ['2']},
  217. '<t-2' => {:op => '<t-', :values => ['2']},
  218. 't-2' => {:op => 't-', :values => ['2']}
  219. },
  220. 'created_on' => {
  221. '>=2011-10-12' => {:op => '>=', :values => ['2011-10-12']},
  222. '<t-2' => {:op => '<t-', :values => ['2']},
  223. '>t-2' => {:op => '>t-', :values => ['2']},
  224. 't-2' => {:op => 't-', :values => ['2']}
  225. },
  226. 'cf_1' => {
  227. 'c' => {:op => '=', :values => ['c']},
  228. '!c' => {:op => '!', :values => ['c']},
  229. '!*' => {:op => '!*', :values => ['']},
  230. '*' => {:op => '*', :values => ['']}
  231. },
  232. 'estimated_hours' => {
  233. '=13.4' => {:op => '=', :values => ['13.4']},
  234. '>=45' => {:op => '>=', :values => ['45']},
  235. '<=125' => {:op => '<=', :values => ['125']},
  236. '><10.5|20.5' => {:op => '><', :values => ['10.5', '20.5']},
  237. '!*' => {:op => '!*', :values => ['']},
  238. '*' => {:op => '*', :values => ['']}
  239. }
  240. }
  241. default_filter = {'status_id' => {:operator => 'o', :values => ['']}}
  242. to_test.each do |field, expression_and_expected|
  243. expression_and_expected.each do |filter_expression, expected|
  244. get(:index, :params => {:set_filter => 1, field => filter_expression})
  245. assert_response :success
  246. expected_with_default =
  247. default_filter.
  248. merge({field => {:operator => expected[:op], :values => expected[:values]}})
  249. assert_query_filters(
  250. expected_with_default.map {|f, v| [f, v[:operator], v[:values]]}
  251. )
  252. end
  253. end
  254. end
  255. def test_index_with_project_and_empty_filters
  256. get(
  257. :index,
  258. :params => {
  259. :project_id => 1,
  260. :set_filter => 1,
  261. :fields => ['']
  262. }
  263. )
  264. assert_response :success
  265. # no filter
  266. assert_query_filters []
  267. end
  268. def test_index_with_project_custom_field_filter
  269. field =
  270. ProjectCustomField.
  271. create!(:name => 'Client', :is_filter => true, :field_format => 'string')
  272. CustomValue.create!(:custom_field => field, :customized => Project.find(3), :value => 'Foo')
  273. CustomValue.create!(:custom_field => field, :customized => Project.find(5), :value => 'Foo')
  274. filter_name = "project.cf_#{field.id}"
  275. @request.session[:user_id] = 1
  276. get(
  277. :index,
  278. :params => {
  279. :set_filter => 1,
  280. :f => [filter_name],
  281. :op => {
  282. filter_name => '='
  283. },
  284. :v => {
  285. filter_name => ['Foo']
  286. },
  287. :c => ['project']
  288. }
  289. )
  290. assert_response :success
  291. assert_equal [3, 5], issues_in_list.map(&:project_id).uniq.sort
  292. end
  293. def test_index_with_project_status_filter
  294. project = Project.find(2)
  295. project.close
  296. project.save
  297. get(
  298. :index,
  299. :params => {
  300. :set_filter => 1,
  301. :f => ['project.status'],
  302. :op => {'project.status' => '='},
  303. :v => {'project.status' => ['1']}
  304. }
  305. )
  306. assert_response :success
  307. issues = issues_in_list.map(&:id).uniq.sort
  308. assert_include 1, issues
  309. assert_not_include 4, issues
  310. end
  311. def test_index_with_query
  312. get(
  313. :index,
  314. :params => {
  315. :project_id => 1,
  316. :query_id => 5
  317. }
  318. )
  319. assert_response :success
  320. assert_select '#sidebar .queries' do
  321. # assert only query is selected in sidebar
  322. assert_select 'a.query.selected', 1
  323. # assert link properties
  324. assert_select(
  325. 'a.query.selected[href=?]',
  326. '/projects/ecookbook/issues?query_id=5',
  327. :text => "Open issues by priority and tracker"
  328. )
  329. # assert only one clear link exists
  330. assert_select 'a.icon-clear-query', 1
  331. # assert clear link properties
  332. assert_select(
  333. 'a.icon-clear-query[title=?][href=?]',
  334. 'Clear',
  335. '/projects/ecookbook/issues?set_filter=1&sort=',
  336. 1
  337. )
  338. end
  339. end
  340. def test_index_with_query_grouped_by_tracker
  341. get(
  342. :index,
  343. :params => {
  344. :project_id => 1,
  345. :query_id => 6
  346. }
  347. )
  348. assert_response :success
  349. assert_select 'tr.group span.count'
  350. end
  351. def test_index_with_query_grouped_and_sorted_by_category
  352. get(
  353. :index,
  354. :params => {
  355. :project_id => 1,
  356. :set_filter => 1,
  357. :group_by => "category",
  358. :sort => "category"
  359. }
  360. )
  361. assert_response :success
  362. assert_select 'tr.group span.count'
  363. end
  364. def test_index_with_query_grouped_and_sorted_by_fixed_version
  365. get(
  366. :index,
  367. :params => {
  368. :project_id => 1,
  369. :set_filter => 1,
  370. :group_by => "fixed_version",
  371. :sort => "fixed_version"
  372. }
  373. )
  374. assert_response :success
  375. assert_select 'tr.group span.count'
  376. end
  377. def test_index_with_query_grouped_and_sorted_by_fixed_version_in_reverse_order
  378. get(
  379. :index,
  380. :params => {
  381. :project_id => 1,
  382. :set_filter => 1,
  383. :group_by => "fixed_version",
  384. :sort => "fixed_version:desc"
  385. }
  386. )
  387. assert_response :success
  388. assert_select 'tr.group span.count'
  389. end
  390. def test_index_grouped_by_due_date
  391. set_tmp_attachments_directory
  392. Issue.destroy_all
  393. Issue.generate!(:due_date => '2018-08-10')
  394. Issue.generate!(:due_date => '2018-08-10')
  395. Issue.generate!
  396. get(
  397. :index,
  398. :params => {
  399. :set_filter => 1,
  400. :group_by => "due_date"
  401. }
  402. )
  403. assert_response :success
  404. assert_select 'tr.group span.name', :value => '2018-08-10' do
  405. assert_select '~ span.count', :value => '2'
  406. end
  407. assert_select 'tr.group span.name', :value => '(blank)' do
  408. assert_select '~ span.count', :value => '1'
  409. end
  410. end
  411. def test_index_grouped_by_created_on_if_time_zone_is_utc
  412. # TODO: test fails with mysql
  413. skip if mysql?
  414. skip unless IssueQuery.new.groupable_columns.detect {|c| c.name == :created_on}
  415. @request.session[:user_id] = 2
  416. User.find(2).pref.update(time_zone: 'UTC')
  417. get(
  418. :index,
  419. :params => {
  420. :set_filter => 1,
  421. :group_by => 'created_on'
  422. }
  423. )
  424. assert_response :success
  425. assert_select 'tr.group span.name', :text => '07/19/2006' do
  426. assert_select '+ span.count', :text => '2'
  427. end
  428. end
  429. def test_index_grouped_by_created_on_if_time_zone_is_nil
  430. skip unless IssueQuery.new.groupable_columns.detect {|c| c.name == :created_on}
  431. current_user = User.find(2)
  432. @request.session[:user_id] = current_user.id
  433. current_user.pref.update(time_zone: nil)
  434. get(
  435. :index,
  436. :params => {
  437. :set_filter => 1,
  438. :group_by => 'created_on'
  439. }
  440. )
  441. assert_response :success
  442. # group_name depends on localtime
  443. group_name = format_date(Issue.second.created_on.localtime)
  444. assert_select 'tr.group span.name', :text => group_name do
  445. assert_select '+ span.count', :text => '2'
  446. end
  447. end
  448. def test_index_grouped_by_created_on_as_pdf
  449. skip unless IssueQuery.new.groupable_columns.detect {|c| c.name == :created_on}
  450. get(
  451. :index,
  452. :params => {
  453. :set_filter => 1,
  454. :group_by => 'created_on',
  455. :format => 'pdf'
  456. }
  457. )
  458. assert_response :success
  459. assert_equal 'application/pdf', response.media_type
  460. end
  461. def test_index_with_query_grouped_by_list_custom_field
  462. get(
  463. :index,
  464. :params => {
  465. :project_id => 1,
  466. :query_id => 9
  467. }
  468. )
  469. assert_response :success
  470. assert_select 'tr.group span.count'
  471. end
  472. def test_index_with_query_grouped_by_key_value_custom_field
  473. cf = IssueCustomField.
  474. create!(
  475. :name => 'Key',
  476. :is_for_all => true,
  477. :tracker_ids => [1, 2, 3],
  478. :field_format => 'enumeration'
  479. )
  480. cf.enumerations << valueb = CustomFieldEnumeration.new(:name => 'Value B', :position => 1)
  481. cf.enumerations << valuea = CustomFieldEnumeration.new(:name => 'Value A', :position => 2)
  482. CustomValue.create!(:custom_field => cf, :customized => Issue.find(1), :value => valueb.id)
  483. CustomValue.create!(:custom_field => cf, :customized => Issue.find(2), :value => valueb.id)
  484. CustomValue.create!(:custom_field => cf, :customized => Issue.find(3), :value => valuea.id)
  485. CustomValue.create!(:custom_field => cf, :customized => Issue.find(5), :value => '')
  486. get(
  487. :index,
  488. :params => {
  489. :project_id => 1,
  490. :set_filter => 1,
  491. :group_by => "cf_#{cf.id}"
  492. }
  493. )
  494. assert_response :success
  495. assert_select 'tr.group', 3
  496. assert_select 'tr.group' do
  497. assert_select 'span.name', :text => 'Value B'
  498. assert_select 'span.count', :text => '2'
  499. end
  500. assert_select 'tr.group' do
  501. assert_select 'span.name', :text => 'Value A'
  502. assert_select 'span.count', :text => '1'
  503. end
  504. end
  505. def test_index_with_query_grouped_by_user_custom_field
  506. cf = IssueCustomField.
  507. create!(
  508. :name => 'User',
  509. :is_for_all => true,
  510. :tracker_ids => [1, 2, 3],
  511. :field_format => 'user'
  512. )
  513. CustomValue.create!(:custom_field => cf, :customized => Issue.find(1), :value => '2')
  514. CustomValue.create!(:custom_field => cf, :customized => Issue.find(2), :value => '3')
  515. CustomValue.create!(:custom_field => cf, :customized => Issue.find(3), :value => '3')
  516. CustomValue.create!(:custom_field => cf, :customized => Issue.find(5), :value => '')
  517. get(
  518. :index,
  519. :params => {
  520. :project_id => 1,
  521. :set_filter => 1,
  522. :group_by => "cf_#{cf.id}"
  523. }
  524. )
  525. assert_response :success
  526. assert_select 'tr.group', 3
  527. assert_select 'tr.group' do
  528. assert_select 'a', :text => 'John Smith'
  529. assert_select 'span.count', :text => '1'
  530. end
  531. assert_select 'tr.group' do
  532. assert_select 'a', :text => 'Dave Lopper'
  533. assert_select 'span.count', :text => '2'
  534. end
  535. end
  536. def test_index_grouped_by_boolean_custom_field_should_distinguish_blank_and_false_values
  537. cf = IssueCustomField.
  538. create!(
  539. :name => 'Bool',
  540. :is_for_all => true,
  541. :tracker_ids => [1, 2, 3],
  542. :field_format => 'bool'
  543. )
  544. CustomValue.create!(:custom_field => cf, :customized => Issue.find(1), :value => '1')
  545. CustomValue.create!(:custom_field => cf, :customized => Issue.find(2), :value => '0')
  546. CustomValue.create!(:custom_field => cf, :customized => Issue.find(3), :value => '')
  547. with_settings :default_language => 'en' do
  548. get(
  549. :index,
  550. :params => {
  551. :project_id => 1,
  552. :set_filter => 1,
  553. :group_by => "cf_#{cf.id}"
  554. }
  555. )
  556. assert_response :success
  557. end
  558. assert_select 'tr.group', 3
  559. assert_select 'tr.group', :text => /Yes/
  560. assert_select 'tr.group', :text => /No/
  561. assert_select 'tr.group', :text => /blank/
  562. end
  563. def test_index_grouped_by_boolean_custom_field_with_false_group_in_first_position_should_show_the_group
  564. cf = IssueCustomField.
  565. create!(
  566. :name => 'Bool',
  567. :is_for_all => true,
  568. :tracker_ids => [1, 2, 3],
  569. :field_format => 'bool',
  570. :is_filter => true
  571. )
  572. CustomValue.create!(:custom_field => cf, :customized => Issue.find(1), :value => '0')
  573. CustomValue.create!(:custom_field => cf, :customized => Issue.find(2), :value => '0')
  574. with_settings :default_language => 'en' do
  575. get(
  576. :index,
  577. :params => {
  578. :project_id => 1,
  579. :set_filter => 1, "cf_#{cf.id}" => "*",
  580. :group_by => "cf_#{cf.id}"
  581. }
  582. )
  583. assert_response :success
  584. end
  585. assert_equal [1, 2], issues_in_list.map(&:id).sort
  586. assert_select 'tr.group', 1
  587. assert_select 'tr.group', :text => /No/
  588. end
  589. def test_index_with_query_grouped_by_tracker_in_normal_order
  590. 3.times {|i| Issue.generate!(:tracker_id => (i + 1))}
  591. get(
  592. :index,
  593. :params => {
  594. :set_filter => 1,
  595. :group_by => 'tracker',
  596. :sort => 'id:desc'
  597. }
  598. )
  599. assert_response :success
  600. assert_equal ["Bug", "Feature request", "Support request"],
  601. css_select("tr.issue td.tracker").map(&:text).uniq
  602. end
  603. def test_index_with_query_grouped_by_tracker_in_reverse_order
  604. 3.times {|i| Issue.generate!(:tracker_id => (i + 1))}
  605. get(
  606. :index,
  607. :params => {
  608. :set_filter => 1,
  609. :group_by => 'tracker',
  610. :c => ['tracker', 'subject'],
  611. :sort => 'id:desc,tracker:desc'
  612. }
  613. )
  614. assert_response :success
  615. assert_equal ["Bug", "Feature request", "Support request"].reverse,
  616. css_select("tr.issue td.tracker").map(&:text).uniq
  617. end
  618. def test_index_with_query_id_and_project_id_should_set_session_query
  619. get(
  620. :index,
  621. :params => {
  622. :project_id => 1,
  623. :query_id => 4
  624. }
  625. )
  626. assert_response :success
  627. assert_kind_of Hash, session[:issue_query]
  628. assert_equal 4, session[:issue_query][:id]
  629. assert_equal 1, session[:issue_query][:project_id]
  630. end
  631. def test_index_with_invalid_query_id_should_respond_404
  632. get(
  633. :index,
  634. :params => {
  635. :project_id => 1,
  636. :query_id => 999
  637. }
  638. )
  639. assert_response 404
  640. end
  641. def test_index_with_cross_project_query_in_session_should_show_project_issues
  642. q = IssueQuery.
  643. create!(
  644. :name => "cross_project_query", :user_id => 2,
  645. :project => nil, :column_names => ['project']
  646. )
  647. @request.session[:issue_query] = {:id => q.id, :project_id => 1}
  648. with_settings :display_subprojects_issues => '0' do
  649. get(:index, :params => {:project_id => 1})
  650. end
  651. assert_response :success
  652. assert_select 'h2', :text => q.name
  653. assert_equal ["eCookbook"], css_select("tr.issue td.project").map(&:text).uniq
  654. end
  655. def test_private_query_should_not_be_available_to_other_users
  656. q = IssueQuery.
  657. create!(
  658. :name => "private", :user => User.find(2),
  659. :visibility => IssueQuery::VISIBILITY_PRIVATE,
  660. :project => nil
  661. )
  662. @request.session[:user_id] = 3
  663. get(:index, :params => {:query_id => q.id})
  664. assert_response 403
  665. end
  666. def test_private_query_should_be_available_to_its_user
  667. q = IssueQuery.
  668. create!(
  669. :name => "private", :user => User.find(2),
  670. :visibility => IssueQuery::VISIBILITY_PRIVATE,
  671. :project => nil
  672. )
  673. @request.session[:user_id] = 2
  674. get(:index, :params => {:query_id => q.id})
  675. assert_response :success
  676. end
  677. def test_public_query_should_be_available_to_other_users
  678. q = IssueQuery.
  679. create!(
  680. :name => "public", :user => User.find(2),
  681. :visibility => IssueQuery::VISIBILITY_PUBLIC,
  682. :project => nil
  683. )
  684. @request.session[:user_id] = 3
  685. get(:index, :params => {:query_id => q.id})
  686. assert_response :success
  687. end
  688. def test_index_should_omit_page_param_in_export_links
  689. get(:index, :params => {:page => 2})
  690. assert_response :success
  691. assert_select 'a.atom[href="/issues.atom"]'
  692. assert_select 'a.csv[href="/issues.csv"]'
  693. assert_select 'a.pdf[href="/issues.pdf"]'
  694. assert_select 'form#csv-export-form[action="/issues.csv"]'
  695. end
  696. def test_index_should_not_warn_when_not_exceeding_export_limit
  697. with_settings :issues_export_limit => 200 do
  698. get :index
  699. assert_select '#csv-export-options p.icon-warning', 0
  700. end
  701. end
  702. def test_index_should_warn_when_exceeding_export_limit
  703. with_settings :issues_export_limit => 2 do
  704. get :index
  705. assert_select '#csv-export-options p.icon-warning', :text => %r{limit: 2}
  706. end
  707. end
  708. def test_index_should_include_query_params_as_hidden_fields_in_csv_export_form
  709. get(
  710. :index,
  711. :params => {
  712. :project_id => 1,
  713. :set_filter => "1",
  714. :tracker_id => "2",
  715. :sort => 'status',
  716. :c => ["status", "priority"]
  717. }
  718. )
  719. assert_select '#csv-export-form[action=?]', '/projects/ecookbook/issues.csv'
  720. assert_select '#csv-export-form[method=?]', 'get'
  721. assert_select '#csv-export-form' do
  722. assert_select 'input[name=?][value=?]', 'set_filter', '1'
  723. assert_select 'input[name=?][value=?]', 'f[]', 'tracker_id'
  724. assert_select 'input[name=?][value=?]', 'op[tracker_id]', '='
  725. assert_select 'input[name=?][value=?]', 'v[tracker_id][]', '2'
  726. assert_select 'input[name=?][value=?]', 'c[]', 'status'
  727. assert_select 'input[name=?][value=?]', 'c[]', 'priority'
  728. assert_select 'input[name=?][value=?]', 'sort', 'status'
  729. end
  730. get(
  731. :index,
  732. :params => {
  733. :project_id => 1,
  734. :set_filter => "1",
  735. :f => ['']
  736. }
  737. )
  738. assert_select '#csv-export-form input[name=?][value=?]', 'f[]', ''
  739. end
  740. def test_index_should_show_block_columns_in_csv_export_form
  741. field = IssueCustomField.
  742. create!(
  743. :name => 'Long text', :field_format => 'text',
  744. :full_width_layout => '1',
  745. :tracker_ids => [1], :is_for_all => true
  746. )
  747. get :index
  748. assert_response :success
  749. assert_select '#csv-export-form' do
  750. assert_select 'input[value=?]', 'description'
  751. assert_select 'input[value=?]', 'last_notes'
  752. assert_select 'input[value=?]', "cf_#{field.id}"
  753. end
  754. end
  755. def test_index_csv
  756. get(:index, :params => {:format => 'csv'})
  757. assert_response :success
  758. assert_equal 'text/csv; header=present', @response.media_type
  759. assert response.body.starts_with?("#,")
  760. lines = response.body.chomp.split("\n")
  761. # default columns + id and project
  762. assert_equal Setting.issue_list_default_columns.size + 2, lines[0].split(',').size
  763. end
  764. def test_index_csv_filename_without_query_name_param
  765. get :index, :params => {:format => 'csv'}
  766. assert_response :success
  767. assert_match /issues.csv/, @response.headers['Content-Disposition']
  768. end
  769. def test_index_csv_filename_with_query_name_param
  770. get :index, :params => {:query_name => 'My Query Name', :format => 'csv'}
  771. assert_response :success
  772. assert_match /my_query_name\.csv/, @response.headers['Content-Disposition']
  773. end
  774. def test_index_csv_with_project
  775. get(
  776. :index,
  777. :params => {
  778. :project_id => 1,
  779. :format => 'csv'
  780. }
  781. )
  782. assert_response :success
  783. assert_equal 'text/csv; header=present', @response.media_type
  784. end
  785. def test_index_csv_without_any_filters
  786. @request.session[:user_id] = 1
  787. Issue.
  788. create!(
  789. :project_id => 1, :tracker_id => 1,
  790. :status_id => 5, :subject => 'Closed issue', :author_id => 1
  791. )
  792. get(
  793. :index,
  794. :params => {
  795. :set_filter => 1,
  796. :f => [''],
  797. :format => 'csv'
  798. }
  799. )
  800. assert_response :success
  801. # -1 for headers
  802. assert_equal Issue.count, response.body.chomp.split("\n").size - 1
  803. end
  804. def test_index_csv_with_description
  805. Issue.generate!(:description => 'test_index_csv_with_description')
  806. with_settings :default_language => 'en' do
  807. get(
  808. :index,
  809. :params => {
  810. :format => 'csv',
  811. :c => [:tracker, :description]
  812. }
  813. )
  814. assert_response :success
  815. end
  816. assert_equal 'text/csv; header=present', response.media_type
  817. headers = response.body.chomp.split("\n").first.split(',')
  818. assert_include 'Description', headers
  819. assert_include 'test_index_csv_with_description', response.body
  820. end
  821. def test_index_csv_with_spent_time_column
  822. issue = Issue.
  823. create!(
  824. :project_id => 1, :tracker_id => 1,
  825. :subject => 'test_index_csv_with_spent_time_column',
  826. :author_id => 2
  827. )
  828. TimeEntry.
  829. create!(
  830. :project => issue.project, :issue => issue,
  831. :hours => 7.33, :user => User.find(2),
  832. :spent_on => Date.today
  833. )
  834. get(
  835. :index,
  836. :params => {
  837. :format => 'csv',
  838. :set_filter => '1',
  839. :c => %w(subject spent_hours)
  840. }
  841. )
  842. assert_response :success
  843. assert_equal 'text/csv; header=present', @response.media_type
  844. lines = @response.body.chomp.split("\n")
  845. assert_include "#{issue.id},#{issue.subject},7.33", lines
  846. end
  847. def test_index_csv_with_all_columns
  848. get(
  849. :index,
  850. :params => {
  851. :format => 'csv',
  852. :c => ['all_inline']
  853. }
  854. )
  855. assert_response :success
  856. assert_equal 'text/csv; header=present', @response.media_type
  857. assert_match /\A#,/, response.body
  858. lines = response.body.chomp.split("\n")
  859. assert_equal IssueQuery.new.available_inline_columns.size, lines[0].split(',').size
  860. end
  861. def test_index_csv_with_multi_column_field
  862. CustomField.find(1).update_attribute :multiple, true
  863. issue = Issue.find(1)
  864. issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
  865. issue.save!
  866. get(
  867. :index,
  868. :params => {
  869. :format => 'csv',
  870. :c => ['tracker', "cf_1"]
  871. }
  872. )
  873. assert_response :success
  874. lines = @response.body.chomp.split("\n")
  875. assert lines.detect {|line| line.include?('"MySQL, Oracle"')}
  876. end
  877. def test_index_csv_should_format_float_custom_fields_with_csv_decimal_separator
  878. field =
  879. IssueCustomField.
  880. create!(
  881. :name => 'Float',
  882. :is_for_all => true,
  883. :tracker_ids => [1],
  884. :field_format => 'float'
  885. )
  886. issue =
  887. Issue.
  888. generate!(
  889. :project_id => 1, :tracker_id => 1,
  890. :custom_field_values => {field.id => '185.6'}
  891. )
  892. with_settings :default_language => 'fr' do
  893. get(
  894. :index,
  895. :params => {
  896. :format => 'csv',
  897. :c => ['id', 'tracker', "cf_#{field.id}"]
  898. }
  899. )
  900. assert_response :success
  901. issue_line =
  902. response.body.chomp.split("\n").
  903. map {|line| line.split(';')}.
  904. detect {|line| line[0]==issue.id.to_s}
  905. assert_include '185,60', issue_line
  906. end
  907. with_settings :default_language => 'en' do
  908. get(
  909. :index,
  910. :params => {
  911. :format => 'csv',
  912. :c => ['id', 'tracker', "cf_#{field.id}"]
  913. }
  914. )
  915. assert_response :success
  916. issue_line = response.body.chomp.
  917. split("\n").map {|line| line.split(',')}.
  918. detect {|line| line[0]==issue.id.to_s}
  919. assert_include '185.60', issue_line
  920. end
  921. end
  922. def test_index_csv_should_fill_parent_column_with_parent_id
  923. Issue.delete_all
  924. parent = Issue.generate!
  925. child = Issue.generate!(:parent_issue_id => parent.id)
  926. with_settings :default_language => 'en' do
  927. get(
  928. :index,
  929. :params => {
  930. :format => 'csv',
  931. :c => %w(parent)
  932. }
  933. )
  934. end
  935. lines = response.body.split("\n")
  936. assert_include "#{child.id},#{parent.id}", lines
  937. end
  938. def test_index_csv_big_5
  939. with_settings :default_language => "zh-TW" do
  940. str_utf8 = '一月'
  941. str_big5 = (+"\xa4@\xa4\xeb").force_encoding('Big5')
  942. issue = Issue.generate!(:subject => str_utf8)
  943. get(
  944. :index, :params => {
  945. :project_id => 1,
  946. :subject => str_utf8,
  947. :format => 'csv'
  948. }
  949. )
  950. assert_equal 'text/csv; header=present', @response.media_type
  951. lines = @response.body.chomp.split("\n")
  952. header = lines[0]
  953. status = (+"\xaa\xac\xbaA").force_encoding('Big5')
  954. assert_include status, header
  955. issue_line = lines.find {|l| l =~ /^#{issue.id},/}
  956. assert_include str_big5, issue_line
  957. end
  958. end
  959. def test_index_csv_cannot_convert_should_be_replaced_big_5
  960. with_settings :default_language => "zh-TW" do
  961. str_utf8 = '以内'
  962. issue = Issue.generate!(:subject => str_utf8)
  963. get(
  964. :index, :params => {
  965. :project_id => 1,
  966. :subject => str_utf8,
  967. :c => ['status', 'subject'],
  968. :format => 'csv',
  969. :set_filter => 1
  970. }
  971. )
  972. assert_equal 'text/csv; header=present', @response.media_type
  973. lines = @response.body.chomp.split("\n")
  974. header = lines[0]
  975. issue_line = lines.find {|l| l =~ /^#{issue.id},/}
  976. s1 = (+"\xaa\xac\xbaA").force_encoding('Big5') # status
  977. assert header.include?(s1)
  978. s2 = issue_line.split(",")[2]
  979. s3 = (+"\xa5H?").force_encoding('Big5') # subject
  980. assert_equal s3, s2
  981. end
  982. end
  983. def test_index_csv_tw
  984. with_settings :default_language => "zh-TW" do
  985. str1 = "test_index_csv_tw"
  986. issue = Issue.generate!(:subject => str1, :estimated_hours => '1234.5')
  987. get(
  988. :index,
  989. :params => {
  990. :project_id => 1,
  991. :subject => str1,
  992. :c => ['estimated_hours', 'subject'],
  993. :format => 'csv',
  994. :set_filter => 1
  995. }
  996. )
  997. assert_equal 'text/csv; header=present', @response.media_type
  998. lines = @response.body.chomp.split("\n")
  999. assert_include "#{issue.id},1234.50,#{str1}", lines
  1000. end
  1001. end
  1002. def test_index_csv_fr
  1003. with_settings :default_language => "fr" do
  1004. str1 = "test_index_csv_fr"
  1005. issue = Issue.generate!(:subject => str1, :estimated_hours => '1234.5')
  1006. get(
  1007. :index,
  1008. :params => {
  1009. :project_id => 1,
  1010. :subject => str1,
  1011. :c => ['estimated_hours', 'subject'],
  1012. :format => 'csv',
  1013. :set_filter => 1
  1014. }
  1015. )
  1016. assert_equal 'text/csv; header=present', @response.media_type
  1017. lines = @response.body.chomp.split("\n")
  1018. assert_include "#{issue.id};1234,50;#{str1}", lines
  1019. end
  1020. end
  1021. def test_index_csv_should_not_change_selected_columns
  1022. get(
  1023. :index,
  1024. :params => {
  1025. :set_filter => 1,
  1026. :c => ["subject", "due_date"],
  1027. :project_id => "ecookbook"
  1028. }
  1029. )
  1030. assert_response :success
  1031. assert_equal [:subject, :due_date], session[:issue_query][:column_names]
  1032. get(
  1033. :index,
  1034. :params => {
  1035. :set_filter => 1,
  1036. :c =>["all_inline"],
  1037. :project_id => "ecookbook",
  1038. :format => 'csv'
  1039. }
  1040. )
  1041. assert_response :success
  1042. assert_equal [:subject, :due_date], session[:issue_query][:column_names]
  1043. end
  1044. def test_index_pdf
  1045. ["en", "zh", "zh-TW", "ja", "ko", "ar"].each do |lang|
  1046. with_settings :default_language => lang do
  1047. get :index
  1048. assert_response :success
  1049. get(:index, :params => {:format => 'pdf'})
  1050. assert_response :success
  1051. assert_equal 'application/pdf', @response.media_type
  1052. get(
  1053. :index,
  1054. :params => {
  1055. :project_id => 1,
  1056. :format => 'pdf'
  1057. }
  1058. )
  1059. assert_response :success
  1060. assert_equal 'application/pdf', @response.media_type
  1061. get(
  1062. :index,
  1063. :params => {
  1064. :project_id => 1,
  1065. :query_id => 6,
  1066. :format => 'pdf'
  1067. }
  1068. )
  1069. assert_response :success
  1070. assert_equal 'application/pdf', @response.media_type
  1071. end
  1072. end
  1073. end
  1074. def test_index_pdf_with_query_grouped_by_list_custom_field
  1075. get(
  1076. :index,
  1077. :params => {
  1078. :project_id => 1,
  1079. :query_id => 9,
  1080. :format => 'pdf'
  1081. }
  1082. )
  1083. assert_response :success
  1084. assert_equal 'application/pdf', @response.media_type
  1085. end
  1086. def test_index_pdf_with_query_grouped_by_full_width_text_custom_field
  1087. field = IssueCustomField.
  1088. create!(
  1089. :name => 'Long text', :field_format => 'text',
  1090. :full_width_layout => '1',
  1091. :tracker_ids => [1, 3], :is_for_all => true
  1092. )
  1093. issue = Issue.find(1)
  1094. issue.custom_field_values = {field.id => 'This is a long text'}
  1095. issue.save!
  1096. get(
  1097. :index,
  1098. :params => {
  1099. :set_filter => 1,
  1100. :c => ['subject', 'description', "cf_#{field.id}"],
  1101. :format => 'pdf'
  1102. }
  1103. )
  1104. assert_response :success
  1105. assert_equal 'application/pdf', @response.media_type
  1106. end
  1107. def test_index_pdf_filename_without_query
  1108. get :index, :params => {:format => 'pdf'}
  1109. assert_response :success
  1110. assert_match /issues.pdf/, @response.headers['Content-Disposition']
  1111. end
  1112. def test_index_pdf_filename_with_query
  1113. query = IssueQuery.create!(:name => 'My Query Name', :visibility => IssueQuery::VISIBILITY_PUBLIC)
  1114. get :index, :params => {:query_id => query.id, :format => 'pdf'}
  1115. assert_response :success
  1116. assert_match /my_query_name\.pdf/, @response.headers['Content-Disposition']
  1117. end
  1118. def test_index_atom
  1119. get(
  1120. :index,
  1121. :params => {
  1122. :project_id => 'ecookbook',
  1123. :format => 'atom'
  1124. }
  1125. )
  1126. assert_response :success
  1127. assert_equal 'application/atom+xml', response.media_type
  1128. assert_select 'feed' do
  1129. assert_select 'link[rel=self][href=?]', 'http://test.host/projects/ecookbook/issues.atom'
  1130. assert_select 'link[rel=alternate][href=?]', 'http://test.host/projects/ecookbook/issues'
  1131. assert_select 'entry link[href=?]', 'http://test.host/issues/1'
  1132. end
  1133. end
  1134. def test_index_should_include_back_url_input
  1135. get(
  1136. :index,
  1137. :params => {
  1138. :project_id => 'ecookbook',
  1139. :foo => 'bar'
  1140. }
  1141. )
  1142. assert_response :success
  1143. assert_select 'input[name=back_url][value=?]', '/projects/ecookbook/issues?foo=bar'
  1144. end
  1145. def test_index_sort
  1146. get(:index, :params => {:sort => 'tracker,id:desc'})
  1147. assert_response :success
  1148. assert_equal(
  1149. issues_in_list.sort_by {|issue| [issue.tracker.position, -issue.id]},
  1150. issues_in_list
  1151. )
  1152. assert_select 'table.issues.sort-by-tracker.sort-asc'
  1153. end
  1154. def test_index_sort_by_field_not_included_in_columns
  1155. with_settings :issue_list_default_columns => %w(subject author) do
  1156. get(:index, :params => {:sort => 'tracker'})
  1157. assert_response :success
  1158. end
  1159. end
  1160. def test_index_sort_by_assigned_to
  1161. get(:index, :params => {:sort => 'assigned_to'})
  1162. assert_response :success
  1163. assignees = issues_in_list.filter_map(&:assigned_to)
  1164. assert_equal assignees.sort, assignees
  1165. assert_select 'table.issues.sort-by-assigned-to.sort-asc'
  1166. end
  1167. def test_index_sort_by_assigned_to_desc
  1168. get(:index, :params => {:sort => 'assigned_to:desc'})
  1169. assert_response :success
  1170. assignees = issues_in_list.filter_map(&:assigned_to)
  1171. assert_equal assignees.sort.reverse, assignees
  1172. assert_select 'table.issues.sort-by-assigned-to.sort-desc'
  1173. end
  1174. def test_index_group_by_assigned_to
  1175. get(
  1176. :index,
  1177. :params => {
  1178. :group_by => 'assigned_to',
  1179. :sort => 'priority'
  1180. }
  1181. )
  1182. assert_response :success
  1183. end
  1184. def test_index_sort_by_author
  1185. get(
  1186. :index,
  1187. :params => {
  1188. :sort => 'author',
  1189. :c => ['author']
  1190. }
  1191. )
  1192. assert_response :success
  1193. authors = issues_in_list.map(&:author)
  1194. assert_equal authors.sort, authors
  1195. end
  1196. def test_index_sort_by_author_desc
  1197. get(:index, :params => {:sort => 'author:desc'})
  1198. assert_response :success
  1199. authors = issues_in_list.map(&:author)
  1200. assert_equal authors.sort.reverse, authors
  1201. end
  1202. def test_index_group_by_author
  1203. get(
  1204. :index,
  1205. :params => {
  1206. :group_by => 'author',
  1207. :sort => 'priority'
  1208. }
  1209. )
  1210. assert_response :success
  1211. end
  1212. def test_index_sort_by_last_updated_by
  1213. get(:index, :params => {:sort => 'last_updated_by'})
  1214. assert_response :success
  1215. assert_select 'table.issues.sort-by-last-updated-by.sort-asc'
  1216. end
  1217. def test_index_sort_by_last_updated_by_desc
  1218. get(:index, :params => {:sort => 'last_updated_by:desc'})
  1219. assert_response :success
  1220. assert_select 'table.issues.sort-by-last-updated-by.sort-desc'
  1221. end
  1222. def test_index_sort_by_spent_hours
  1223. get(:index, :params => {:sort => 'spent_hours:desc'})
  1224. assert_response :success
  1225. hours = issues_in_list.map(&:spent_hours)
  1226. assert_equal hours.sort.reverse, hours
  1227. end
  1228. def test_index_sort_by_spent_hours_should_sort_by_visible_spent_hours
  1229. TimeEntry.delete_all
  1230. TimeEntry.generate!(:issue => Issue.generate!(:project_id => 1), :hours => 3)
  1231. TimeEntry.generate!(:issue => Issue.generate!(:project_id => 3), :hours => 4)
  1232. get(
  1233. :index,
  1234. :params => {
  1235. :sort => "spent_hours:desc",
  1236. :c => ['subject', 'spent_hours']
  1237. }
  1238. )
  1239. assert_response :success
  1240. assert_equal ['4:00', '3:00', '0:00'], columns_values_in_list('spent_hours').first(3)
  1241. Project.find(3).disable_module!(:time_tracking)
  1242. get(
  1243. :index,
  1244. :params => {
  1245. :sort => "spent_hours:desc",
  1246. :c => ['subject', 'spent_hours']
  1247. }
  1248. )
  1249. assert_response :success
  1250. assert_equal ['3:00', '0:00', '0:00'], columns_values_in_list('spent_hours').first(3)
  1251. end
  1252. def test_index_sort_by_total_spent_hours
  1253. get(:index, :params => {:sort => 'total_spent_hours:desc'})
  1254. assert_response :success
  1255. hours = issues_in_list.map(&:total_spent_hours)
  1256. assert_equal hours.sort.reverse, hours
  1257. end
  1258. def test_index_sort_by_total_estimated_hours
  1259. get(:index, :params => {:sort => 'total_estimated_hours:desc'})
  1260. assert_response :success
  1261. hours = issues_in_list.map(&:total_estimated_hours)
  1262. # Removes nil because the position of NULL is database dependent
  1263. hours.compact!
  1264. assert_equal hours.sort.reverse, hours
  1265. end
  1266. def test_index_sort_by_user_custom_field
  1267. cf = IssueCustomField.
  1268. create!(
  1269. :name => 'User',
  1270. :is_for_all => true,
  1271. :tracker_ids => [1, 2, 3],
  1272. :field_format => 'user'
  1273. )
  1274. CustomValue.create!(:custom_field => cf, :customized => Issue.find(1), :value => '2')
  1275. CustomValue.create!(:custom_field => cf, :customized => Issue.find(2), :value => '3')
  1276. CustomValue.create!(:custom_field => cf, :customized => Issue.find(3), :value => '3')
  1277. CustomValue.create!(:custom_field => cf, :customized => Issue.find(5), :value => '')
  1278. get(
  1279. :index,
  1280. :params => {
  1281. :project_id => 1,
  1282. :set_filter => 1,
  1283. :sort => "cf_#{cf.id},id"
  1284. }
  1285. )
  1286. assert_response :success
  1287. assert_equal(
  1288. [2, 3, 1],
  1289. issues_in_list.select {|issue| issue.custom_field_value(cf).present?}.map(&:id)
  1290. )
  1291. end
  1292. def test_index_with_columns
  1293. columns = ['tracker', 'subject', 'assigned_to', 'buttons']
  1294. get(
  1295. :index,
  1296. :params => {
  1297. :set_filter => 1,
  1298. :c => columns
  1299. }
  1300. )
  1301. assert_response :success
  1302. # query should use specified columns + id and checkbox
  1303. assert_select 'table.issues thead' do
  1304. assert_select 'th', columns.size + 2
  1305. assert_select 'th.tracker'
  1306. assert_select 'th.subject'
  1307. assert_select 'th.assigned_to'
  1308. assert_select 'th.buttons'
  1309. end
  1310. # columns should be stored in session
  1311. assert_kind_of Hash, session[:issue_query]
  1312. assert_kind_of Array, session[:issue_query][:column_names]
  1313. assert_equal columns, session[:issue_query][:column_names].map(&:to_s)
  1314. # ensure only these columns are kept in the selected columns list
  1315. assert_select 'select[name=?] option', 'c[]' do
  1316. assert_select 'option', 3
  1317. assert_select 'option[value=tracker]'
  1318. assert_select 'option[value=project]', 0
  1319. end
  1320. end
  1321. def test_index_without_project_should_implicitly_add_project_column_to_default_columns
  1322. with_settings :issue_list_default_columns => ['tracker', 'subject', 'assigned_to'] do
  1323. get(:index, :params => {:set_filter => 1})
  1324. end
  1325. # query should use specified columns
  1326. assert_equal ["#", "Project", "Tracker", "Subject", "Assignee"], columns_in_issues_list
  1327. end
  1328. def test_index_without_project_and_explicit_default_columns_should_not_add_project_column
  1329. with_settings :issue_list_default_columns => ['tracker', 'subject', 'assigned_to'] do
  1330. columns = ['id', 'tracker', 'subject', 'assigned_to']
  1331. get(
  1332. :index,
  1333. :params => {
  1334. :set_filter => 1,
  1335. :c => columns
  1336. }
  1337. )
  1338. end
  1339. # query should use specified columns
  1340. assert_equal ["#", "Tracker", "Subject", "Assignee"], columns_in_issues_list
  1341. end
  1342. def test_index_with_default_columns_should_respect_default_columns_order
  1343. columns = ['assigned_to', 'subject', 'status', 'tracker']
  1344. with_settings :issue_list_default_columns => columns do
  1345. get(
  1346. :index,
  1347. :params => {
  1348. :project_id => 1,
  1349. :set_filter => 1
  1350. }
  1351. )
  1352. assert_equal ["#", "Assignee", "Subject", "Status", "Tracker"], columns_in_issues_list
  1353. end
  1354. end
  1355. def test_index_with_custom_field_column
  1356. columns = %w(tracker subject cf_2)
  1357. get(
  1358. :index,
  1359. :params => {
  1360. :set_filter => 1,
  1361. :c => columns
  1362. }
  1363. )
  1364. assert_response :success
  1365. # query should use specified columns
  1366. assert_equal ["#", "Tracker", "Subject", "Searchable field"], columns_in_issues_list
  1367. assert_select 'table.issues' do
  1368. assert_select 'th.cf_2.string'
  1369. assert_select 'td.cf_2.string'
  1370. end
  1371. end
  1372. def test_index_with_multi_custom_field_column
  1373. field = CustomField.find(1)
  1374. field.update_attribute :multiple, true
  1375. issue = Issue.find(1)
  1376. issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
  1377. issue.save!
  1378. get(
  1379. :index,
  1380. :params => {
  1381. :set_filter => 1,
  1382. :c => %w(tracker subject cf_1)
  1383. }
  1384. )
  1385. assert_response :success
  1386. assert_select 'table.issues td.cf_1', :text => 'MySQL, Oracle'
  1387. end
  1388. def test_index_with_multi_user_custom_field_column
  1389. field =
  1390. IssueCustomField.
  1391. create!(
  1392. :name => 'Multi user', :field_format => 'user',
  1393. :multiple => true,
  1394. :tracker_ids => [1], :is_for_all => true
  1395. )
  1396. issue = Issue.find(1)
  1397. issue.custom_field_values = {field.id => ['2', '3']}
  1398. issue.save!
  1399. get(
  1400. :index,
  1401. :params => {
  1402. :set_filter => 1,
  1403. :c => ['tracker', 'subject', "cf_#{field.id}"]
  1404. }
  1405. )
  1406. assert_response :success
  1407. assert_select "table.issues td.cf_#{field.id}" do
  1408. assert_select 'a', 2
  1409. assert_select 'a[href=?]', '/users/2', :text => 'John Smith'
  1410. assert_select 'a[href=?]', '/users/3', :text => 'Dave Lopper'
  1411. end
  1412. end
  1413. def test_index_with_date_column
  1414. with_settings :date_format => '%d/%m/%Y' do
  1415. Issue.find(1).update_attribute :start_date, '1987-08-24'
  1416. get(
  1417. :index,
  1418. :params => {
  1419. :set_filter => 1,
  1420. :c => %w(start_date)
  1421. }
  1422. )
  1423. assert_select 'table.issues' do
  1424. assert_select 'th.start_date'
  1425. assert_select 'td.start_date', :text => '24/08/1987'
  1426. end
  1427. end
  1428. end
  1429. def test_index_with_done_ratio_column
  1430. Issue.find(1).update_attribute :done_ratio, 40
  1431. get(
  1432. :index,
  1433. :params => {
  1434. :set_filter => 1,
  1435. :c => %w(done_ratio)
  1436. }
  1437. )
  1438. assert_select 'table.issues td.done_ratio' do
  1439. assert_select 'table.progress' do
  1440. assert_select 'td.closed[style=?]', 'width: 40%;'
  1441. end
  1442. end
  1443. end
  1444. def test_index_with_spent_hours_column
  1445. Issue.expects(:load_visible_spent_hours).once
  1446. get(
  1447. :index,
  1448. :params => {
  1449. :set_filter => 1,
  1450. :c => %w(subject spent_hours)
  1451. }
  1452. )
  1453. assert_select 'table.issues tr#issue-3 td.spent_hours', :text => '1:00'
  1454. end
  1455. def test_index_with_total_spent_hours_column
  1456. Issue.expects(:load_visible_total_spent_hours).once
  1457. get(
  1458. :index,
  1459. :params => {
  1460. :set_filter => 1,
  1461. :c => %w(subject total_spent_hours)
  1462. }
  1463. )
  1464. assert_select 'table.issues tr#issue-3 td.total_spent_hours', :text => '1:00'
  1465. end
  1466. def test_index_with_total_estimated_hours_column
  1467. get(
  1468. :index,
  1469. :params => {
  1470. :set_filter => 1,
  1471. :c => %w(subject total_estimated_hours)
  1472. }
  1473. )
  1474. assert_select 'table.issues td.total_estimated_hours'
  1475. end
  1476. def test_index_should_not_show_spent_hours_column_without_permission
  1477. Role.anonymous.remove_permission! :view_time_entries
  1478. get(
  1479. :index,
  1480. :params => {
  1481. :set_filter => 1,
  1482. :c => %w(subject spent_hours)
  1483. }
  1484. )
  1485. assert_select 'td.spent_hours', 0
  1486. end
  1487. def test_index_with_fixed_version_column
  1488. get(
  1489. :index,
  1490. :params => {
  1491. :set_filter => 1,
  1492. :c => %w(fixed_version)
  1493. }
  1494. )
  1495. assert_select 'table.issues td.fixed_version' do
  1496. assert_select 'a[href=?]', '/versions/2', :text => 'eCookbook - 1.0'
  1497. end
  1498. end
  1499. def test_index_with_relations_column
  1500. IssueRelation.delete_all
  1501. IssueRelation.
  1502. create!(
  1503. :relation_type => "relates",
  1504. :issue_from => Issue.find(1), :issue_to => Issue.find(7)
  1505. )
  1506. IssueRelation.
  1507. create!(
  1508. :relation_type => "relates",
  1509. :issue_from => Issue.find(8), :issue_to => Issue.find(1)
  1510. )
  1511. IssueRelation.
  1512. create!(
  1513. :relation_type => "blocks",
  1514. :issue_from => Issue.find(1), :issue_to => Issue.find(11)
  1515. )
  1516. IssueRelation.
  1517. create!(
  1518. :relation_type => "blocks", :issue_from => Issue.find(12),
  1519. :issue_to => Issue.find(2)
  1520. )
  1521. get(
  1522. :index,
  1523. :params => {
  1524. :set_filter => 1,
  1525. :c => %w(subject relations)
  1526. }
  1527. )
  1528. assert_response :success
  1529. assert_select "tr#issue-1 td.relations" do
  1530. assert_select "span", 3
  1531. assert_select "span", :text => "Related to #7"
  1532. assert_select "span", :text => "Related to #8"
  1533. assert_select "span", :text => "Blocks #11"
  1534. end
  1535. assert_select "tr#issue-2 td.relations" do
  1536. assert_select "span", 1
  1537. assert_select "span", :text => "Blocked by #12"
  1538. end
  1539. assert_select "tr#issue-3 td.relations" do
  1540. assert_select "span", 0
  1541. end
  1542. get(
  1543. :index,
  1544. :params => {
  1545. :set_filter => 1,
  1546. :c => %w(relations),
  1547. :format => 'csv'
  1548. }
  1549. )
  1550. assert_response :success
  1551. assert_equal 'text/csv; header=present', response.media_type
  1552. lines = response.body.chomp.split("\n")
  1553. assert_include '1,"Related to #7, Related to #8, Blocks #11"', lines
  1554. assert_include '2,Blocked by #12', lines
  1555. assert_include '3,""', lines
  1556. get(
  1557. :index,
  1558. :params => {
  1559. :set_filter => 1,
  1560. :c => %w(subject relations),
  1561. :format => 'pdf'
  1562. }
  1563. )
  1564. assert_response :success
  1565. assert_equal 'application/pdf', response.media_type
  1566. end
  1567. def test_index_with_description_column
  1568. get(
  1569. :index,
  1570. :params => {
  1571. :set_filter => 1,
  1572. :c => %w(subject description)
  1573. }
  1574. )
  1575. assert_select 'table.issues thead th', 4 # columns: chekbox + id + subject
  1576. assert_select 'td.description[colspan="4"]', :text => 'Unable to print recipes'
  1577. get(
  1578. :index,
  1579. :params => {
  1580. :set_filter => 1,
  1581. :c => %w(subject description),
  1582. :format => 'pdf'
  1583. }
  1584. )
  1585. assert_response :success
  1586. assert_equal 'application/pdf', response.media_type
  1587. end
  1588. def test_index_with_last_notes_column
  1589. with_settings :text_formatting => 'textile' do
  1590. get(
  1591. :index,
  1592. :params => {
  1593. :set_filter => 1,
  1594. :c => %w(subject last_notes)
  1595. }
  1596. )
  1597. assert_response :success
  1598. assert_select 'table.issues thead th', 4 # columns: chekbox + id + subject
  1599. assert_select 'td.last_notes[colspan="4"]', :text => 'Some notes with Redmine links: #2, r2.'
  1600. assert_select(
  1601. 'td.last_notes[colspan="4"]',
  1602. :text => 'A comment with inline image: and a reference to #1 and r2.'
  1603. )
  1604. get(
  1605. :index,
  1606. :params => {
  1607. :set_filter => 1,
  1608. :c => %w(subject last_notes),
  1609. :format => 'pdf'
  1610. }
  1611. )
  1612. assert_response :success
  1613. assert_equal 'application/pdf', response.media_type
  1614. end
  1615. end
  1616. def test_index_with_last_notes_column_should_display_private_notes_with_permission_only
  1617. journal = Journal.
  1618. create!(
  1619. :journalized => Issue.find(2),
  1620. :notes => 'Public notes', :user_id => 1
  1621. )
  1622. journal = Journal.
  1623. create!(
  1624. :journalized => Issue.find(2),
  1625. :notes => 'Privates notes', :private_notes => true,
  1626. :user_id => 1
  1627. )
  1628. @request.session[:user_id] = 2
  1629. get(
  1630. :index,
  1631. :params => {
  1632. :set_filter => 1,
  1633. :c => %w(subject last_notes)
  1634. }
  1635. )
  1636. assert_response :success
  1637. assert_select 'td.last_notes[colspan="4"]', :text => 'Privates notes'
  1638. Role.find(1).remove_permission! :view_private_notes
  1639. get(
  1640. :index,
  1641. :params => {
  1642. :set_filter => 1,
  1643. :c => %w(subject last_notes)
  1644. }
  1645. )
  1646. assert_response :success
  1647. assert_select 'td.last_notes[colspan="4"]', :text => 'Public notes'
  1648. end
  1649. def test_index_with_description_and_last_notes_columns_should_display_column_name
  1650. get(
  1651. :index,
  1652. :params => {
  1653. :set_filter => 1,
  1654. :c => %w(subject last_notes description)
  1655. }
  1656. )
  1657. assert_response :success
  1658. assert_select 'td.last_notes[colspan="4"] span', :text => 'Last notes'
  1659. assert_select 'td.description[colspan="4"] span', :text => 'Description'
  1660. end
  1661. def test_index_with_full_width_layout_custom_field_column_should_show_column_as_block_column
  1662. field = IssueCustomField.
  1663. create!(
  1664. :name => 'Long text', :field_format => 'text',
  1665. :full_width_layout => '1',
  1666. :tracker_ids => [1], :is_for_all => true
  1667. )
  1668. issue = Issue.find(1)
  1669. issue.custom_field_values = {field.id => 'This is a long text'}
  1670. issue.save!
  1671. get(
  1672. :index,
  1673. :params => {
  1674. :set_filter => 1,
  1675. :c => ['subject', 'description', "cf_#{field.id}"]
  1676. }
  1677. )
  1678. assert_response :success
  1679. assert_select 'td.description[colspan="4"] span', :text => 'Description'
  1680. assert_select "td.cf_#{field.id} span", :text => 'Long text'
  1681. end
  1682. def test_index_with_parent_column
  1683. Issue.delete_all
  1684. parent = Issue.generate!
  1685. child = Issue.generate!(:parent_issue_id => parent.id)
  1686. get(:index, :params => {:c => %w(parent)})
  1687. assert_select 'td.parent', :text => "#{parent.tracker} ##{parent.id}"
  1688. assert_select 'td.parent a[title=?]', parent.subject
  1689. end
  1690. def test_index_with_parent_subject_column
  1691. Issue.delete_all
  1692. parent = Issue.generate!
  1693. child = Issue.generate!(:parent_issue_id => parent.id)
  1694. get(:index, :params => {:c => %w(parent.subject)})
  1695. assert_select 'table.issues' do
  1696. assert_select 'th.parent-subject', :text => l(:field_parent_issue_subject)
  1697. assert_select "tr#issue-#{child.id}" do
  1698. assert_select 'td.parent-subject', :text => parent.subject
  1699. end
  1700. end
  1701. end
  1702. def test_index_with_last_updated_by_column
  1703. get(
  1704. :index, :params => {
  1705. :c => %w(subject last_updated_by),
  1706. :issue_id => '1,2,3',
  1707. :sort => 'id',
  1708. :set_filter => '1'
  1709. }
  1710. )
  1711. assert_select 'td.last_updated_by'
  1712. assert_equal ["John Smith", "John Smith", ""], css_select('td.last_updated_by').map(&:text)
  1713. end
  1714. def test_index_with_attachments_column
  1715. get(
  1716. :index,
  1717. :params => {
  1718. :c => %w(subject attachments),
  1719. :set_filter => '1',
  1720. :sort => 'id'
  1721. }
  1722. )
  1723. assert_response :success
  1724. assert_select 'td.attachments'
  1725. assert_select 'tr#issue-2' do
  1726. assert_select 'td.attachments' do
  1727. assert_select 'span:nth-of-type(1)' do
  1728. assert_select 'a[href=?]', '/attachments/4', :text => 'source.rb'
  1729. assert_select 'a[href=?].icon-download', '/attachments/download/4/source.rb'
  1730. end
  1731. assert_select 'span:nth-of-type(2)' do
  1732. assert_select 'a[href=?]', '/attachments/10', :text => 'picture.jpg'
  1733. assert_select 'a[href=?].icon-download', '/attachments/download/10/picture.jpg'
  1734. end
  1735. end
  1736. end
  1737. end
  1738. def test_index_with_attachments_column_as_csv
  1739. get(
  1740. :index,
  1741. :params => {
  1742. :c => %w(subject attachments),
  1743. :set_filter => '1',
  1744. :sort => 'id',
  1745. :format => 'csv'
  1746. }
  1747. )
  1748. assert_response :success
  1749. assert_include "\"source.rb\npicture.jpg\"", response.body
  1750. end
  1751. def test_index_with_watchers_column
  1752. @request.session[:user_id] = 2
  1753. get(
  1754. :index,
  1755. :params => {
  1756. :c => %w(subject watcher_users),
  1757. :set_filter => '1',
  1758. :sort => 'id',
  1759. }
  1760. )
  1761. assert_response :success
  1762. assert_select 'td.watcher_users'
  1763. assert_select 'tr#issue-2' do
  1764. assert_select 'td.watcher_users' do
  1765. assert_select 'a[href=?]', '/users/1', :text => User.find(1).name
  1766. assert_select 'a[href=?]', '/users/3', :text => User.find(3).name
  1767. end
  1768. end
  1769. end
  1770. def test_index_with_watchers_column_only_visible_watchers
  1771. @request.session[:user_id] = 3
  1772. User.find(3).roles.first.remove_permission! :view_issue_watchers
  1773. get(
  1774. :index,
  1775. :params => {
  1776. :c => %w(subject watcher_users),
  1777. :set_filter => '1',
  1778. :sort => 'id',
  1779. }
  1780. )
  1781. assert_response :success
  1782. assert_select 'td.watcher_users'
  1783. assert_select 'tr#issue-2' do
  1784. assert_select 'td.watcher_users' do
  1785. assert_select 'a[href=?]', '/users/1', 0
  1786. # Currently not implemented, see https://www.redmine.org/issues/29894#note-17
  1787. # You can only know that you are a watcher yourself
  1788. # assert_select 'a[href=?]', '/users/3', :text => User.find(3).name
  1789. end
  1790. end
  1791. end
  1792. def test_index_with_watchers_column_as_csv
  1793. @request.session[:user_id] = 2
  1794. get(
  1795. :index,
  1796. :params => {
  1797. :c => %w(subject watcher_users),
  1798. :set_filter => '1',
  1799. :sort => 'id',
  1800. :format => 'csv',
  1801. }
  1802. )
  1803. assert_response :success
  1804. assert_include "\"#{User.find(1).name}\n#{User.find(3).name}\"", response.body
  1805. end
  1806. def test_index_with_estimated_hours_total
  1807. Issue.delete_all
  1808. Issue.generate!(:estimated_hours => '5:30')
  1809. Issue.generate!(:estimated_hours => '1:06')
  1810. get(:index, :params => {:t => %w(estimated_hours)})
  1811. assert_response :success
  1812. assert_select '.query-totals'
  1813. assert_select '.total-for-estimated-hours span.value', :text => '6:36'
  1814. assert_select 'input[type=checkbox][name=?][value=estimated_hours][checked=checked]', 't[]'
  1815. end
  1816. def test_index_with_grouped_query_and_estimated_hours_total
  1817. Issue.delete_all
  1818. Issue.generate!(:estimated_hours => '5:30', :category_id => 1)
  1819. Issue.generate!(:estimated_hours => '2:18', :category_id => 1)
  1820. Issue.generate!(:estimated_hours => '1:06', :category_id => 2)
  1821. Issue.generate!(:estimated_hours => '4:36')
  1822. get(
  1823. :index,
  1824. :params => {
  1825. :t => %w(estimated_hours),
  1826. :group_by => 'category'
  1827. }
  1828. )
  1829. assert_response :success
  1830. assert_select '.query-totals'
  1831. assert_select '.query-totals .total-for-estimated-hours span.value', :text => '13:30'
  1832. assert_select 'tr.group', :text => /Printing/ do
  1833. assert_select '.total-for-estimated-hours span.value', :text => '7:48'
  1834. end
  1835. assert_select 'tr.group', :text => /Recipes/ do
  1836. assert_select '.total-for-estimated-hours span.value', :text => '1:06'
  1837. end
  1838. assert_select 'tr.group', :text => /blank/ do
  1839. assert_select '.total-for-estimated-hours span.value', :text => '4:36'
  1840. end
  1841. end
  1842. def test_index_with_int_custom_field_total
  1843. field = IssueCustomField.generate!(:field_format => 'int', :is_for_all => true)
  1844. CustomValue.create!(:customized => Issue.find(1), :custom_field => field, :value => '2')
  1845. CustomValue.create!(:customized => Issue.find(2), :custom_field => field, :value => '7')
  1846. get(:index, :params => {:t => ["cf_#{field.id}"]})
  1847. assert_response :success
  1848. assert_select '.query-totals'
  1849. assert_select ".total-for-cf-#{field.id} span.value", :text => '9'
  1850. end
  1851. def test_index_with_spent_time_total_should_sum_visible_spent_time_only
  1852. TimeEntry.delete_all
  1853. TimeEntry.generate!(:issue => Issue.generate!(:project_id => 1), :hours => 3)
  1854. TimeEntry.generate!(:issue => Issue.generate!(:project_id => 3), :hours => 4)
  1855. get :index, :params => {:t => ["spent_hours"]}
  1856. assert_response :success
  1857. assert_select ".total-for-spent-hours span.value", :text => '7:00'
  1858. Project.find(3).disable_module!(:time_tracking)
  1859. get :index, :params => {:t => ["spent_hours"]}
  1860. assert_response :success
  1861. assert_select ".total-for-spent-hours span.value", :text => '3:00'
  1862. end
  1863. def test_index_totals_should_default_to_settings
  1864. with_settings :issue_list_default_totals => ['estimated_hours'] do
  1865. get :index
  1866. assert_response :success
  1867. assert_select '.total-for-estimated-hours span.value'
  1868. assert_select '.query-totals>span', 1
  1869. end
  1870. end
  1871. def test_index_send_html_if_query_is_invalid
  1872. get(
  1873. :index,
  1874. :params => {
  1875. :f => ['start_date'],
  1876. :op => {
  1877. :start_date => '='
  1878. }
  1879. }
  1880. )
  1881. assert_equal 'text/html', @response.media_type
  1882. assert_select_error /Start date cannot be blank/i
  1883. end
  1884. def test_index_send_nothing_if_query_is_invalid
  1885. get(
  1886. :index,
  1887. :params => {
  1888. :f => ['start_date'],
  1889. :op => {
  1890. :start_date => '='
  1891. },
  1892. :format => 'csv'
  1893. }
  1894. )
  1895. assert_equal 'text/csv', @response.media_type
  1896. assert @response.body.blank?
  1897. end
  1898. def test_index_should_include_new_issue_link
  1899. @request.session[:user_id] = 2
  1900. get(:index, :params => {:project_id => 1})
  1901. assert_select(
  1902. '#content a.new-issue[href="/projects/ecookbook/issues/new"]',
  1903. :text => 'New issue'
  1904. )
  1905. end
  1906. def test_index_should_not_include_new_issue_link_for_project_without_trackers
  1907. Project.find(1).trackers.clear
  1908. @request.session[:user_id] = 2
  1909. get(:index, :params => {:project_id => 1})
  1910. assert_select '#content a.new-issue', 0
  1911. end
  1912. def test_index_should_not_include_new_issue_link_for_users_with_copy_issues_permission_only
  1913. role = Role.find(1)
  1914. role.remove_permission! :add_issues
  1915. role.add_permission! :copy_issues
  1916. @request.session[:user_id] = 2
  1917. get(:index, :params => {:project_id => 1})
  1918. assert_select '#content a.new-issue', 0
  1919. end
  1920. def test_index_without_project_should_include_new_issue_link
  1921. @request.session[:user_id] = 2
  1922. get :index
  1923. assert_select '#content a.new-issue[href="/issues/new"]', :text => 'New issue'
  1924. end
  1925. def test_index_should_show_setting_link_with_edit_project_permission
  1926. role = Role.find(1)
  1927. role.add_permission! :edit_project
  1928. @request.session[:user_id] = 2
  1929. get(:index, :params => {:project_id => 1})
  1930. assert_select '#content a.icon-settings[href="/projects/ecookbook/settings/issues"]', 1
  1931. end
  1932. def test_index_should_not_show_setting_link_without_edit_project_permission
  1933. role = Role.find(1)
  1934. role.remove_permission! :edit_project
  1935. @request.session[:user_id] = 2
  1936. get(:index, :params => {:project_id => 1})
  1937. assert_select '#content a.icon-settings[href="/projects/ecookbook/settings/issues"]', 0
  1938. end
  1939. def test_index_should_not_include_new_issue_tab_when_disabled
  1940. with_settings :new_item_menu_tab => '0' do
  1941. @request.session[:user_id] = 2
  1942. get(:index, :params => {:project_id => 1})
  1943. assert_select '#main-menu a.new-issue', 0
  1944. end
  1945. end
  1946. def test_index_should_include_new_issue_tab_when_enabled
  1947. with_settings :new_item_menu_tab => '1' do
  1948. @request.session[:user_id] = 2
  1949. get(:index, :params => {:project_id => 1})
  1950. assert_select(
  1951. '#main-menu a.new-issue[href="/projects/ecookbook/issues/new"]',
  1952. :text => 'New issue'
  1953. )
  1954. end
  1955. end
  1956. def test_new_should_have_new_issue_tab_as_current_menu_item
  1957. with_settings :new_item_menu_tab => '1' do
  1958. @request.session[:user_id] = 2
  1959. get(:new, :params => {:project_id => 1})
  1960. assert_select '#main-menu a.new-issue.selected'
  1961. end
  1962. end
  1963. def test_index_should_not_include_new_issue_tab_for_project_without_trackers
  1964. with_settings :new_item_menu_tab => '1' do
  1965. Project.find(1).trackers.clear
  1966. @request.session[:user_id] = 2
  1967. get(:index, :params => {:project_id => 1})
  1968. assert_select '#main-menu a.new-issue', 0
  1969. end
  1970. end
  1971. def test_index_should_not_include_new_issue_tab_for_users_with_copy_issues_permission_only
  1972. with_settings :new_item_menu_tab => '1' do
  1973. role = Role.find(1)
  1974. role.remove_permission! :add_issues
  1975. role.add_permission! :copy_issues
  1976. @request.session[:user_id] = 2
  1977. get(:index, :params => {:project_id => 1})
  1978. assert_select '#main-menu a.new-issue', 0
  1979. end
  1980. end
  1981. def test_index_should_respect_timespan_format
  1982. with_settings :timespan_format => 'minutes' do
  1983. get(
  1984. :index,
  1985. :params => {
  1986. :set_filter => 1,
  1987. :c => %w(estimated_hours total_estimated_hours spent_hours total_spent_hours)
  1988. }
  1989. )
  1990. assert_select 'table.issues tr#issue-1 td.estimated_hours', :text => '200:00'
  1991. assert_select 'table.issues tr#issue-1 td.total_estimated_hours', :text => '200:00'
  1992. assert_select 'table.issues tr#issue-1 td.spent_hours', :text => '154:15'
  1993. assert_select 'table.issues tr#issue-1 td.total_spent_hours', :text => '154:15'
  1994. end
  1995. end
  1996. def test_show_by_anonymous
  1997. get(:show, :params => {:id => 1})
  1998. assert_response :success
  1999. assert_select 'div.issue div.description', :text => /Unable to print recipes/
  2000. assert_select '#content>.contextual:first-child' do
  2001. assert_select 'a', {:count => 1, :text => 'Edit'}
  2002. assert_select 'a', {:count => 0, :text => 'Log time'}
  2003. assert_select 'a', {:count => 0, :text => 'Watch'}
  2004. assert_select 'a', {:count => 0, :text => 'Copy'}
  2005. assert_select 'div.drdn-items a', {:count => 1, :text => 'Copy link'}
  2006. assert_select 'div.drdn-items a', {:count => 0, :text => 'Delete issue'}
  2007. end
  2008. # anonymous role is allowed to add a note
  2009. assert_select 'form#issue-form' do
  2010. assert_select 'fieldset' do
  2011. assert_select 'legend', :text => 'Notes'
  2012. assert_select 'textarea[name=?]', 'issue[notes]'
  2013. end
  2014. end
  2015. assert_select 'title', :text => "Bug #1: Cannot print recipes - eCookbook - Redmine"
  2016. end
  2017. def test_show_by_manager
  2018. @request.session[:user_id] = 2
  2019. get(:show, :params => {:id => 1})
  2020. assert_select 'a', :text => /Quote/
  2021. assert_select '#content>.contextual:first-child' do
  2022. assert_select 'a', {:count => 1, :text => 'Edit'}
  2023. assert_select 'a', {:count => 1, :text => 'Log time'}
  2024. assert_select 'a', {:count => 1, :text => 'Watch'}
  2025. assert_select 'a', {:count => 1, :text => 'Copy'}
  2026. assert_select 'div.drdn-items a', {:count => 1, :text => 'Copy link'}
  2027. assert_select 'div.drdn-items a', {:count => 1, :text => 'Delete issue'}
  2028. end
  2029. assert_select 'form#issue-form' do
  2030. assert_select 'fieldset' do
  2031. assert_select 'legend', :text => 'Change properties'
  2032. assert_select 'input[name=?]', 'issue[subject]'
  2033. end
  2034. assert_select 'fieldset' do
  2035. assert_select 'legend', :text => 'Log time'
  2036. assert_select 'input[name=?]', 'time_entry[hours]'
  2037. end
  2038. assert_select 'fieldset' do
  2039. assert_select 'legend', :text => 'Notes'
  2040. assert_select 'textarea[name=?]', 'issue[notes]'
  2041. end
  2042. end
  2043. end
  2044. def test_show_should_display_update_form
  2045. @request.session[:user_id] = 2
  2046. get(:show, :params => {:id => 1})
  2047. assert_response :success
  2048. assert_select 'form#issue-form' do
  2049. assert_select 'input[name=?]', 'issue[is_private]'
  2050. assert_select 'select[name=?]', 'issue[project_id]'
  2051. assert_select 'select[name=?]', 'issue[tracker_id]'
  2052. assert_select 'input[name=?]', 'issue[subject]'
  2053. assert_select 'textarea[name=?]', 'issue[description]'
  2054. assert_select 'select[name=?]', 'issue[status_id]'
  2055. assert_select 'select[name=?]', 'issue[priority_id]'
  2056. assert_select 'select[name=?]', 'issue[assigned_to_id]'
  2057. assert_select 'select[name=?]', 'issue[category_id]'
  2058. assert_select 'select[name=?]', 'issue[fixed_version_id]'
  2059. assert_select 'input[name=?]', 'issue[parent_issue_id]'
  2060. assert_select 'input[name=?]', 'issue[start_date]'
  2061. assert_select 'input[name=?]', 'issue[due_date]'
  2062. assert_select 'select[name=?]', 'issue[done_ratio]'
  2063. assert_select 'input[name=?]', 'issue[custom_field_values][2]'
  2064. assert_select 'input[name=?]', 'issue[watcher_user_ids][]', 0
  2065. assert_select 'textarea[name=?]', 'issue[notes]'
  2066. end
  2067. end
  2068. def test_show_should_display_update_form_with_minimal_permissions
  2069. Role.find(1).update_attribute :permissions, [:view_issues, :add_issue_notes]
  2070. WorkflowTransition.where(:role_id => 1).delete_all
  2071. @request.session[:user_id] = 2
  2072. get(:show, :params => {:id => 1})
  2073. assert_response :success
  2074. assert_select 'form#issue-form' do
  2075. assert_select 'input[name=?]', 'issue[is_private]', 0
  2076. assert_select 'select[name=?]', 'issue[project_id]', 0
  2077. assert_select 'select[name=?]', 'issue[tracker_id]', 0
  2078. assert_select 'input[name=?]', 'issue[subject]', 0
  2079. assert_select 'textarea[name=?]', 'issue[description]', 0
  2080. assert_select 'select[name=?]', 'issue[status_id]', 0
  2081. assert_select 'select[name=?]', 'issue[priority_id]', 0
  2082. assert_select 'select[name=?]', 'issue[assigned_to_id]', 0
  2083. assert_select 'select[name=?]', 'issue[category_id]', 0
  2084. assert_select 'select[name=?]', 'issue[fixed_version_id]', 0
  2085. assert_select 'input[name=?]', 'issue[parent_issue_id]', 0
  2086. assert_select 'input[name=?]', 'issue[start_date]', 0
  2087. assert_select 'input[name=?]', 'issue[due_date]', 0
  2088. assert_select 'select[name=?]', 'issue[done_ratio]', 0
  2089. assert_select 'input[name=?]', 'issue[custom_field_values][2]', 0
  2090. assert_select 'input[name=?]', 'issue[watcher_user_ids][]', 0
  2091. assert_select 'textarea[name=?]', 'issue[notes]'
  2092. end
  2093. end
  2094. def test_show_should_not_display_update_form_without_permissions
  2095. Role.find(1).update_attribute :permissions, [:view_issues]
  2096. @request.session[:user_id] = 2
  2097. get(:show, :params => {:id => 1})
  2098. assert_response :success
  2099. assert_select 'form#issue-form', 0
  2100. end
  2101. def test_update_form_should_not_display_inactive_enumerations
  2102. assert !IssuePriority.find(15).active?
  2103. @request.session[:user_id] = 2
  2104. get(:show, :params => {:id => 1})
  2105. assert_response :success
  2106. assert_select 'form#issue-form' do
  2107. assert_select 'select[name=?]', 'issue[priority_id]' do
  2108. assert_select 'option[value="4"]'
  2109. assert_select 'option[value="15"]', 0
  2110. end
  2111. end
  2112. end
  2113. def test_update_form_should_allow_attachment_upload
  2114. @request.session[:user_id] = 2
  2115. get(:show, :params => {:id => 1})
  2116. assert_select 'form#issue-form[method=post][enctype="multipart/form-data"]' do
  2117. assert_select 'input[type=file][name=?]', 'attachments[dummy][file]'
  2118. end
  2119. end
  2120. def test_update_form_should_render_assign_to_me_link_when_issue_can_be_assigned_to_the_current_user
  2121. @request.session[:user_id] = 1
  2122. get(
  2123. :show,
  2124. :params => {
  2125. :id => 10
  2126. }
  2127. )
  2128. assert_select 'form#issue-form #attributes' do
  2129. assert_select 'a[class=?][data-id=?]', 'assign-to-me-link', '1', 1
  2130. end
  2131. end
  2132. def test_update_form_should_not_render_assign_to_me_link_when_issue_cannot_be_assigned_to_the_current_user
  2133. @request.session[:user_id] = 1
  2134. get(
  2135. :show,
  2136. :params => {
  2137. :id => 2
  2138. }
  2139. )
  2140. assert_select 'form#issue-form #attributes' do
  2141. assert_select 'a[class=?]', 'assign-to-me-link', 0
  2142. end
  2143. end
  2144. def test_update_form_should_not_show_assign_to_me_link_when_issue_is_assigned_to_the_current_user
  2145. issue = Issue.find(10)
  2146. issue.assigned_to_id = 1
  2147. issue.save!
  2148. @request.session[:user_id] = 1
  2149. get(
  2150. :show,
  2151. :params => {
  2152. :id => 10
  2153. }
  2154. )
  2155. assert_select 'form#issue-form #attributes' do
  2156. assert_select 'a[class=?]', 'assign-to-me-link hidden', 1
  2157. end
  2158. end
  2159. def test_show_should_deny_anonymous_access_without_permission
  2160. Role.anonymous.remove_permission!(:view_issues)
  2161. get(:show, :params => {:id => 1})
  2162. assert_response :redirect
  2163. end
  2164. def test_show_should_deny_anonymous_access_to_private_issue
  2165. Issue.where(:id => 1).update_all(["is_private = ?", true])
  2166. get(:show, :params => {:id => 1})
  2167. assert_response :redirect
  2168. end
  2169. def test_show_should_deny_non_member_access_without_permission
  2170. Role.non_member.remove_permission!(:view_issues)
  2171. @request.session[:user_id] = 9
  2172. get(:show, :params => {:id => 1})
  2173. assert_response 403
  2174. end
  2175. def test_show_should_deny_non_member_access_to_private_issue
  2176. Issue.where(:id => 1).update_all(["is_private = ?", true])
  2177. @request.session[:user_id] = 9
  2178. get(:show, :params => {:id => 1})
  2179. assert_response 403
  2180. end
  2181. def test_show_should_deny_member_access_without_permission
  2182. Role.find(1).remove_permission!(:view_issues)
  2183. @request.session[:user_id] = 2
  2184. get(:show, :params => {:id => 1})
  2185. assert_response 403
  2186. end
  2187. def test_show_should_deny_member_access_to_private_issue_without_permission
  2188. Issue.where(:id => 1).update_all(["is_private = ?", true])
  2189. @request.session[:user_id] = 3
  2190. get(:show, :params => {:id => 1})
  2191. assert_response 403
  2192. end
  2193. def test_show_should_allow_author_access_to_private_issue
  2194. Issue.where(:id => 1).update_all(["is_private = ?, author_id = 3", true])
  2195. @request.session[:user_id] = 3
  2196. get(:show, :params => {:id => 1})
  2197. assert_response :success
  2198. end
  2199. def test_show_should_allow_assignee_access_to_private_issue
  2200. Issue.where(:id => 1).update_all(["is_private = ?, assigned_to_id = 3", true])
  2201. @request.session[:user_id] = 3
  2202. get(:show, :params => {:id => 1})
  2203. assert_response :success
  2204. end
  2205. def test_show_should_allow_member_access_to_private_issue_with_permission
  2206. Issue.where(:id => 1).update_all(["is_private = ?", true])
  2207. User.find(3).roles_for_project(Project.find(1)).first.update_attribute :issues_visibility, 'all'
  2208. @request.session[:user_id] = 3
  2209. get(:show, :params => {:id => 1})
  2210. assert_response :success
  2211. end
  2212. def test_show_should_format_related_issues_dates
  2213. with_settings :date_format => '%d/%m/%Y' do
  2214. issue = Issue.generate!(:start_date => '2018-11-29', :due_date => '2018-12-01')
  2215. IssueRelation.
  2216. create!(
  2217. :issue_from => Issue.find(1), :issue_to => issue,
  2218. :relation_type => 'relates'
  2219. )
  2220. get(:show, :params => {:id => 1})
  2221. assert_response :success
  2222. assert_select '#relations td.start_date', :text => '29/11/2018'
  2223. assert_select '#relations td.due_date', :text => '01/12/2018'
  2224. end
  2225. end
  2226. def test_show_should_not_disclose_relations_to_invisible_issues
  2227. with_settings :cross_project_issue_relations => '1' do
  2228. IssueRelation.
  2229. create!(
  2230. :issue_from => Issue.find(1),
  2231. :issue_to => Issue.find(2),
  2232. :relation_type => 'relates'
  2233. )
  2234. # Relation to a private project issue
  2235. IssueRelation.
  2236. create!(
  2237. :issue_from => Issue.find(1),
  2238. :issue_to => Issue.find(4),
  2239. :relation_type => 'relates'
  2240. )
  2241. get(:show, :params => {:id => 1})
  2242. assert_response :success
  2243. assert_select 'div#relations' do
  2244. assert_select 'a', :text => /#2$/
  2245. assert_select 'a', :text => /#4$/, :count => 0
  2246. end
  2247. end
  2248. end
  2249. def test_show_should_list_subtasks
  2250. issue = Issue.
  2251. create!(
  2252. :project_id => 1, :author_id => 1, :tracker_id => 1,
  2253. :parent_issue_id => 1, :subject => 'Child Issue'
  2254. )
  2255. get(:show, :params => {:id => 1})
  2256. assert_response :success
  2257. assert_select 'div#issue_tree' do
  2258. assert_select 'td.subject', :text => /Child Issue/
  2259. end
  2260. assert_select 'div#tab-content-history' do
  2261. assert_select 'div[id=?]', "change-#{Issue.find(1).journals.last.id}" do
  2262. assert_select 'ul.details', :text => "Subtask ##{issue.id} added"
  2263. end
  2264. end
  2265. end
  2266. def test_show_should_show_subtasks_stats
  2267. @request.session[:user_id] = 1
  2268. child1 = Issue.generate!(parent_issue_id: 1, subject: 'Open child issue')
  2269. Issue.generate!(parent_issue_id: 1, subject: 'Closed child issue', status_id: 5)
  2270. Issue.generate!(parent_issue_id: child1.id, subject: 'Open child of child')
  2271. # Issue not visible for anonymous
  2272. Issue.generate!(parent_issue_id: 1, subject: 'Private child', project_id: 5)
  2273. get(:show, params: {:id => 1})
  2274. assert_response :success
  2275. assert_select 'div#issue_tree span.issues-stat' do
  2276. assert_select 'span.badge', text: '4'
  2277. assert_select 'span.open a', text: '3 open'
  2278. assert_equal CGI.unescape(css_select('span.open a').first.attr(:href)),
  2279. "/issues?parent_id=~1&set_filter=true&status_id=o"
  2280. assert_select 'span.closed a', text: '1 closed'
  2281. assert_equal CGI.unescape(css_select('span.closed a').first.attr(:href)),
  2282. "/issues?parent_id=~1&set_filter=true&status_id=c"
  2283. end
  2284. end
  2285. def test_show_subtasks_stats_should_not_link_if_issue_has_zero_open_or_closed_subtasks
  2286. child1 = Issue.generate!(parent_issue_id: 1, subject: 'Open child issue')
  2287. get(:show, params: {:id => 1})
  2288. assert_response :success
  2289. assert_select 'div#issue_tree span.issues-stat' do
  2290. assert_select 'span.open a', text: '1 open'
  2291. assert_equal CGI.unescape(css_select('span.open a').first.attr(:href)),
  2292. "/issues?parent_id=~1&set_filter=true&status_id=o"
  2293. assert_select 'span.closed', text: '0 closed'
  2294. assert_select 'span.closed a', 0
  2295. end
  2296. end
  2297. def test_show_should_not_show_subtasks_stats_if_subtasks_are_not_visible
  2298. # Issue not visible for anonymous
  2299. Issue.generate!(parent_issue_id: 1, subject: 'Private child', project_id: 5)
  2300. get(:show, params: {:id => 1})
  2301. assert_response :success
  2302. assert_select 'div#issue_tree span.issues-stat', 0
  2303. end
  2304. def test_show_should_list_parents
  2305. issue = Issue.
  2306. create!(
  2307. :project_id => 1, :author_id => 1,
  2308. :tracker_id => 1, :parent_issue_id => 1,
  2309. :subject => 'Child Issue'
  2310. )
  2311. get(:show, :params => {:id => issue.id})
  2312. assert_response :success
  2313. assert_select 'div.subject' do
  2314. assert_select 'h3', 'Child Issue'
  2315. assert_select 'a[href="/issues/1"]'
  2316. end
  2317. end
  2318. def test_show_should_not_display_prev_next_links_without_query_in_session
  2319. get(:show, :params => {:id => 1})
  2320. assert_response :success
  2321. assert_select 'div.next-prev-links', 0
  2322. end
  2323. def test_show_should_display_prev_next_links_with_query_in_session
  2324. @request.session[:issue_query] =
  2325. {
  2326. :filters => {
  2327. 'status_id' => {
  2328. :values => [''], :operator => 'o'
  2329. }
  2330. },
  2331. :project_id => nil,
  2332. :sort => [['id', 'asc']]
  2333. }
  2334. with_settings :display_subprojects_issues => '0' do
  2335. get(:show, :params => {:id => 3})
  2336. end
  2337. assert_response :success
  2338. count = Issue.open.visible.count
  2339. # Previous and next issues for all projects
  2340. assert_select 'div.next-prev-links' do
  2341. assert_select 'a[href="/issues/2"]', :text => /Previous/
  2342. assert_select 'a[href="/issues/5"]', :text => /Next/
  2343. assert_select 'span.position', :text => "3 of #{count}"
  2344. end
  2345. end
  2346. def test_show_should_display_prev_next_links_with_saved_query_in_session
  2347. query =
  2348. IssueQuery.create!(
  2349. :name => 'test',
  2350. :visibility => IssueQuery::VISIBILITY_PUBLIC,
  2351. :user_id => 1,
  2352. :filters => {'status_id' => {:values => ['5'], :operator => '='}},
  2353. :sort_criteria => [['id', 'asc']]
  2354. )
  2355. @request.session[:issue_query] = {:id => query.id, :project_id => nil}
  2356. get(:show, :params => {:id => 11})
  2357. assert_response :success
  2358. # Previous and next issues for all projects
  2359. assert_select 'div.next-prev-links' do
  2360. assert_select 'a[href="/issues/8"]', :text => /Previous/
  2361. assert_select 'a[href="/issues/12"]', :text => /Next/
  2362. end
  2363. end
  2364. def test_show_should_display_prev_next_links_with_query_and_sort_on_association
  2365. @request.session[:issue_query] =
  2366. {
  2367. :filters => {
  2368. 'status_id' => {
  2369. :values => [''], :operator => 'o'
  2370. }
  2371. },
  2372. :project_id => nil
  2373. }
  2374. %w(project tracker status priority author assigned_to category fixed_version).
  2375. each do |assoc_sort|
  2376. @request.session[:issue_query][:sort] = [[assoc_sort, 'asc']]
  2377. get(:show, :params => {:id => 3})
  2378. assert_response :success, "Wrong response status for #{assoc_sort} sort"
  2379. assert_select 'div.next-prev-links' do
  2380. assert_select 'a', :text => /(Previous|Next)/
  2381. end
  2382. end
  2383. end
  2384. def test_show_should_display_prev_next_links_with_project_query_in_session
  2385. @request.session[:issue_query] =
  2386. {
  2387. :filters => {
  2388. 'status_id' => {:values => [''], :operator => 'o'}
  2389. },
  2390. :project_id => 1, :sort => [['id', 'asc']]
  2391. }
  2392. with_settings :display_subprojects_issues => '0' do
  2393. get(:show, :params => {:id => 3})
  2394. end
  2395. assert_response :success
  2396. # Previous and next issues inside project
  2397. assert_select 'div.next-prev-links' do
  2398. assert_select 'a[href="/issues/2"]', :text => /Previous/
  2399. assert_select 'a[href="/issues/7"]', :text => /Next/
  2400. end
  2401. end
  2402. def test_show_should_not_display_prev_link_for_first_issue
  2403. @request.session[:issue_query] =
  2404. {
  2405. :filters => {
  2406. 'status_id' => {:values => [''], :operator => 'o'}
  2407. },
  2408. :project_id => 1, :sort => [['id', 'asc']]
  2409. }
  2410. with_settings :display_subprojects_issues => '0' do
  2411. get(:show, :params => {:id => 1})
  2412. end
  2413. assert_response :success
  2414. assert_select 'div.next-prev-links' do
  2415. assert_select 'a', :text => /Previous/, :count => 0
  2416. assert_select 'a[href="/issues/2"]', :text => /Next/
  2417. end
  2418. end
  2419. def test_show_should_not_display_prev_next_links_for_issue_not_in_query_results
  2420. @request.session[:issue_query] =
  2421. {
  2422. :filters => {
  2423. 'status_id' => {:values => [''], :operator => 'c'}
  2424. },
  2425. :project_id => 1,
  2426. :sort => [['id', 'asc']]
  2427. }
  2428. get(:show, :params => {:id => 1})
  2429. assert_response :success
  2430. assert_select 'a', :text => /Previous/, :count => 0
  2431. assert_select 'a', :text => /Next/, :count => 0
  2432. end
  2433. def test_show_show_should_display_prev_next_links_with_query_sort_by_user_custom_field
  2434. cf = IssueCustomField.
  2435. create!(
  2436. :name => 'User',
  2437. :is_for_all => true,
  2438. :tracker_ids => [1, 2, 3],
  2439. :field_format => 'user'
  2440. )
  2441. CustomValue.create!(:custom_field => cf, :customized => Issue.find(1), :value => '2')
  2442. CustomValue.create!(:custom_field => cf, :customized => Issue.find(2), :value => '3')
  2443. CustomValue.create!(:custom_field => cf, :customized => Issue.find(3), :value => '3')
  2444. CustomValue.create!(:custom_field => cf, :customized => Issue.find(5), :value => '')
  2445. query =
  2446. IssueQuery.create!(
  2447. :name => 'test',
  2448. :visibility => IssueQuery::VISIBILITY_PUBLIC,
  2449. :user_id => 1, :filters => {},
  2450. :sort_criteria => [["cf_#{cf.id}", 'asc'], ['id', 'asc']]
  2451. )
  2452. @request.session[:issue_query] = {:id => query.id, :project_id => nil}
  2453. get(:show, :params => {:id => 3})
  2454. assert_response :success
  2455. assert_select 'div.next-prev-links' do
  2456. assert_select 'a[href="/issues/2"]', :text => /Previous/
  2457. assert_select 'a[href="/issues/1"]', :text => /Next/
  2458. end
  2459. end
  2460. def test_show_should_display_prev_next_links_when_request_has_previous_and_next_issue_ids_params
  2461. get(
  2462. :show,
  2463. :params => {
  2464. :id => 1,
  2465. :prev_issue_id => 1,
  2466. :next_issue_id => 3,
  2467. :issue_position => 2,
  2468. :issue_count => 4
  2469. }
  2470. )
  2471. assert_response :success
  2472. assert_select 'div.next-prev-links' do
  2473. assert_select 'a[href="/issues/1"]', :text => /Previous/
  2474. assert_select 'a[href="/issues/3"]', :text => /Next/
  2475. assert_select 'span.position', :text => "2 of 4"
  2476. end
  2477. end
  2478. def test_show_should_display_category_field_if_categories_are_defined
  2479. Issue.update_all :category_id => nil
  2480. get(:show, :params => {:id => 1})
  2481. assert_response :success
  2482. assert_select '.attributes .category'
  2483. end
  2484. def test_show_should_not_display_category_field_if_no_categories_are_defined
  2485. Project.find(1).issue_categories.delete_all
  2486. get(:show, :params => {:id => 1})
  2487. assert_response :success
  2488. assert_select 'table.attributes .category', 0
  2489. end
  2490. def test_show_should_display_link_to_the_assigned_user
  2491. get(:show, :params => {:id => 2})
  2492. assert_response :success
  2493. assert_select '.assigned-to' do
  2494. assert_select 'a[href="/users/3"]'
  2495. end
  2496. end
  2497. def test_show_should_display_link_to_the_assigned_group
  2498. Issue.find(2).update_attribute(:assigned_to_id, 10)
  2499. get(:show, :params => {:id => 2})
  2500. assert_response :success
  2501. assert_select '.assigned-to' do
  2502. assert_select 'a[href="/groups/10"]'
  2503. end
  2504. end
  2505. def test_show_should_display_visible_changesets_from_other_projects
  2506. project = Project.find(2)
  2507. issue = project.issues.first
  2508. issue.changeset_ids = [102]
  2509. issue.save!
  2510. # changesets from other projects should be displayed even if repository
  2511. # is disabled on issue's project
  2512. project.disable_module! :repository
  2513. @request.session[:user_id] = 2
  2514. get(
  2515. :issue_tab,
  2516. :params => {
  2517. :id => issue.id,
  2518. :name => 'changesets'
  2519. },
  2520. :xhr => true
  2521. )
  2522. assert_select 'a[href=?]', '/projects/ecookbook/repository/10/revisions/3'
  2523. assert_select 'div.changeset p', :text => /eCookbook - /
  2524. end
  2525. def test_show_should_display_watchers
  2526. @request.session[:user_id] = 2
  2527. issue = Issue.find(1)
  2528. issue.add_watcher User.find(2)
  2529. issue.add_watcher Group.find(10)
  2530. get(:show, :params => {:id => 1})
  2531. assert_select 'div#watchers ul' do
  2532. assert_select 'li.user-2' do
  2533. assert_select 'a[href="/users/2"]'
  2534. assert_select 'a[class*=delete]'
  2535. end
  2536. assert_select "li.user-10" do
  2537. assert_select 'a[href="/users/10"]', false
  2538. assert_select 'a[class*=delete]'
  2539. end
  2540. end
  2541. end
  2542. def test_show_should_display_watchers_with_gravatars
  2543. @request.session[:user_id] = 2
  2544. issue = Issue.find(1)
  2545. issue.add_watcher User.find(2)
  2546. issue.add_watcher Group.find(10)
  2547. with_settings :gravatar_enabled => '1' do
  2548. get(:show, :params => {:id => 1})
  2549. end
  2550. assert_select 'div#watchers ul' do
  2551. assert_select 'li.user-2' do
  2552. assert_select 'img.gravatar[title=?]', 'John Smith'
  2553. assert_select 'a[href="/users/2"]'
  2554. assert_select 'a[class*=delete]'
  2555. end
  2556. assert_select "li.user-10" do
  2557. assert_select 'img.gravatar[title=?]', 'A Team'
  2558. assert_select 'a[href="/groups/10"]'
  2559. assert_select 'a[class*=delete]'
  2560. end
  2561. end
  2562. end
  2563. def test_show_should_mark_invalid_watchers
  2564. @request.session[:user_id] = 2
  2565. issue = Issue.find(4)
  2566. issue.add_watcher User.find(4)
  2567. get :show, :params => {:id => issue.id}
  2568. assert_response :success
  2569. assert_select 'div#watchers ul' do
  2570. assert_select 'li.user-4' do
  2571. assert_select 'span.icon-warning[title=?]', l(:notice_invalid_watcher), text: l(:notice_invalid_watcher)
  2572. end
  2573. end
  2574. end
  2575. def test_show_with_thumbnails_enabled_should_display_thumbnails
  2576. skip unless convert_installed?
  2577. @request.session[:user_id] = 2
  2578. with_settings :thumbnails_enabled => '1' do
  2579. get(:show, :params => {:id => 14})
  2580. assert_response :success
  2581. end
  2582. assert_select 'div.thumbnails' do
  2583. assert_select 'a[href="/attachments/16"]' do
  2584. assert_select 'img[src="/attachments/thumbnail/16/200"]'
  2585. end
  2586. end
  2587. end
  2588. def test_show_with_thumbnails_disabled_should_not_display_thumbnails
  2589. @request.session[:user_id] = 2
  2590. with_settings :thumbnails_enabled => '0' do
  2591. get(:show, :params => {:id => 14})
  2592. assert_response :success
  2593. end
  2594. assert_select 'div.thumbnails', 0
  2595. end
  2596. def test_show_with_multi_custom_field
  2597. field = CustomField.find(1)
  2598. field.update_attribute :multiple, true
  2599. issue = Issue.find(1)
  2600. issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
  2601. issue.save!
  2602. get(:show, :params => {:id => 1})
  2603. assert_response :success
  2604. assert_select ".cf_1 .value", :text => 'MySQL, Oracle'
  2605. end
  2606. def test_show_with_full_width_layout_custom_field_should_show_field_under_description
  2607. field =
  2608. IssueCustomField.
  2609. create!(
  2610. :name => 'Long text',
  2611. :field_format => 'text', :full_width_layout => '1',
  2612. :tracker_ids => [1], :is_for_all => true
  2613. )
  2614. issue = Issue.find(1)
  2615. issue.custom_field_values = {field.id => 'This is a long text'}
  2616. issue.save!
  2617. get(:show, :params => {:id => 1})
  2618. assert_response :success
  2619. # long text custom field should not be render in the attributes div
  2620. assert_select "div.attributes div.attribute.cf_#{field.id} p strong", 0
  2621. assert_select "div.attributes div.attribute.cf_#{field.id} div.value", 0
  2622. # long text custom field should be render under description field
  2623. assert_select "div.description ~ div.attribute.cf_#{field.id} p strong", :text => 'Long text'
  2624. assert_select(
  2625. "div.description ~ div.attribute.cf_#{field.id} div.value",
  2626. :text => 'This is a long text'
  2627. )
  2628. end
  2629. def test_show_custom_fields_with_full_text_formatting_should_be_rendered_using_wiki_class
  2630. half_field =
  2631. IssueCustomField.
  2632. create!(
  2633. :name => 'Half width field', :field_format => 'text',
  2634. :tracker_ids => [1], :is_for_all => true, :text_formatting => 'full'
  2635. )
  2636. full_field =
  2637. IssueCustomField.
  2638. create!(
  2639. :name => 'Full width field',
  2640. :field_format => 'text', :full_width_layout => '1',
  2641. :tracker_ids => [1], :is_for_all => true, :text_formatting => 'full'
  2642. )
  2643. issue = Issue.find(1)
  2644. issue.custom_field_values =
  2645. {full_field.id => 'This is a long text',
  2646. half_field.id => 'This is a short text'}
  2647. issue.save!
  2648. get(:show, :params => {:id => 1})
  2649. assert_response :success
  2650. assert_select "div.attribute.cf_#{half_field.id} div.value div.wiki", 1
  2651. assert_select "div.attribute.cf_#{full_field.id} div.value div.wiki", 1
  2652. end
  2653. def test_show_with_multi_user_custom_field
  2654. field =
  2655. IssueCustomField.
  2656. create!(
  2657. :name => 'Multi user',
  2658. :field_format => 'user', :multiple => true,
  2659. :tracker_ids => [1], :is_for_all => true
  2660. )
  2661. issue = Issue.find(1)
  2662. issue.custom_field_values = {field.id => ['2', '3']}
  2663. issue.save!
  2664. get(:show, :params => {:id => 1})
  2665. assert_response :success
  2666. assert_select ".cf_#{field.id} .value", :text => 'Dave Lopper, John Smith' do
  2667. assert_select 'a', :text => 'Dave Lopper'
  2668. assert_select 'a', :text => 'John Smith'
  2669. end
  2670. end
  2671. def test_show_should_not_display_default_value_for_new_custom_field
  2672. prior = Issue.generate!
  2673. field =
  2674. IssueCustomField.
  2675. generate!(
  2676. :name => 'WithDefault', :field_format => 'string',
  2677. :default_value => 'DEFAULT'
  2678. )
  2679. after = Issue.generate!
  2680. get :show, :params => {:id => prior.id}
  2681. assert_response :success
  2682. assert_select ".cf_#{field.id} .value", :text => ''
  2683. get :show, :params => {:id => after.id}
  2684. assert_response :success
  2685. assert_select ".cf_#{field.id} .value", :text => 'DEFAULT'
  2686. end
  2687. def test_show_should_display_private_notes_with_permission_only
  2688. journal =
  2689. Journal.
  2690. create!(
  2691. :journalized => Issue.find(2),
  2692. :notes => 'Privates notes',
  2693. :private_notes => true,
  2694. :user_id => 1
  2695. )
  2696. @request.session[:user_id] = 2
  2697. get(:show, :params => {:id => 2})
  2698. assert_response :success
  2699. assert_select "#change-#{journal.id}", 1
  2700. Role.find(1).remove_permission! :view_private_notes
  2701. get(:show, :params => {:id => 2})
  2702. assert_response :success
  2703. assert_select "#change-#{journal.id}", 0
  2704. end
  2705. def test_show_should_display_private_notes_created_by_current_user
  2706. User.find(3).roles_for_project(Project.find(1)).each do |role|
  2707. role.remove_permission! :view_private_notes
  2708. end
  2709. visible =
  2710. Journal.
  2711. create!(
  2712. :journalized => Issue.find(2),
  2713. :notes => 'Private notes',
  2714. :private_notes => true, :user_id => 3
  2715. )
  2716. not_visible =
  2717. Journal.create!(
  2718. :journalized => Issue.find(2),
  2719. :notes => 'Private notes',
  2720. :private_notes => true, :user_id => 1
  2721. )
  2722. @request.session[:user_id] = 3
  2723. get(:show, :params => {:id => 2})
  2724. assert_response :success
  2725. assert_select "#change-#{visible.id}", 1
  2726. assert_select "#change-#{not_visible.id}", 0
  2727. end
  2728. def test_show_should_mark_notes_as_edited_only_for_edited_notes
  2729. get :show, :params => {:id => 1}
  2730. assert_response :success
  2731. journal = Journal.find(1)
  2732. journal_title = l(:label_time_by_author, :time => format_time(journal.updated_on), :author => journal.updated_by)
  2733. assert_select "#change-1 h4 span.update-info[title=?]", journal_title, :text => '· Edited'
  2734. assert_select "#change-2 h4 span.update-info", 0
  2735. end
  2736. def test_show_atom
  2737. with_settings :text_formatting => 'textile' do
  2738. get(
  2739. :show,
  2740. :params => {
  2741. :id => 2,
  2742. :format => 'atom'
  2743. }
  2744. )
  2745. assert_response :success
  2746. assert_equal 'application/atom+xml', response.media_type
  2747. # Inline image
  2748. assert_select(
  2749. 'content',
  2750. :text => Regexp.new(Regexp.quote('http://test.host/attachments/download/10'))
  2751. )
  2752. end
  2753. end
  2754. def test_show_export_to_pdf
  2755. issue = Issue.find(3)
  2756. assert issue.relations.any? {|r| r.other_issue(issue).visible?}
  2757. get(
  2758. :show,
  2759. :params => {
  2760. :id => 3,
  2761. :format => 'pdf'
  2762. }
  2763. )
  2764. assert_response :success
  2765. assert_equal 'application/pdf', @response.media_type
  2766. assert @response.body.starts_with?('%PDF')
  2767. end
  2768. def test_export_to_pdf_with_utf8_u_fffd
  2769. issue = Issue.generate!(:subject => "�")
  2770. ["en", "zh", "zh-TW", "ja", "ko", "ar"].each do |lang|
  2771. with_settings :default_language => lang do
  2772. get(
  2773. :show,
  2774. :params => {
  2775. :id => issue.id,
  2776. :format => 'pdf'
  2777. }
  2778. )
  2779. assert_response :success
  2780. assert_equal 'application/pdf', @response.media_type
  2781. assert @response.body.starts_with?('%PDF')
  2782. end
  2783. end
  2784. end
  2785. def test_show_export_to_pdf_with_ancestors
  2786. issue = Issue.generate!(:project_id => 1, :author_id => 2,
  2787. :tracker_id => 1, :subject => 'child',
  2788. :parent_issue_id => 1)
  2789. get(
  2790. :show,
  2791. :params => {
  2792. :id => issue.id,
  2793. :format => 'pdf'
  2794. }
  2795. )
  2796. assert_response :success
  2797. assert_equal 'application/pdf', @response.media_type
  2798. assert @response.body.starts_with?('%PDF')
  2799. end
  2800. def test_show_export_to_pdf_with_descendants
  2801. c1 = Issue.generate!(:project_id => 1, :author_id => 2, :tracker_id => 1,
  2802. :subject => 'child', :parent_issue_id => 1)
  2803. c2 = Issue.generate!(:project_id => 1, :author_id => 2, :tracker_id => 1,
  2804. :subject => 'child', :parent_issue_id => 1)
  2805. c3 = Issue.generate!(:project_id => 1, :author_id => 2, :tracker_id => 1,
  2806. :subject => 'child', :parent_issue_id => c1.id)
  2807. get(
  2808. :show,
  2809. :params => {
  2810. :id => 1,
  2811. :format => 'pdf'
  2812. }
  2813. )
  2814. assert_response :success
  2815. assert_equal 'application/pdf', @response.media_type
  2816. assert @response.body.starts_with?('%PDF')
  2817. end
  2818. def test_show_export_to_pdf_with_journals
  2819. get(
  2820. :show,
  2821. :params => {
  2822. :id => 1,
  2823. :format => 'pdf'
  2824. }
  2825. )
  2826. assert_response :success
  2827. assert_equal 'application/pdf', @response.media_type
  2828. assert @response.body.starts_with?('%PDF')
  2829. end
  2830. def test_show_export_to_pdf_with_private_journal
  2831. Journal.create!(
  2832. :journalized => Issue.find(1),
  2833. :notes => 'Private notes',
  2834. :private_notes => true,
  2835. :user_id => 3
  2836. )
  2837. @request.session[:user_id] = 3
  2838. get(
  2839. :show,
  2840. :params => {
  2841. :id => 1,
  2842. :format => 'pdf'
  2843. }
  2844. )
  2845. assert_response :success
  2846. assert_equal 'application/pdf', @response.media_type
  2847. assert @response.body.starts_with?('%PDF')
  2848. end
  2849. def test_show_export_to_pdf_with_changesets
  2850. [[100], [100, 101], [100, 101, 102]].each do |cs|
  2851. issue1 = Issue.find(3)
  2852. issue1.changesets = Changeset.find(cs)
  2853. issue1.save!
  2854. issue = Issue.find(3)
  2855. assert_equal issue.changesets.count, cs.size
  2856. get(
  2857. :show,
  2858. :params => {
  2859. :id => 3,
  2860. :format => 'pdf'
  2861. }
  2862. )
  2863. assert_response :success
  2864. assert_equal 'application/pdf', @response.media_type
  2865. assert @response.body.starts_with?('%PDF')
  2866. end
  2867. end
  2868. def test_show_invalid_should_respond_with_404
  2869. get(:show, :params => {:id => 999})
  2870. assert_response 404
  2871. end
  2872. def test_show_on_active_project_should_display_edit_links
  2873. @request.session[:user_id] = 1
  2874. get(:show, :params => {:id => 1})
  2875. assert_response :success
  2876. assert_select 'a', :text => 'Edit'
  2877. assert_select 'a', :text => 'Delete issue'
  2878. end
  2879. def test_show_on_closed_project_should_not_display_edit_links
  2880. Issue.find(1).project.close
  2881. @request.session[:user_id] = 1
  2882. get(:show, :params => {:id => 1})
  2883. assert_response :success
  2884. assert_select 'a', :text => 'Edit', :count => 0
  2885. assert_select 'a', :text => 'Delete issue', :count => 0
  2886. end
  2887. def test_show_should_not_display_history_tabs_for_issue_without_journals
  2888. @request.session[:user_id] = 1
  2889. get :show, :params => {:id => 5}
  2890. assert_response :success
  2891. assert_select '#history div.tabs', 0
  2892. assert_select '#history p.nodata', :text => 'No data to display'
  2893. end
  2894. def test_show_display_only_all_and_notes_tabs_for_issue_with_notes_only
  2895. @request.session[:user_id] = 1
  2896. get :show, :params => {:id => 14}
  2897. assert_response :success
  2898. assert_select '#history' do
  2899. assert_select 'div.tabs ul a', 2
  2900. assert_select 'div.tabs a[id=?]', 'tab-history', :text => 'History'
  2901. assert_select 'div.tabs a[id=?]', 'tab-notes', :text => 'Notes'
  2902. end
  2903. end
  2904. def test_show_display_only_all_and_history_tabs_for_issue_with_history_changes_only
  2905. journal = Journal.create!(:journalized => Issue.find(5), :user_id => 1)
  2906. detail =
  2907. JournalDetail.
  2908. create!(
  2909. :journal => journal, :property => 'attr',
  2910. :prop_key => 'description',
  2911. :old_value => 'Foo', :value => 'Bar'
  2912. )
  2913. @request.session[:user_id] = 1
  2914. get :show, :params => {:id => 5}
  2915. assert_response :success
  2916. assert_select '#history' do
  2917. assert_select 'div.tabs ul a', 2
  2918. assert_select 'div.tabs a[id=?]', 'tab-history', :text => 'History'
  2919. assert_select 'div.tabs a[id=?]', 'tab-properties', :text => 'Property changes'
  2920. end
  2921. end
  2922. def test_show_display_all_notes_and_history_tabs_for_issue_with_notes_and_history_changes
  2923. journal = Journal.create!(:journalized => Issue.find(6), :user_id => 1)
  2924. @request.session[:user_id] = 1
  2925. get :show, :params => {:id => 6}
  2926. assert_response :success
  2927. assert_select '#history' do
  2928. assert_select 'div.tabs ul a', 3
  2929. assert_select 'div.tabs a[id=?]', 'tab-history', :text => 'History'
  2930. assert_select 'div.tabs a[id=?]', 'tab-notes', :text => 'Notes'
  2931. assert_select 'div.tabs a[id=?]', 'tab-properties', :text => 'Property changes'
  2932. end
  2933. end
  2934. def test_show_display_changesets_tab_for_issue_with_changesets
  2935. project = Project.find(2)
  2936. issue = Issue.find(9)
  2937. issue.changeset_ids = [102]
  2938. issue.save!
  2939. @request.session[:user_id] = 2
  2940. get :show, :params => {:id => issue.id}
  2941. assert_select '#history' do
  2942. assert_select 'div.tabs ul a', 1
  2943. assert_select 'div.tabs a[id=?]', 'tab-changesets', :text => 'Associated revisions'
  2944. end
  2945. end
  2946. def test_show_should_display_spent_time_tab_for_issue_with_time_entries
  2947. @request.session[:user_id] = 1
  2948. get :show, :params => {:id => 3}
  2949. assert_response :success
  2950. assert_select '#history' do
  2951. assert_select 'div.tabs ul a', 1
  2952. assert_select 'div.tabs a[id=?]', 'tab-time_entries', :text => 'Spent time'
  2953. end
  2954. get(
  2955. :issue_tab,
  2956. :params => {
  2957. :id => 3,
  2958. :name => 'time_entries'
  2959. },
  2960. :xhr => true
  2961. )
  2962. assert_response :success
  2963. assert_select 'div[id=?]', 'time-entry-3' do
  2964. assert_select 'a[title=?][href=?]', 'Edit', '/time_entries/3/edit'
  2965. assert_select 'a[title=?][href=?]', 'Delete', '/time_entries/3'
  2966. assert_select 'ul[class=?]', 'details', :text => /1.00 h/
  2967. end
  2968. end
  2969. def test_show_should_display_open_badge_for_open_issue
  2970. get :show, params: {id: 1}
  2971. assert_response :success
  2972. assert_select 'span.badge.badge-status-open', text: 'open'
  2973. end
  2974. def test_show_should_display_closed_badge_for_closed_issue
  2975. get :show, params: {id: 8}
  2976. assert_response :success
  2977. assert_select 'span.badge.badge-status-closed', text: 'closed'
  2978. end
  2979. def test_show_should_display_private_badge_for_private_issue
  2980. @request.session[:user_id] = 1
  2981. get :show, params: {id: 14}
  2982. assert_response :success
  2983. assert_select 'span.badge.badge-private', text: 'Private'
  2984. end
  2985. def test_show_should_not_display_edit_attachment_icon_for_user_without_edit_issue_permission_on_tracker
  2986. role = Role.find(2)
  2987. role.set_permission_trackers 'edit_issues', [2, 3]
  2988. role.save!
  2989. @request.session[:user_id] = 2
  2990. get :show, params: {id: 4}
  2991. assert_response :success
  2992. assert_select 'div.attachments .icon-edit', 0
  2993. end
  2994. def test_show_should_not_display_delete_attachment_icon_for_user_without_edit_issue_permission_on_tracker
  2995. role = Role.find(2)
  2996. role.set_permission_trackers 'edit_issues', [2, 3]
  2997. role.save!
  2998. @request.session[:user_id] = 2
  2999. get :show, params: {id: 4}
  3000. assert_response :success
  3001. assert_select 'div.attachments .icon-del', 0
  3002. end
  3003. def test_get_new
  3004. @request.session[:user_id] = 2
  3005. get(
  3006. :new,
  3007. :params => {
  3008. :project_id => 1,
  3009. :tracker_id => 1
  3010. }
  3011. )
  3012. assert_response :success
  3013. assert_select 'form#issue-form[action=?]', '/projects/ecookbook/issues'
  3014. assert_select 'form#issue-form' do
  3015. assert_select 'input[name=?]', 'issue[is_private]'
  3016. assert_select 'select[name=?]', 'issue[project_id]'
  3017. assert_select 'select[name=?]', 'issue[tracker_id]'
  3018. assert_select 'input[name=?]', 'issue[subject]'
  3019. assert_select 'textarea[name=?]', 'issue[description]'
  3020. assert_select 'select[name=?]', 'issue[status_id]'
  3021. assert_select 'select[name=?]', 'issue[priority_id]'
  3022. assert_select 'select[name=?]', 'issue[assigned_to_id]'
  3023. assert_select 'select[name=?]', 'issue[category_id]'
  3024. assert_select 'select[name=?]', 'issue[fixed_version_id]'
  3025. assert_select 'input[name=?]', 'issue[parent_issue_id]'
  3026. assert_select 'input[name=?]', 'issue[start_date]'
  3027. assert_select 'input[name=?]', 'issue[due_date]'
  3028. assert_select 'select[name=?]', 'issue[done_ratio]'
  3029. assert_select 'input[name=?][value=?]', 'issue[custom_field_values][2]', 'Default string'
  3030. assert_select 'input[name=?]', 'issue[watcher_user_ids][]'
  3031. # Assert submit buttons
  3032. assert_select 'input[type=submit][name=?]', 'commit'
  3033. assert_select 'input[type=submit][name=?]', 'continue'
  3034. assert_select 'input[type=submit][name=?]', 'follow', 0
  3035. end
  3036. # Be sure we don't display inactive IssuePriorities
  3037. assert_not IssuePriority.find(15).active?
  3038. assert_select 'select[name=?]', 'issue[priority_id]' do
  3039. assert_select 'option[value="15"]', 0
  3040. end
  3041. end
  3042. def test_get_new_global_should_show_all_projects
  3043. @request.session[:user_id] = 1
  3044. get :new
  3045. assert_response :success
  3046. assert_select 'select[name=?]', 'issue[project_id]' do
  3047. assert_select 'option[value=?]', '1'
  3048. assert_select 'option[value=?]', '2'
  3049. end
  3050. end
  3051. def test_get_new_should_show_project_selector_for_project_with_subprojects
  3052. @request.session[:user_id] = 2
  3053. get(
  3054. :new,
  3055. :params => {
  3056. :project_id => 3,
  3057. :tracker_id => 1
  3058. }
  3059. )
  3060. assert_response :success
  3061. assert_select 'select[name="issue[project_id]"]' do
  3062. assert_select 'option', 3
  3063. assert_select 'option[value=?]', '1', :text => 'eCookbook'
  3064. assert_select 'option[value=?]', '5', :text => '  » Private child of eCookbook'
  3065. assert_select 'option[selected=selected][value=?]', '3', :text => '  » eCookbook Subproject 1'
  3066. # user_id 2 is not allowed to add issues on project_id 4 (it's not a member)
  3067. assert_select 'option[value=?]', '4', 0
  3068. end
  3069. end
  3070. def test_get_new_should_not_show_project_selector_for_project_without_subprojects
  3071. @request.session[:user_id] = 2
  3072. get(
  3073. :new,
  3074. :params => {
  3075. :project_id => 2,
  3076. :tracker_id => 1
  3077. }
  3078. )
  3079. assert_response :success
  3080. assert_select 'select[name="issue[project_id]"]', 0
  3081. end
  3082. def test_get_new_should_not_show_invalid_projects_when_issue_is_a_subtask
  3083. @request.session[:user_id] = 2
  3084. issue = Issue.find(1)
  3085. issue.parent_id = 3
  3086. issue.save
  3087. with_settings :cross_project_subtasks => 'tree' do
  3088. get(
  3089. :new,
  3090. :params => {
  3091. :project_id => 1,
  3092. :parent_issue_id => 1
  3093. }
  3094. )
  3095. end
  3096. assert_response :success
  3097. assert_select 'select[name="issue[project_id]"]' do
  3098. assert_select 'option', 3
  3099. # Onlinestore project should not be included
  3100. assert_select 'option[value=?]', '2', 0
  3101. end
  3102. end
  3103. def test_get_new_with_minimal_permissions
  3104. Role.find(1).update_attribute :permissions, [:add_issues]
  3105. WorkflowTransition.where(:role_id => 1).delete_all
  3106. @request.session[:user_id] = 2
  3107. get(
  3108. :new,
  3109. :params => {
  3110. :project_id => 1,
  3111. :tracker_id => 1
  3112. }
  3113. )
  3114. assert_response :success
  3115. assert_select 'form#issue-form' do
  3116. assert_select 'input[name=?]', 'issue[is_private]', 0
  3117. assert_select 'select[name=?]', 'issue[project_id]'
  3118. assert_select 'select[name=?]', 'issue[tracker_id]'
  3119. assert_select 'input[name=?]', 'issue[subject]'
  3120. assert_select 'textarea[name=?]', 'issue[description]'
  3121. assert_select 'select[name=?]', 'issue[status_id]'
  3122. assert_select 'select[name=?]', 'issue[priority_id]'
  3123. assert_select 'select[name=?]', 'issue[assigned_to_id]'
  3124. assert_select 'select[name=?]', 'issue[category_id]'
  3125. assert_select 'select[name=?]', 'issue[fixed_version_id]'
  3126. assert_select 'input[name=?]', 'issue[parent_issue_id]', 0
  3127. assert_select 'input[name=?]', 'issue[start_date]'
  3128. assert_select 'input[name=?]', 'issue[due_date]'
  3129. assert_select 'select[name=?]', 'issue[done_ratio]'
  3130. assert_select 'input[name=?][value=?]', 'issue[custom_field_values][2]', 'Default string'
  3131. assert_select 'input[name=?]', 'issue[watcher_user_ids][]', 0
  3132. end
  3133. end
  3134. def test_new_without_project_id
  3135. @request.session[:user_id] = 2
  3136. get :new
  3137. assert_response :success
  3138. assert_select 'form#issue-form[action=?]', '/issues'
  3139. assert_select 'form#issue-form' do
  3140. assert_select 'select[name=?]', 'issue[project_id]'
  3141. end
  3142. end
  3143. def test_new_with_me_assigned_to_id
  3144. @request.session[:user_id] = 2
  3145. get(
  3146. :new,
  3147. :params => {
  3148. :issue => {:assigned_to_id => 'me'}
  3149. }
  3150. )
  3151. assert_response :success
  3152. assert_select 'select[name=?]', 'issue[assigned_to_id]' do
  3153. assert_select 'option[value="2"][selected=selected]'
  3154. end
  3155. end
  3156. def test_new_should_select_default_status
  3157. @request.session[:user_id] = 2
  3158. get(:new, :params => {:project_id => 1})
  3159. assert_response :success
  3160. assert_select 'select[name=?]', 'issue[status_id]' do
  3161. assert_select 'option[value="1"][selected=selected]'
  3162. end
  3163. assert_select 'input[name=was_default_status][value="1"]'
  3164. end
  3165. def test_new_should_propose_allowed_statuses
  3166. WorkflowTransition.delete_all
  3167. WorkflowTransition.create!(:tracker_id => 1, :role_id => 1,
  3168. :old_status_id => 0, :new_status_id => 1)
  3169. WorkflowTransition.create!(:tracker_id => 1, :role_id => 1,
  3170. :old_status_id => 0, :new_status_id => 3)
  3171. @request.session[:user_id] = 2
  3172. get(:new, :params => {:project_id => 1})
  3173. assert_response :success
  3174. assert_select 'select[name=?]', 'issue[status_id]' do
  3175. assert_select 'option[value="1"]'
  3176. assert_select 'option[value="3"]'
  3177. assert_select 'option', 2
  3178. assert_select 'option[value="1"][selected=selected]'
  3179. end
  3180. end
  3181. def test_new_should_propose_allowed_statuses_without_default_status_allowed
  3182. WorkflowTransition.delete_all
  3183. WorkflowTransition.create!(:tracker_id => 1, :role_id => 1,
  3184. :old_status_id => 0, :new_status_id => 2)
  3185. assert_equal 1, Tracker.find(1).default_status_id
  3186. @request.session[:user_id] = 2
  3187. get(:new, :params => {:project_id => 1})
  3188. assert_response :success
  3189. assert_select 'select[name=?]', 'issue[status_id]' do
  3190. assert_select 'option[value="2"]'
  3191. assert_select 'option', 1
  3192. assert_select 'option[value="2"][selected=selected]'
  3193. end
  3194. end
  3195. def test_new_should_propose_allowed_trackers
  3196. role = Role.find(1)
  3197. role.set_permission_trackers 'add_issues', [1, 3]
  3198. role.save!
  3199. @request.session[:user_id] = 2
  3200. get(:new, :params => {:project_id => 1})
  3201. assert_response :success
  3202. assert_select 'select[name=?]', 'issue[tracker_id]' do
  3203. assert_select 'option', 2
  3204. assert_select 'option[value="1"]'
  3205. assert_select 'option[value="3"]'
  3206. end
  3207. end
  3208. def test_new_should_default_to_first_tracker
  3209. @request.session[:user_id] = 2
  3210. get(:new, :params => {:project_id => 1})
  3211. assert_response :success
  3212. assert_select 'select[name=?]', 'issue[tracker_id]' do
  3213. assert_select 'option', 3
  3214. assert_select 'option[value="1"][selected=selected]'
  3215. end
  3216. end
  3217. def test_new_with_parent_issue_id_should_default_to_first_tracker_without_disabled_parent_field
  3218. tracker = Tracker.find(1)
  3219. tracker.core_fields -= ['parent_issue_id']
  3220. tracker.save!
  3221. @request.session[:user_id] = 2
  3222. get(
  3223. :new,
  3224. :params => {
  3225. :project_id => 1,
  3226. :issue => {
  3227. :parent_issue_id => 1
  3228. }
  3229. }
  3230. )
  3231. assert_response :success
  3232. assert_select 'select[name=?]', 'issue[tracker_id]' do
  3233. assert_select 'option', 2
  3234. assert_select 'option[value="2"][selected=selected]'
  3235. assert_select 'option[value="1"]', 0
  3236. end
  3237. end
  3238. def test_new_without_allowed_trackers_should_respond_with_403
  3239. role = Role.find(1)
  3240. role.set_permission_trackers 'add_issues', []
  3241. role.save!
  3242. @request.session[:user_id] = 2
  3243. get(:new, :params => {:project_id => 1})
  3244. assert_response 403
  3245. end
  3246. def test_new_without_projects_should_respond_with_403
  3247. Project.delete_all
  3248. @request.session[:user_id] = 2
  3249. get :new
  3250. assert_response 403
  3251. assert_select_error /no projects/
  3252. end
  3253. def test_new_without_enabled_trackers_on_projects_should_respond_with_403
  3254. Project.all.each {|p| p.trackers.clear}
  3255. @request.session[:user_id] = 2
  3256. get :new
  3257. assert_response 403
  3258. assert_select_error /no projects/
  3259. end
  3260. def test_new_should_preselect_default_version
  3261. version = Version.generate!(:project_id => 1)
  3262. Project.find(1).update_attribute :default_version_id, version.id
  3263. @request.session[:user_id] = 2
  3264. get(:new, :params => {:project_id => 1})
  3265. assert_response :success
  3266. assert_select 'select[name=?]', 'issue[fixed_version_id]' do
  3267. assert_select 'option[value=?][selected=selected]', version.id.to_s
  3268. end
  3269. end
  3270. def test_get_new_with_list_custom_field
  3271. @request.session[:user_id] = 2
  3272. get(
  3273. :new,
  3274. :params => {
  3275. :project_id => 1,
  3276. :tracker_id => 1
  3277. }
  3278. )
  3279. assert_response :success
  3280. assert_select 'select.list_cf[name=?]', 'issue[custom_field_values][1]' do
  3281. assert_select 'option', 4
  3282. assert_select 'option[value=MySQL]', :text => 'MySQL'
  3283. end
  3284. end
  3285. def test_get_new_with_multi_custom_field
  3286. field = IssueCustomField.find(1)
  3287. field.update_attribute :multiple, true
  3288. @request.session[:user_id] = 2
  3289. get(
  3290. :new,
  3291. :params => {
  3292. :project_id => 1,
  3293. :tracker_id => 1
  3294. }
  3295. )
  3296. assert_response :success
  3297. assert_select 'select[name=?][multiple=multiple]', 'issue[custom_field_values][1][]' do
  3298. assert_select 'option', 3
  3299. assert_select 'option[value=MySQL]', :text => 'MySQL'
  3300. end
  3301. assert_select 'input[name=?][type=hidden][value=?]', 'issue[custom_field_values][1][]', ''
  3302. end
  3303. def test_get_new_with_multi_user_custom_field
  3304. field =
  3305. IssueCustomField.
  3306. create!(
  3307. :name => 'Multi user', :field_format => 'user', :multiple => true,
  3308. :tracker_ids => [1], :is_for_all => true
  3309. )
  3310. @request.session[:user_id] = 2
  3311. get(
  3312. :new,
  3313. :params => {
  3314. :project_id => 1,
  3315. :tracker_id => 1
  3316. }
  3317. )
  3318. assert_response :success
  3319. assert_select 'select[name=?][multiple=multiple]', "issue[custom_field_values][#{field.id}][]" do
  3320. assert_select 'option', Project.find(1).users.count + 1 # users + 'me'
  3321. assert_select 'option[value="2"]', :text => 'John Smith'
  3322. end
  3323. assert_select 'input[name=?][type=hidden][value=?]', "issue[custom_field_values][#{field.id}][]", ''
  3324. end
  3325. def test_get_new_with_date_custom_field
  3326. field = IssueCustomField.create!(:name => 'Date', :field_format => 'date',
  3327. :tracker_ids => [1], :is_for_all => true)
  3328. @request.session[:user_id] = 2
  3329. get(
  3330. :new,
  3331. :params => {
  3332. :project_id => 1,
  3333. :tracker_id => 1
  3334. }
  3335. )
  3336. assert_response :success
  3337. assert_select 'input[name=?]', "issue[custom_field_values][#{field.id}]"
  3338. end
  3339. def test_get_new_with_text_custom_field
  3340. field = IssueCustomField.create!(:name => 'Text', :field_format => 'text',
  3341. :tracker_ids => [1], :is_for_all => true)
  3342. @request.session[:user_id] = 2
  3343. get(
  3344. :new,
  3345. :params => {
  3346. :project_id => 1,
  3347. :tracker_id => 1
  3348. }
  3349. )
  3350. assert_response :success
  3351. assert_select 'textarea[name=?]', "issue[custom_field_values][#{field.id}]"
  3352. end
  3353. def test_get_new_without_default_start_date_is_creation_date
  3354. with_settings :default_issue_start_date_to_creation_date => 0 do
  3355. @request.session[:user_id] = 2
  3356. get(
  3357. :new,
  3358. :params => {
  3359. :project_id => 1,
  3360. :tracker_id => 1
  3361. }
  3362. )
  3363. assert_response :success
  3364. assert_select 'input[name=?]', 'issue[start_date]'
  3365. assert_select 'input[name=?][value]', 'issue[start_date]', 0
  3366. end
  3367. end
  3368. def test_get_new_with_default_start_date_is_creation_date
  3369. with_settings :default_issue_start_date_to_creation_date => 1 do
  3370. @request.session[:user_id] = 2
  3371. get(
  3372. :new,
  3373. :params => {
  3374. :project_id => 1,
  3375. :tracker_id => 1
  3376. }
  3377. )
  3378. assert_response :success
  3379. assert_select 'input[name=?][value=?]', 'issue[start_date]',
  3380. Date.today.to_s
  3381. end
  3382. end
  3383. def test_get_new_form_should_allow_attachment_upload
  3384. @request.session[:user_id] = 2
  3385. get(
  3386. :new,
  3387. :params => {
  3388. :project_id => 1,
  3389. :tracker_id => 1
  3390. }
  3391. )
  3392. assert_response :success
  3393. assert_select 'form[id=issue-form][method=post][enctype="multipart/form-data"]' do
  3394. assert_select 'input[name=?][type=file]', 'attachments[dummy][file]'
  3395. end
  3396. end
  3397. def test_get_new_should_prefill_the_form_from_params
  3398. @request.session[:user_id] = 2
  3399. get(
  3400. :new,
  3401. :params => {
  3402. :project_id => 1,
  3403. :issue => {
  3404. :tracker_id => 3,
  3405. :description => 'Prefilled',
  3406. :custom_field_values => {
  3407. '2' => 'Custom field value'
  3408. }
  3409. }
  3410. }
  3411. )
  3412. assert_select 'select[name=?]', 'issue[tracker_id]' do
  3413. assert_select 'option[value="3"][selected=selected]'
  3414. end
  3415. assert_select 'textarea[name=?]', 'issue[description]', :text => /Prefilled/
  3416. assert_select 'input[name=?][value=?]', 'issue[custom_field_values][2]', 'Custom field value'
  3417. end
  3418. def test_get_new_should_mark_required_fields
  3419. cf1 = IssueCustomField.create!(:name => 'Foo', :field_format => 'string',
  3420. :is_for_all => true, :tracker_ids => [1, 2])
  3421. cf2 = IssueCustomField.create!(:name => 'Bar', :field_format => 'string',
  3422. :is_for_all => true, :tracker_ids => [1, 2])
  3423. WorkflowPermission.delete_all
  3424. WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1,
  3425. :field_name => 'due_date', :rule => 'required')
  3426. WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1,
  3427. :field_name => cf2.id.to_s, :rule => 'required')
  3428. @request.session[:user_id] = 2
  3429. get(:new, :params => {:project_id => 1})
  3430. assert_response :success
  3431. assert_select 'label[for=issue_start_date]' do
  3432. assert_select 'span[class=required]', 0
  3433. end
  3434. assert_select 'label[for=issue_due_date]' do
  3435. assert_select 'span[class=required]'
  3436. end
  3437. assert_select 'label[for=?]', "issue_custom_field_values_#{cf1.id}" do
  3438. assert_select 'span[class=required]', 0
  3439. end
  3440. assert_select 'label[for=?]', "issue_custom_field_values_#{cf2.id}" do
  3441. assert_select 'span[class=required]'
  3442. end
  3443. end
  3444. def test_get_new_should_not_display_readonly_fields
  3445. cf1 = IssueCustomField.create!(:name => 'Foo', :field_format => 'string',
  3446. :is_for_all => true, :tracker_ids => [1, 2])
  3447. cf2 = IssueCustomField.create!(:name => 'Bar', :field_format => 'string',
  3448. :is_for_all => true, :tracker_ids => [1, 2])
  3449. WorkflowPermission.delete_all
  3450. WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
  3451. :role_id => 1, :field_name => 'due_date', :rule => 'readonly')
  3452. WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
  3453. :role_id => 1, :field_name => cf2.id.to_s, :rule => 'readonly')
  3454. @request.session[:user_id] = 2
  3455. get(:new, :params => {:project_id => 1})
  3456. assert_response :success
  3457. assert_select 'input[name=?]', 'issue[start_date]'
  3458. assert_select 'input[name=?]', 'issue[due_date]', 0
  3459. assert_select 'input[name=?]', "issue[custom_field_values][#{cf1.id}]"
  3460. assert_select 'input[name=?]', "issue[custom_field_values][#{cf2.id}]", 0
  3461. end
  3462. def test_new_with_tracker_set_as_readonly_should_accept_status
  3463. WorkflowPermission.delete_all
  3464. [1, 2].each do |status_id|
  3465. WorkflowPermission.
  3466. create!(
  3467. :tracker_id => 1,
  3468. :old_status_id => status_id,
  3469. :role_id => 1,
  3470. :field_name => 'tracker_id',
  3471. :rule => 'readonly'
  3472. )
  3473. end
  3474. @request.session[:user_id] = 2
  3475. get(
  3476. :new,
  3477. :params => {
  3478. :project_id => 1,
  3479. :issue => {
  3480. :status_id => 2
  3481. }
  3482. }
  3483. )
  3484. assert_select 'select[name=?]', 'issue[tracker_id]', 0
  3485. assert_select 'select[name=?]', 'issue[status_id]' do
  3486. assert_select 'option[value=?][selected=selected]', '2'
  3487. end
  3488. end
  3489. def test_get_new_without_tracker_id
  3490. @request.session[:user_id] = 2
  3491. get(
  3492. :new,
  3493. :params => {
  3494. :project_id => 1
  3495. }
  3496. )
  3497. assert_response :success
  3498. assert_select 'select[name=?]', 'issue[tracker_id]' do
  3499. assert_select 'option[value=?][selected=selected]', Project.find(1).trackers.first.id.to_s
  3500. end
  3501. end
  3502. def test_get_new_with_no_default_status_should_display_an_error
  3503. @request.session[:user_id] = 2
  3504. IssueStatus.delete_all
  3505. get(
  3506. :new,
  3507. :params => {
  3508. :project_id => 1
  3509. }
  3510. )
  3511. assert_response 500
  3512. assert_select_error /No default issue/
  3513. end
  3514. def test_get_new_with_no_tracker_should_display_an_error
  3515. @request.session[:user_id] = 2
  3516. Tracker.delete_all
  3517. get(
  3518. :new,
  3519. :params => {
  3520. :project_id => 1
  3521. }
  3522. )
  3523. assert_response 500
  3524. assert_select_error /No tracker/
  3525. end
  3526. def test_new_with_invalid_project_id
  3527. @request.session[:user_id] = 1
  3528. get(
  3529. :new,
  3530. :params => {
  3531. :project_id => 'invalid'
  3532. }
  3533. )
  3534. assert_response 404
  3535. end
  3536. def test_new_with_parent_id_should_only_propose_valid_trackers
  3537. @request.session[:user_id] = 2
  3538. t = Tracker.find(3)
  3539. assert !t.disabled_core_fields.include?('parent_issue_id')
  3540. get(
  3541. :new,
  3542. :params => {
  3543. :project_id => 1, :issue => {:parent_issue_id => 1}
  3544. }
  3545. )
  3546. assert_response :success
  3547. assert_select 'option', text: /#{t.name}/, count: 1
  3548. t.core_fields = Tracker::CORE_FIELDS - ['parent_issue_id']
  3549. t.save!
  3550. assert t.disabled_core_fields.include?('parent_issue_id')
  3551. get(
  3552. :new,
  3553. :params => {
  3554. :project_id => 1, :issue => {:parent_issue_id => 1}
  3555. }
  3556. )
  3557. assert_response :success
  3558. assert_select 'option', text: /#{t.name}/, count: 0
  3559. end
  3560. def test_get_new_should_show_trackers_description
  3561. @request.session[:user_id] = 2
  3562. get :new, :params => {
  3563. :project_id => 1,
  3564. :issue => {
  3565. :tracker_id => 1
  3566. }
  3567. }
  3568. assert_response :success
  3569. assert_select 'form#issue-form' do
  3570. assert_select 'a[title=?]', 'View all trackers description', :text => 'View all trackers description'
  3571. assert_select 'select[name=?][title=?]', 'issue[tracker_id]', 'Description for Bug tracker'
  3572. end
  3573. assert_select 'div#trackers_description' do
  3574. assert_select 'h3', :text => 'Trackers description', :count => 1
  3575. # only Bug and Feature have descriptions
  3576. assert_select 'dt', 2
  3577. assert_select 'dt', :text => 'Bug', :count => 1
  3578. assert_select 'dd', :text => 'Description for Bug tracker', :count => 1
  3579. end
  3580. end
  3581. def test_get_new_should_not_show_trackers_description_for_trackers_without_description
  3582. Tracker.update_all(:description => '')
  3583. @request.session[:user_id] = 2
  3584. get :new, :params => {
  3585. :project_id => 1,
  3586. :issue => {
  3587. :tracker_id => 1
  3588. }
  3589. }
  3590. assert_response :success
  3591. assert_select 'form#issue-form' do
  3592. assert_select 'a[title=?]', 'View all trackers description', 0
  3593. assert_select 'select[name=?][title=?]', 'issue[tracker_id]', ''
  3594. end
  3595. assert_select 'div#trackers_description', 0
  3596. end
  3597. def test_get_new_should_show_issue_status_description
  3598. @request.session[:user_id] = 2
  3599. get :new, :params => {
  3600. :project_id => 1,
  3601. :issue => {
  3602. :status_id => 2
  3603. }
  3604. }
  3605. assert_response :success
  3606. assert_select 'form#issue-form' do
  3607. assert_select 'a[title=?]', 'View all issue statuses description', :text => 'View all issue statuses description'
  3608. assert_select 'select[name=?][title=?]', 'issue[status_id]', 'Description for Assigned issue status'
  3609. end
  3610. assert_select 'div#issue_statuses_description' do
  3611. assert_select 'h3', :text => 'Issue statuses description', :count => 1
  3612. assert_select 'dt', 2
  3613. assert_select 'dt', :text => 'New', :count => 1
  3614. assert_select 'dd', :text => 'Description for New issue status', :count => 1
  3615. end
  3616. end
  3617. def test_get_new_should_not_show_issue_status_description
  3618. IssueStatus.update_all(:description => '')
  3619. @request.session[:user_id] = 2
  3620. get :new, :params => {
  3621. :project_id => 1,
  3622. :issue => {
  3623. :status_id => 2
  3624. }
  3625. }
  3626. assert_response :success
  3627. assert_select 'form#issue-form' do
  3628. assert_select 'a[title=?]', 'View all issue statuses description', 0
  3629. assert_select 'select[name=?][title=?]', 'issue[status_id]', ''
  3630. end
  3631. assert_select 'div#issue_statuses_description', 0
  3632. end
  3633. def test_get_new_should_show_create_and_follow_button_when_issue_is_subtask_and_back_url_is_present
  3634. @request.session[:user_id] = 2
  3635. get :new, params: {
  3636. project_id: 1,
  3637. issue: {
  3638. parent_issue_id: 2
  3639. },
  3640. back_url: "/issues/2"
  3641. }
  3642. assert_response :success
  3643. assert_select 'form#issue-form' do
  3644. # Assert submit buttons
  3645. assert_select 'input[type=submit][name=?]', 'commit'
  3646. assert_select 'input[type=submit][name=?]', 'continue'
  3647. assert_select 'input[type=submit][name=?]', 'follow'
  3648. end
  3649. end
  3650. def test_update_form_for_new_issue
  3651. @request.session[:user_id] = 2
  3652. post(
  3653. :new,
  3654. :params => {
  3655. :project_id => 1,
  3656. :issue => {
  3657. :tracker_id => 2,
  3658. :subject => 'This is the test_new issue',
  3659. :description => 'This is the description',
  3660. :priority_id => 5
  3661. }
  3662. },
  3663. :xhr => true
  3664. )
  3665. assert_response :success
  3666. assert_equal 'text/javascript', response.media_type
  3667. assert_include 'This is the test_new issue', response.body
  3668. end
  3669. def test_update_form_for_new_issue_should_propose_transitions_based_on_initial_status
  3670. @request.session[:user_id] = 2
  3671. WorkflowTransition.delete_all
  3672. WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
  3673. :old_status_id => 0, :new_status_id => 2)
  3674. WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
  3675. :old_status_id => 0, :new_status_id => 5)
  3676. WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
  3677. :old_status_id => 5, :new_status_id => 4)
  3678. post(
  3679. :new,
  3680. :params => {
  3681. :project_id => 1,
  3682. :issue => {
  3683. :tracker_id => 1,
  3684. :status_id => 5,
  3685. :subject => 'This is an issue'
  3686. }
  3687. }
  3688. )
  3689. assert_select 'select[name=?]', 'issue[status_id]' do
  3690. assert_select 'option[value=?][selected=selected]', '5'
  3691. assert_select 'option[value=?]', '2'
  3692. assert_select 'option', :count => 2
  3693. end
  3694. end
  3695. def test_update_form_with_default_status_should_ignore_submitted_status_id_if_equals
  3696. @request.session[:user_id] = 2
  3697. tracker = Tracker.find(2)
  3698. tracker.update! :default_status_id => 2
  3699. tracker.generate_transitions! 2 => 1, :clear => true
  3700. post(
  3701. :new,
  3702. :params => {
  3703. :project_id => 1,
  3704. :issue => {
  3705. :tracker_id => 2,
  3706. :status_id => 1
  3707. },
  3708. :was_default_status => 1
  3709. }
  3710. )
  3711. assert_response :success
  3712. assert_select 'select[name=?]', 'issue[status_id]' do
  3713. assert_select 'option[value=?][selected=selected]', '2'
  3714. end
  3715. end
  3716. def test_update_form_for_new_issue_should_ignore_version_when_changing_project
  3717. version = Version.generate!(:project_id => 1)
  3718. Project.find(1).update_attribute :default_version_id, version.id
  3719. @request.session[:user_id] = 2
  3720. post(
  3721. :new,
  3722. :params => {
  3723. :issue => {
  3724. :project_id => 1,
  3725. :fixed_version_id => ''
  3726. },
  3727. :form_update_triggered_by => 'issue_project_id'
  3728. }
  3729. )
  3730. assert_response :success
  3731. assert_select 'select[name=?]', 'issue[project_id]' do
  3732. assert_select 'option[value=?][selected=selected]', '1'
  3733. end
  3734. assert_select 'select[name=?]', 'issue[fixed_version_id]' do
  3735. assert_select 'option[value=?][selected=selected]', version.id.to_s
  3736. end
  3737. end
  3738. def test_post_create
  3739. @request.session[:user_id] = 2
  3740. assert_difference 'Issue.count' do
  3741. assert_no_difference 'Journal.count' do
  3742. post(
  3743. :create,
  3744. :params => {
  3745. :project_id => 1,
  3746. :issue => {
  3747. :tracker_id => 3,
  3748. :status_id => 2,
  3749. :subject => 'This is the test_new issue',
  3750. :description => 'This is the description',
  3751. :priority_id => 5,
  3752. :start_date => '2010-11-07',
  3753. :estimated_hours => '',
  3754. :custom_field_values => {
  3755. '2' => 'Value for field 2'
  3756. }
  3757. }
  3758. }
  3759. )
  3760. end
  3761. end
  3762. assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
  3763. issue = Issue.find_by_subject('This is the test_new issue')
  3764. assert_not_nil issue
  3765. assert_equal 2, issue.author_id
  3766. assert_equal 3, issue.tracker_id
  3767. assert_equal 2, issue.status_id
  3768. assert_equal Date.parse('2010-11-07'), issue.start_date
  3769. assert_nil issue.estimated_hours
  3770. v = issue.custom_values.where(:custom_field_id => 2).first
  3771. assert_not_nil v
  3772. assert_equal 'Value for field 2', v.value
  3773. end
  3774. def test_post_new_with_group_assignment
  3775. group = Group.find(11)
  3776. project = Project.find(1)
  3777. project.members << Member.new(:principal => group, :roles => [Role.givable.first])
  3778. with_settings :issue_group_assignment => '1' do
  3779. @request.session[:user_id] = 2
  3780. assert_difference 'Issue.count' do
  3781. post(
  3782. :create,
  3783. :params => {
  3784. :project_id => project.id,
  3785. :issue => {
  3786. :tracker_id => 3,
  3787. :status_id => 1,
  3788. :subject => 'This is the test_new_with_group_assignment issue',
  3789. :assigned_to_id => group.id
  3790. }
  3791. }
  3792. )
  3793. end
  3794. end
  3795. assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
  3796. issue = Issue.find_by_subject('This is the test_new_with_group_assignment issue')
  3797. assert_not_nil issue
  3798. assert_equal group, issue.assigned_to
  3799. end
  3800. def test_post_create_without_start_date_and_default_start_date_is_not_creation_date
  3801. with_settings :default_issue_start_date_to_creation_date => 0 do
  3802. @request.session[:user_id] = 2
  3803. assert_difference 'Issue.count' do
  3804. post(
  3805. :create,
  3806. :params => {
  3807. :project_id => 1,
  3808. :issue => {
  3809. :tracker_id => 3,
  3810. :status_id => 2,
  3811. :subject => 'This is the test_new issue',
  3812. :description => 'This is the description',
  3813. :priority_id => 5,
  3814. :estimated_hours => '',
  3815. :custom_field_values => {
  3816. '2' => 'Value for field 2'
  3817. }
  3818. }
  3819. }
  3820. )
  3821. end
  3822. assert_redirected_to :controller => 'issues', :action => 'show',
  3823. :id => Issue.last.id
  3824. issue = Issue.find_by_subject('This is the test_new issue')
  3825. assert_not_nil issue
  3826. assert_nil issue.start_date
  3827. end
  3828. end
  3829. def test_post_create_without_start_date_and_default_start_date_is_creation_date
  3830. with_settings :default_issue_start_date_to_creation_date => 1 do
  3831. @request.session[:user_id] = 2
  3832. assert_difference 'Issue.count' do
  3833. post(
  3834. :create,
  3835. :params => {
  3836. :project_id => 1,
  3837. :issue => {
  3838. :tracker_id => 3,
  3839. :status_id => 2,
  3840. :subject => 'This is the test_new issue',
  3841. :description => 'This is the description',
  3842. :priority_id => 5,
  3843. :estimated_hours => '',
  3844. :custom_field_values => {
  3845. '2' => 'Value for field 2'
  3846. }
  3847. }
  3848. }
  3849. )
  3850. end
  3851. assert_redirected_to :controller => 'issues', :action => 'show',
  3852. :id => Issue.last.id
  3853. issue = Issue.find_by_subject('This is the test_new issue')
  3854. assert_not_nil issue
  3855. assert_equal Date.today, issue.start_date
  3856. end
  3857. end
  3858. def test_post_create_and_continue
  3859. @request.session[:user_id] = 2
  3860. assert_difference 'Issue.count' do
  3861. post(
  3862. :create,
  3863. :params => {
  3864. :project_id => 1,
  3865. :issue => {
  3866. :tracker_id => 3,
  3867. :subject => 'This is first issue',
  3868. :priority_id => 5
  3869. },
  3870. :continue => ''
  3871. }
  3872. )
  3873. end
  3874. issue = Issue.order('id DESC').first
  3875. assert_redirected_to :controller => 'issues',
  3876. :action => 'new', :project_id => 'ecookbook',
  3877. :issue => {:tracker_id => 3}
  3878. assert_not_nil flash[:notice], "flash was not set"
  3879. assert_select_in flash[:notice],
  3880. 'a[href=?][title=?]', "/issues/#{issue.id}",
  3881. "This is first issue", :text => "##{issue.id}"
  3882. end
  3883. def test_post_create_without_custom_fields_param
  3884. @request.session[:user_id] = 2
  3885. assert_difference 'Issue.count' do
  3886. post(
  3887. :create,
  3888. :params => {
  3889. :project_id => 1,
  3890. :issue => {
  3891. :tracker_id => 1,
  3892. :subject => 'This is the test_new issue',
  3893. :description => 'This is the description',
  3894. :priority_id => 5
  3895. }
  3896. }
  3897. )
  3898. end
  3899. assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
  3900. end
  3901. def test_post_create_with_multi_custom_field
  3902. field = IssueCustomField.find_by_name('Database')
  3903. field.update_attribute(:multiple, true)
  3904. @request.session[:user_id] = 2
  3905. assert_difference 'Issue.count' do
  3906. post(
  3907. :create,
  3908. :params => {
  3909. :project_id => 1,
  3910. :issue => {
  3911. :tracker_id => 1,
  3912. :subject => 'This is the test_new issue',
  3913. :description => 'This is the description',
  3914. :priority_id => 5,
  3915. :custom_field_values => {
  3916. '1' => ['', 'MySQL', 'Oracle']
  3917. }
  3918. }
  3919. }
  3920. )
  3921. end
  3922. assert_response 302
  3923. issue = Issue.order('id DESC').first
  3924. assert_equal ['MySQL', 'Oracle'], issue.custom_field_value(1).sort
  3925. end
  3926. def test_post_create_with_empty_multi_custom_field
  3927. field = IssueCustomField.find_by_name('Database')
  3928. field.update_attribute(:multiple, true)
  3929. @request.session[:user_id] = 2
  3930. assert_difference 'Issue.count' do
  3931. post(
  3932. :create,
  3933. :params => {
  3934. :project_id => 1,
  3935. :issue => {
  3936. :tracker_id => 1,
  3937. :subject => 'This is the test_new issue',
  3938. :description => 'This is the description',
  3939. :priority_id => 5,
  3940. :custom_field_values => {
  3941. '1' => ['']
  3942. }
  3943. }
  3944. }
  3945. )
  3946. end
  3947. assert_response 302
  3948. issue = Issue.order('id DESC').first
  3949. assert_equal [''], issue.custom_field_value(1).sort
  3950. end
  3951. def test_post_create_with_multi_user_custom_field
  3952. field =
  3953. IssueCustomField.create!(:name => 'Multi user', :field_format => 'user',
  3954. :multiple => true,
  3955. :tracker_ids => [1], :is_for_all => true)
  3956. @request.session[:user_id] = 2
  3957. assert_difference 'Issue.count' do
  3958. post(
  3959. :create,
  3960. :params => {
  3961. :project_id => 1,
  3962. :issue => {
  3963. :tracker_id => 1,
  3964. :subject => 'This is the test_new issue',
  3965. :description => 'This is the description',
  3966. :priority_id => 5,
  3967. :custom_field_values => {
  3968. field.id.to_s => ['', '2', '3']
  3969. }
  3970. }
  3971. }
  3972. )
  3973. end
  3974. assert_response 302
  3975. issue = Issue.order('id DESC').first
  3976. assert_equal ['2', '3'], issue.custom_field_value(field).sort
  3977. end
  3978. def test_post_create_with_required_custom_field_and_without_custom_fields_param
  3979. field = IssueCustomField.find_by_name('Database')
  3980. field.update_attribute(:is_required, true)
  3981. @request.session[:user_id] = 2
  3982. assert_no_difference 'Issue.count' do
  3983. post(
  3984. :create,
  3985. :params => {
  3986. :project_id => 1,
  3987. :issue => {
  3988. :tracker_id => 1,
  3989. :subject => 'This is the test_new issue',
  3990. :description => 'This is the description',
  3991. :priority_id => 5
  3992. }
  3993. }
  3994. )
  3995. end
  3996. assert_response :success
  3997. assert_select 'label[for=?][class=?]', "issue_custom_field_values_#{field.id}", 'error'
  3998. assert_select_error /Database cannot be blank/
  3999. end
  4000. def test_create_should_validate_required_fields
  4001. cf1 = IssueCustomField.create!(:name => 'Foo', :field_format => 'string',
  4002. :is_for_all => true, :tracker_ids => [1, 2])
  4003. cf2 = IssueCustomField.create!(:name => 'Bar', :field_format => 'string',
  4004. :is_for_all => true, :tracker_ids => [1, 2])
  4005. WorkflowPermission.delete_all
  4006. WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2, :role_id => 1,
  4007. :field_name => 'due_date', :rule => 'required')
  4008. WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2, :role_id => 1,
  4009. :field_name => cf2.id.to_s, :rule => 'required')
  4010. @request.session[:user_id] = 2
  4011. assert_no_difference 'Issue.count' do
  4012. post(
  4013. :create,
  4014. :params => {
  4015. :project_id => 1,
  4016. :issue => {
  4017. :tracker_id => 2,
  4018. :status_id => 1,
  4019. :subject => 'Test',
  4020. :start_date => '',
  4021. :due_date => '',
  4022. :custom_field_values => {
  4023. cf1.id.to_s => '', cf2.id.to_s => ''
  4024. }
  4025. }
  4026. }
  4027. )
  4028. assert_response :success
  4029. end
  4030. assert_select 'label[for=?][class=?]', 'issue_due_date', 'error'
  4031. assert_select 'label[for=?][class=?]', "issue_custom_field_values_#{cf2.id}", 'error'
  4032. assert_select_error /Due date cannot be blank/i
  4033. assert_select_error /Bar cannot be blank/i
  4034. end
  4035. def test_create_should_validate_required_list_fields
  4036. cf1 = IssueCustomField.create!(:name => 'Foo', :field_format => 'list',
  4037. :is_for_all => true, :tracker_ids => [1, 2],
  4038. :multiple => false, :possible_values => ['a', 'b'])
  4039. cf2 = IssueCustomField.create!(:name => 'Bar', :field_format => 'list',
  4040. :is_for_all => true, :tracker_ids => [1, 2],
  4041. :multiple => true, :possible_values => ['a', 'b'])
  4042. WorkflowPermission.delete_all
  4043. WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2, :role_id => 1,
  4044. :field_name => cf1.id.to_s, :rule => 'required')
  4045. WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2, :role_id => 1,
  4046. :field_name => cf2.id.to_s, :rule => 'required')
  4047. @request.session[:user_id] = 2
  4048. assert_no_difference 'Issue.count' do
  4049. post(
  4050. :create,
  4051. :params => {
  4052. :project_id => 1,
  4053. :issue => {
  4054. :tracker_id => 2,
  4055. :status_id => 1,
  4056. :subject => 'Test',
  4057. :start_date => '',
  4058. :due_date => '',
  4059. :custom_field_values => {
  4060. cf1.id.to_s => '', cf2.id.to_s => ['']
  4061. }
  4062. }
  4063. }
  4064. )
  4065. assert_response :success
  4066. end
  4067. assert_select_error /Foo cannot be blank/i
  4068. assert_select_error /Bar cannot be blank/i
  4069. end
  4070. def test_create_should_ignore_readonly_fields
  4071. cf1 = IssueCustomField.create!(:name => 'Foo', :field_format => 'string',
  4072. :is_for_all => true, :tracker_ids => [1, 2])
  4073. cf2 = IssueCustomField.create!(:name => 'Bar', :field_format => 'string',
  4074. :is_for_all => true, :tracker_ids => [1, 2])
  4075. WorkflowPermission.delete_all
  4076. WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2, :role_id => 1,
  4077. :field_name => 'due_date', :rule => 'readonly')
  4078. WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2, :role_id => 1,
  4079. :field_name => cf2.id.to_s, :rule => 'readonly')
  4080. @request.session[:user_id] = 2
  4081. assert_difference 'Issue.count' do
  4082. post(
  4083. :create,
  4084. :params => {
  4085. :project_id => 1,
  4086. :issue => {
  4087. :tracker_id => 2,
  4088. :status_id => 1,
  4089. :subject => 'Test',
  4090. :start_date => '2012-07-14',
  4091. :due_date => '2012-07-16',
  4092. :custom_field_values => {
  4093. cf1.id.to_s => 'value1', cf2.id.to_s => 'value2'
  4094. }
  4095. }
  4096. }
  4097. )
  4098. assert_response 302
  4099. end
  4100. issue = Issue.order('id DESC').first
  4101. assert_equal Date.parse('2012-07-14'), issue.start_date
  4102. assert_nil issue.due_date
  4103. assert_equal 'value1', issue.custom_field_value(cf1)
  4104. assert_nil issue.custom_field_value(cf2)
  4105. end
  4106. def test_create_should_ignore_unallowed_trackers
  4107. role = Role.find(1)
  4108. role.set_permission_trackers :add_issues, [3]
  4109. role.save!
  4110. @request.session[:user_id] = 2
  4111. issue = new_record(Issue) do
  4112. post(
  4113. :create,
  4114. :params => {
  4115. :project_id => 1,
  4116. :issue => {
  4117. :tracker_id => 1,
  4118. :status_id => 1,
  4119. :subject => 'Test'
  4120. }
  4121. }
  4122. )
  4123. assert_response 302
  4124. end
  4125. assert_equal 3, issue.tracker_id
  4126. end
  4127. def test_post_create_with_watchers
  4128. @request.session[:user_id] = 2
  4129. ActionMailer::Base.deliveries.clear
  4130. with_settings :notified_events => %w(issue_added) do
  4131. assert_difference 'Watcher.count', 3 do
  4132. post(
  4133. :create,
  4134. :params => {
  4135. :project_id => 1,
  4136. :issue => {
  4137. :tracker_id => 1,
  4138. :subject => 'This is a new issue with watchers',
  4139. :description => 'This is the description',
  4140. :priority_id => 5,
  4141. :watcher_user_ids => ['2', '3', '10']
  4142. }
  4143. }
  4144. )
  4145. end
  4146. end
  4147. issue = Issue.find_by_subject('This is a new issue with watchers')
  4148. assert_not_nil issue
  4149. assert_redirected_to :controller => 'issues', :action => 'show', :id => issue
  4150. # Watchers added
  4151. assert_equal [2, 3, 10], issue.watcher_user_ids.sort
  4152. assert issue.watched_by?(User.find(3))
  4153. assert issue.watched_by?(Group.find(10))
  4154. # Watchers notified
  4155. assert_equal 3, ActionMailer::Base.deliveries.size
  4156. mail = ActionMailer::Base.deliveries[1]
  4157. assert [mail.to].flatten.include?(User.find(3).mail)
  4158. mail = ActionMailer::Base.deliveries[2]
  4159. assert [mail.to].flatten.include?(User.find(8).mail)
  4160. end
  4161. def test_post_create_subissue
  4162. @request.session[:user_id] = 2
  4163. assert_difference 'Issue.count' do
  4164. post(
  4165. :create,
  4166. :params => {
  4167. :project_id => 1,
  4168. :issue => {
  4169. :tracker_id => 1,
  4170. :subject => 'This is a child issue',
  4171. :parent_issue_id => '2'
  4172. }
  4173. }
  4174. )
  4175. assert_response 302
  4176. end
  4177. issue = Issue.order('id DESC').first
  4178. assert_equal Issue.find(2), issue.parent
  4179. end
  4180. def test_post_create_subissue_with_sharp_parent_id
  4181. @request.session[:user_id] = 2
  4182. assert_difference 'Issue.count' do
  4183. post(
  4184. :create,
  4185. :params => {
  4186. :project_id => 1,
  4187. :issue => {
  4188. :tracker_id => 1,
  4189. :subject => 'This is a child issue',
  4190. :parent_issue_id => '#2'
  4191. }
  4192. }
  4193. )
  4194. assert_response 302
  4195. end
  4196. issue = Issue.order('id DESC').first
  4197. assert_equal Issue.find(2), issue.parent
  4198. end
  4199. def test_post_create_subissue_with_non_visible_parent_id_should_not_validate
  4200. @request.session[:user_id] = 2
  4201. assert_no_difference 'Issue.count' do
  4202. post(
  4203. :create,
  4204. :params => {
  4205. :project_id => 1,
  4206. :issue => {
  4207. :tracker_id => 1,
  4208. :subject => 'This is a child issue',
  4209. :parent_issue_id => '4'
  4210. }
  4211. }
  4212. )
  4213. assert_response :success
  4214. assert_select 'input[name=?][value=?]', 'issue[parent_issue_id]', '4'
  4215. assert_select_error /Parent task is invalid/i
  4216. end
  4217. end
  4218. def test_post_create_subissue_with_non_numeric_parent_id_should_not_validate
  4219. @request.session[:user_id] = 2
  4220. assert_no_difference 'Issue.count' do
  4221. post(
  4222. :create,
  4223. :params => {
  4224. :project_id => 1,
  4225. :issue => {
  4226. :tracker_id => 1,
  4227. :subject => 'This is a child issue',
  4228. :parent_issue_id => '01ABC'
  4229. }
  4230. }
  4231. )
  4232. assert_response :success
  4233. assert_select 'input[name=?][value=?]', 'issue[parent_issue_id]', '01ABC'
  4234. assert_select_error /Parent task is invalid/i
  4235. end
  4236. end
  4237. def test_post_create_private
  4238. @request.session[:user_id] = 2
  4239. assert_difference 'Issue.count' do
  4240. post(
  4241. :create,
  4242. :params => {
  4243. :project_id => 1,
  4244. :issue => {
  4245. :tracker_id => 1,
  4246. :subject => 'This is a private issue',
  4247. :is_private => '1'
  4248. }
  4249. }
  4250. )
  4251. end
  4252. issue = Issue.order('id DESC').first
  4253. assert issue.is_private?
  4254. end
  4255. def test_post_create_private_with_set_own_issues_private_permission
  4256. role = Role.find(1)
  4257. role.remove_permission! :set_issues_private
  4258. role.add_permission! :set_own_issues_private
  4259. @request.session[:user_id] = 2
  4260. assert_difference 'Issue.count' do
  4261. post(
  4262. :create,
  4263. :params => {
  4264. :project_id => 1,
  4265. :issue => {
  4266. :tracker_id => 1,
  4267. :subject => 'This is a private issue',
  4268. :is_private => '1'
  4269. }
  4270. }
  4271. )
  4272. end
  4273. issue = Issue.order('id DESC').first
  4274. assert issue.is_private?
  4275. end
  4276. def test_create_without_project_id
  4277. @request.session[:user_id] = 2
  4278. assert_difference 'Issue.count' do
  4279. post(
  4280. :create,
  4281. :params => {
  4282. :issue => {
  4283. :project_id => 3,
  4284. :tracker_id => 2,
  4285. :subject => 'Foo'
  4286. }
  4287. }
  4288. )
  4289. assert_response 302
  4290. end
  4291. issue = Issue.order('id DESC').first
  4292. assert_equal 3, issue.project_id
  4293. assert_equal 2, issue.tracker_id
  4294. end
  4295. def test_create_without_project_id_and_continue_should_redirect_without_project_id
  4296. @request.session[:user_id] = 2
  4297. assert_difference 'Issue.count' do
  4298. post(
  4299. :create,
  4300. :params => {
  4301. :issue => {
  4302. :project_id => 3,
  4303. :tracker_id => 2,
  4304. :subject => 'Foo'
  4305. },
  4306. :continue => '1'
  4307. }
  4308. )
  4309. assert_redirected_to '/issues/new?issue%5Bproject_id%5D=3&issue%5Btracker_id%5D=2'
  4310. end
  4311. end
  4312. def test_create_without_project_id_should_be_denied_without_permission
  4313. Role.non_member.remove_permission! :add_issues
  4314. Role.anonymous.remove_permission! :add_issues
  4315. @request.session[:user_id] = 2
  4316. assert_no_difference 'Issue.count' do
  4317. post(
  4318. :create,
  4319. :params => {
  4320. :issue => {
  4321. :project_id => 3,
  4322. :tracker_id => 2,
  4323. :subject => 'Foo'
  4324. }
  4325. }
  4326. )
  4327. assert_response 422
  4328. end
  4329. end
  4330. def test_create_without_project_id_with_failure_should_not_set_project
  4331. @request.session[:user_id] = 2
  4332. post(
  4333. :create,
  4334. :params => {
  4335. :issue => {
  4336. :project_id => 3,
  4337. :tracker_id => 2,
  4338. :subject => ''
  4339. }
  4340. }
  4341. )
  4342. assert_response :success
  4343. # no project menu
  4344. assert_select '#main-menu a.overview', 0
  4345. end
  4346. def test_post_create_should_send_a_notification
  4347. ActionMailer::Base.deliveries.clear
  4348. @request.session[:user_id] = 2
  4349. with_settings :notified_events => %w(issue_added) do
  4350. assert_difference 'Issue.count' do
  4351. post(
  4352. :create,
  4353. :params => {
  4354. :project_id => 1,
  4355. :issue => {
  4356. :tracker_id => 3,
  4357. :subject => 'This is the test_new issue',
  4358. :description => 'This is the description',
  4359. :priority_id => 5,
  4360. :estimated_hours => '',
  4361. :custom_field_values => {
  4362. '2' => 'Value for field 2'
  4363. }
  4364. }
  4365. }
  4366. )
  4367. end
  4368. assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
  4369. assert_equal 2, ActionMailer::Base.deliveries.size
  4370. end
  4371. end
  4372. def test_post_create_should_preserve_fields_values_on_validation_failure
  4373. @request.session[:user_id] = 2
  4374. post(
  4375. :create,
  4376. :params => {
  4377. :project_id => 1,
  4378. :issue => {
  4379. :tracker_id => 1,
  4380. :subject => '', # empty subject
  4381. :description => 'This is a description',
  4382. :priority_id => 6,
  4383. :custom_field_values => {'1' => 'Oracle', '2' => 'Value for field 2'}
  4384. }
  4385. }
  4386. )
  4387. assert_response :success
  4388. assert_select 'textarea[name=?]', 'issue[description]', :text => 'This is a description'
  4389. assert_select 'select[name=?]', 'issue[priority_id]' do
  4390. assert_select 'option[value="6"][selected=selected]', :text => 'High'
  4391. end
  4392. # Custom fields
  4393. assert_select 'select[name=?]', 'issue[custom_field_values][1]' do
  4394. assert_select 'option[value=Oracle][selected=selected]', :text => 'Oracle'
  4395. end
  4396. assert_select 'input[name=?][value=?]', 'issue[custom_field_values][2]', 'Value for field 2'
  4397. end
  4398. def test_post_create_with_failure_should_preserve_watchers
  4399. assert !User.find(8).member_of?(Project.find(1))
  4400. @request.session[:user_id] = 2
  4401. post(
  4402. :create,
  4403. :params => {
  4404. :project_id => 1,
  4405. :issue => {
  4406. :tracker_id => 1,
  4407. :watcher_user_ids => ['3', '8']
  4408. }
  4409. }
  4410. )
  4411. assert_response :success
  4412. assert_select 'input[name=?][value="2"]:not(checked)', 'issue[watcher_user_ids][]'
  4413. assert_select 'input[name=?][value="3"][checked=checked]', 'issue[watcher_user_ids][]'
  4414. assert_select 'input[name=?][value="8"][checked=checked]', 'issue[watcher_user_ids][]'
  4415. end
  4416. def test_post_create_with_failure_should_not_dereference_group_watchers
  4417. @request.session[:user_id] = 1
  4418. post(
  4419. :create,
  4420. :params => {
  4421. :project_id => 5,
  4422. :issue => {
  4423. :tracker_id => 1,
  4424. :watcher_user_ids => ['11']
  4425. }
  4426. }
  4427. )
  4428. assert_response :success
  4429. assert_select 'input[name=?][value="8"][checked=checked]', 'issue[watcher_user_ids][]', 0
  4430. assert_select 'input[name=?][value="11"][checked=checked]', 'issue[watcher_user_ids][]', 1
  4431. end
  4432. def test_post_create_should_ignore_non_safe_attributes
  4433. @request.session[:user_id] = 2
  4434. assert_nothing_raised do
  4435. post(
  4436. :create,
  4437. :params => {
  4438. :project_id => 1,
  4439. :issue => {
  4440. :tracker => "A param can not be a Tracker"
  4441. }
  4442. }
  4443. )
  4444. end
  4445. end
  4446. def test_post_create_with_attachment
  4447. set_tmp_attachments_directory
  4448. @request.session[:user_id] = 2
  4449. assert_difference 'Issue.count' do
  4450. assert_difference 'Attachment.count' do
  4451. assert_no_difference 'Journal.count' do
  4452. post(
  4453. :create,
  4454. :params => {
  4455. :project_id => 1,
  4456. :issue => {
  4457. :tracker_id => '1',
  4458. :subject => 'With attachment'
  4459. },
  4460. :attachments => {
  4461. '1' => {
  4462. 'file' => uploaded_test_file('testfile.txt', 'text/plain'),
  4463. 'description' => 'test file'
  4464. }
  4465. }
  4466. }
  4467. )
  4468. end
  4469. end
  4470. end
  4471. issue = Issue.order('id DESC').first
  4472. attachment = Attachment.order('id DESC').first
  4473. assert_equal issue, attachment.container
  4474. assert_equal 2, attachment.author_id
  4475. assert_equal 'testfile.txt', attachment.filename
  4476. assert_equal 'text/plain', attachment.content_type
  4477. assert_equal 'test file', attachment.description
  4478. assert_equal 59, attachment.filesize
  4479. assert File.exist?(attachment.diskfile)
  4480. assert_equal 59, File.size(attachment.diskfile)
  4481. end
  4482. def test_post_create_with_attachment_should_notify_with_attachments
  4483. ActionMailer::Base.deliveries.clear
  4484. set_tmp_attachments_directory
  4485. @request.session[:user_id] = 2
  4486. with_settings :notified_events => %w(issue_added) do
  4487. assert_difference 'Issue.count' do
  4488. post(
  4489. :create,
  4490. :params => {
  4491. :project_id => 1,
  4492. :issue => {
  4493. :tracker_id => '1',
  4494. :subject => 'With attachment'
  4495. },
  4496. :attachments => {
  4497. '1' => {
  4498. 'file' => uploaded_test_file('testfile.txt', 'text/plain'),
  4499. 'description' => 'test file'
  4500. }
  4501. }
  4502. }
  4503. )
  4504. end
  4505. end
  4506. assert_not_nil ActionMailer::Base.deliveries.last
  4507. assert_select_email do
  4508. assert_select 'a[href^=?]', 'http://localhost:3000/attachments/download', 'testfile.txt'
  4509. end
  4510. end
  4511. def test_post_create_with_failure_should_save_attachments
  4512. set_tmp_attachments_directory
  4513. @request.session[:user_id] = 2
  4514. assert_no_difference 'Issue.count' do
  4515. assert_difference 'Attachment.count' do
  4516. post(
  4517. :create,
  4518. :params => {
  4519. :project_id => 1,
  4520. :issue => {
  4521. :tracker_id => '1',
  4522. :subject => ''
  4523. },
  4524. :attachments => {
  4525. '1' => {
  4526. 'file' => uploaded_test_file('testfile.txt', 'text/plain'),
  4527. 'description' => 'test file'
  4528. }
  4529. }
  4530. }
  4531. )
  4532. assert_response :success
  4533. end
  4534. end
  4535. attachment = Attachment.order('id DESC').first
  4536. assert_equal 'testfile.txt', attachment.filename
  4537. assert File.exist?(attachment.diskfile)
  4538. assert_nil attachment.container
  4539. assert_select 'input[name=?][value=?]', 'attachments[p0][token]', attachment.token
  4540. assert_select 'input[name=?][value=?]', 'attachments[p0][filename]', 'testfile.txt'
  4541. end
  4542. def test_post_create_with_failure_should_keep_saved_attachments
  4543. set_tmp_attachments_directory
  4544. attachment =
  4545. Attachment.create!(:file => uploaded_test_file("testfile.txt", "text/plain"),
  4546. :author_id => 2)
  4547. @request.session[:user_id] = 2
  4548. assert_no_difference 'Issue.count' do
  4549. assert_no_difference 'Attachment.count' do
  4550. post(
  4551. :create,
  4552. :params => {
  4553. :project_id => 1,
  4554. :issue => {
  4555. :tracker_id => '1',
  4556. :subject => ''
  4557. },
  4558. :attachments => {
  4559. 'p0' => {
  4560. 'token' => attachment.token
  4561. }
  4562. }
  4563. }
  4564. )
  4565. assert_response :success
  4566. end
  4567. end
  4568. assert_select 'input[name=?][value=?]', 'attachments[p0][token]', attachment.token
  4569. assert_select 'input[name=?][value=?]', 'attachments[p0][filename]', 'testfile.txt'
  4570. end
  4571. def test_post_create_should_attach_saved_attachments
  4572. set_tmp_attachments_directory
  4573. attachment =
  4574. Attachment.create!(:file => uploaded_test_file("testfile.txt", "text/plain"),
  4575. :author_id => 2)
  4576. @request.session[:user_id] = 2
  4577. assert_difference 'Issue.count' do
  4578. assert_no_difference 'Attachment.count' do
  4579. post(
  4580. :create,
  4581. :params => {
  4582. :project_id => 1,
  4583. :issue => {
  4584. :tracker_id => '1',
  4585. :subject => 'Saved attachments'
  4586. },
  4587. :attachments => {
  4588. 'p0' => {
  4589. 'token' => attachment.token
  4590. }
  4591. }
  4592. }
  4593. )
  4594. assert_response 302
  4595. end
  4596. end
  4597. issue = Issue.order('id DESC').first
  4598. assert_equal 1, issue.attachments.count
  4599. attachment.reload
  4600. assert_equal issue, attachment.container
  4601. end
  4602. def setup_without_workflow_privilege
  4603. WorkflowTransition.where(["role_id = ?", Role.anonymous.id]).delete_all
  4604. Role.anonymous.add_permission! :add_issues, :add_issue_notes
  4605. end
  4606. private :setup_without_workflow_privilege
  4607. test "without workflow privilege #new should propose default status only" do
  4608. setup_without_workflow_privilege
  4609. get(:new, :params => {:project_id => 1})
  4610. assert_response :success
  4611. assert_select 'select[name=?]', 'issue[status_id]' do
  4612. assert_select 'option', 1
  4613. assert_select 'option[value=?][selected=selected]', '1'
  4614. end
  4615. end
  4616. test "without workflow privilege #create should accept default status" do
  4617. setup_without_workflow_privilege
  4618. assert_difference 'Issue.count' do
  4619. post(
  4620. :create,
  4621. :params => {
  4622. :project_id => 1,
  4623. :issue => {
  4624. :tracker_id => 1,
  4625. :subject => 'This is an issue',
  4626. :status_id => 1
  4627. }
  4628. }
  4629. )
  4630. end
  4631. issue = Issue.order('id').last
  4632. assert_not_nil issue.default_status
  4633. assert_equal issue.default_status, issue.status
  4634. end
  4635. test "without workflow privilege #create should ignore unauthorized status" do
  4636. setup_without_workflow_privilege
  4637. assert_difference 'Issue.count' do
  4638. post(
  4639. :create,
  4640. :params => {
  4641. :project_id => 1,
  4642. :issue => {
  4643. :tracker_id => 1,
  4644. :subject => 'This is an issue',
  4645. :status_id => 3
  4646. }
  4647. }
  4648. )
  4649. end
  4650. issue = Issue.order('id').last
  4651. assert_not_nil issue.default_status
  4652. assert_equal issue.default_status, issue.status
  4653. end
  4654. test "without workflow privilege #update should ignore status change" do
  4655. setup_without_workflow_privilege
  4656. assert_difference 'Journal.count' do
  4657. put(
  4658. :update,
  4659. :params => {
  4660. :id => 1,
  4661. :issue => {
  4662. :status_id => 3,
  4663. :notes => 'just trying'
  4664. }
  4665. }
  4666. )
  4667. end
  4668. assert_equal 1, Issue.find(1).status_id
  4669. end
  4670. test "without workflow privilege #update ignore attributes changes" do
  4671. setup_without_workflow_privilege
  4672. assert_difference 'Journal.count' do
  4673. put(
  4674. :update,
  4675. :params => {
  4676. :id => 1,
  4677. :issue => {
  4678. :subject => 'changed',
  4679. :assigned_to_id => 2,
  4680. :notes => 'just trying'
  4681. }
  4682. }
  4683. )
  4684. end
  4685. issue = Issue.find(1)
  4686. assert_equal "Cannot print recipes", issue.subject
  4687. assert_nil issue.assigned_to
  4688. end
  4689. def setup_with_workflow_privilege
  4690. WorkflowTransition.where(["role_id = ?", Role.anonymous.id]).delete_all
  4691. WorkflowTransition.create!(:role => Role.anonymous, :tracker_id => 1,
  4692. :old_status_id => 1, :new_status_id => 3)
  4693. WorkflowTransition.create!(:role => Role.anonymous, :tracker_id => 1,
  4694. :old_status_id => 1, :new_status_id => 4)
  4695. Role.anonymous.add_permission! :add_issues, :add_issue_notes
  4696. end
  4697. private :setup_with_workflow_privilege
  4698. def setup_with_workflow_privilege_and_edit_issues_permission
  4699. setup_with_workflow_privilege
  4700. Role.anonymous.add_permission! :add_issues, :edit_issues
  4701. end
  4702. private :setup_with_workflow_privilege_and_edit_issues_permission
  4703. test "with workflow privilege and :edit_issues permission should accept authorized status" do
  4704. setup_with_workflow_privilege_and_edit_issues_permission
  4705. assert_difference 'Journal.count' do
  4706. put(
  4707. :update,
  4708. :params => {
  4709. :id => 1,
  4710. :issue => {
  4711. :status_id => 3,
  4712. :notes => 'just trying'
  4713. }
  4714. }
  4715. )
  4716. end
  4717. assert_equal 3, Issue.find(1).status_id
  4718. end
  4719. test "with workflow privilege and :edit_issues permission should ignore unauthorized status" do
  4720. setup_with_workflow_privilege_and_edit_issues_permission
  4721. assert_difference 'Journal.count' do
  4722. put(
  4723. :update,
  4724. :params => {
  4725. :id => 1,
  4726. :issue => {
  4727. :status_id => 2,
  4728. :notes => 'just trying'
  4729. }
  4730. }
  4731. )
  4732. end
  4733. assert_equal 1, Issue.find(1).status_id
  4734. end
  4735. test "with workflow privilege and :edit_issues permission should accept authorized attributes changes" do
  4736. setup_with_workflow_privilege_and_edit_issues_permission
  4737. assert_difference 'Journal.count' do
  4738. put(
  4739. :update,
  4740. :params => {
  4741. :id => 1,
  4742. :issue => {
  4743. :subject => 'changed',
  4744. :assigned_to_id => 2,
  4745. :notes => 'just trying'
  4746. }
  4747. }
  4748. )
  4749. end
  4750. issue = Issue.find(1)
  4751. assert_equal "changed", issue.subject
  4752. assert_equal 2, issue.assigned_to_id
  4753. end
  4754. def test_new_as_copy
  4755. orig = Issue.find(1)
  4756. @request.session[:user_id] = 2
  4757. get(
  4758. :new,
  4759. :params => {
  4760. :project_id => 1,
  4761. :copy_from => orig.id
  4762. }
  4763. )
  4764. assert_response :success
  4765. assert_select 'form[id=issue-form][action="/projects/ecookbook/issues"]' do
  4766. assert_select 'select[name=?]', 'issue[project_id]' do
  4767. assert_select 'option[value="1"][selected=selected]', :text => 'eCookbook'
  4768. assert_select 'option[value="2"]:not([selected])', :text => 'OnlineStore'
  4769. end
  4770. assert_select 'input[name=?][value=?]', 'issue[subject]', orig.subject
  4771. assert_select 'input[name=copy_from][value="1"]'
  4772. end
  4773. end
  4774. def test_new_as_copy_without_add_issues_permission_should_not_propose_current_project_as_target
  4775. user = setup_user_with_copy_but_not_add_permission
  4776. @request.session[:user_id] = user.id
  4777. get(
  4778. :new,
  4779. :params => {
  4780. :project_id => 1,
  4781. :copy_from => 1
  4782. }
  4783. )
  4784. assert_response :success
  4785. assert_select 'select[name=?]', 'issue[project_id]' do
  4786. assert_select 'option[value="1"]', 0
  4787. assert_select 'option[value="2"]', :text => 'OnlineStore'
  4788. end
  4789. end
  4790. def test_new_as_copy_with_attachments_should_show_copy_attachments_checkbox
  4791. @request.session[:user_id] = 2
  4792. issue = Issue.find(3)
  4793. assert issue.attachments.count > 0
  4794. get(
  4795. :new,
  4796. :params => {
  4797. :project_id => 1,
  4798. :copy_from => 3
  4799. }
  4800. )
  4801. assert_select 'input[name=copy_attachments][type=checkbox][checked=checked][value="1"]'
  4802. end
  4803. def test_new_as_copy_without_attachments_should_not_show_copy_attachments_checkbox
  4804. @request.session[:user_id] = 2
  4805. issue = Issue.find(3)
  4806. issue.attachments.delete_all
  4807. get(
  4808. :new,
  4809. :params => {
  4810. :project_id => 1,
  4811. :copy_from => 3
  4812. }
  4813. )
  4814. assert_select 'input[name=copy_attachments]', 0
  4815. end
  4816. def test_new_as_copy_should_preserve_parent_id
  4817. @request.session[:user_id] = 2
  4818. issue = Issue.generate!(:parent_issue_id => 2)
  4819. get(
  4820. :new,
  4821. :params => {
  4822. :project_id => 1,
  4823. :copy_from => issue.id
  4824. }
  4825. )
  4826. assert_select 'input[name=?][value="2"]', 'issue[parent_issue_id]'
  4827. end
  4828. def test_new_as_copy_with_subtasks_should_show_copy_subtasks_checkbox
  4829. @request.session[:user_id] = 2
  4830. issue = Issue.generate_with_descendants!
  4831. get(
  4832. :new,
  4833. :params => {
  4834. :project_id => 1,
  4835. :copy_from => issue.id
  4836. }
  4837. )
  4838. assert_select 'input[type=checkbox][name=copy_subtasks][checked=checked][value="1"]'
  4839. end
  4840. def test_new_as_copy_should_preserve_watchers
  4841. @request.session[:user_id] = 2
  4842. issue = Issue.find(1)
  4843. user = User.generate!
  4844. Watcher.create!(:watchable => issue, :user => user)
  4845. Watcher.create!(:watchable => issue, :user => Group.find(10))
  4846. get(
  4847. :new,
  4848. :params => {
  4849. :project_id => 1,
  4850. :copy_from => 1
  4851. }
  4852. )
  4853. assert_select 'input[type=checkbox][name=?][checked=checked]', 'issue[watcher_user_ids][]', 2
  4854. assert_select 'input[type=checkbox][name=?][checked=checked][value=?]',
  4855. 'issue[watcher_user_ids][]', user.id.to_s
  4856. assert_select 'input[type=checkbox][name=?][checked=checked][value=?]',
  4857. 'issue[watcher_user_ids][]', '10'
  4858. assert_select 'input[type=hidden][name=?][value=?]', 'issue[watcher_user_ids][]', '', 1
  4859. end
  4860. def test_new_as_copy_should_not_propose_locked_watchers
  4861. @request.session[:user_id] = 2
  4862. issue = Issue.find(1)
  4863. user = User.generate!
  4864. user2 = User.generate!
  4865. Watcher.create!(:watchable => issue, :user => user)
  4866. Watcher.create!(:watchable => issue, :user => user2)
  4867. user2.status = User::STATUS_LOCKED
  4868. user2.save!
  4869. get(
  4870. :new,
  4871. :params => {
  4872. :project_id => 1,
  4873. :copy_from => 1
  4874. }
  4875. )
  4876. assert_select 'input[type=checkbox][name=?][checked=checked]', 'issue[watcher_user_ids][]', 1
  4877. assert_select 'input[type=checkbox][name=?][checked=checked][value=?]',
  4878. 'issue[watcher_user_ids][]', user.id.to_s
  4879. assert_select 'input[type=checkbox][name=?][checked=checked][value=?]',
  4880. 'issue[watcher_user_ids][]', user2.id.to_s, 0
  4881. assert_select 'input[type=hidden][name=?][value=?]', 'issue[watcher_user_ids][]', '', 1
  4882. end
  4883. def test_new_as_copy_with_invalid_issue_should_respond_with_404
  4884. @request.session[:user_id] = 2
  4885. get(
  4886. :new,
  4887. :params => {
  4888. :project_id => 1,
  4889. :copy_from => 99999
  4890. }
  4891. )
  4892. assert_response 404
  4893. end
  4894. def test_create_as_copy_on_different_project
  4895. @request.session[:user_id] = 2
  4896. assert_difference 'Issue.count' do
  4897. post(
  4898. :create,
  4899. :params => {
  4900. :project_id => 1,
  4901. :copy_from => 1,
  4902. :issue => {
  4903. :project_id => '2',
  4904. :tracker_id => '3',
  4905. :status_id => '1',
  4906. :subject => 'Copy'
  4907. }
  4908. }
  4909. )
  4910. end
  4911. issue = Issue.order('id DESC').first
  4912. assert_redirected_to "/issues/#{issue.id}"
  4913. assert_equal 2, issue.project_id
  4914. assert_equal 3, issue.tracker_id
  4915. assert_equal 'Copy', issue.subject
  4916. end
  4917. def test_create_as_copy_should_allow_status_to_be_set_to_default
  4918. copied = Issue.generate! :status_id => 2
  4919. assert_equal 2, copied.reload.status_id
  4920. @request.session[:user_id] = 2
  4921. assert_difference 'Issue.count' do
  4922. post(
  4923. :create,
  4924. :params => {
  4925. :project_id => 1,
  4926. :copy_from => copied.id,
  4927. :issue => {
  4928. :project_id => '1',
  4929. :tracker_id => '1',
  4930. :status_id => '1'
  4931. },
  4932. :was_default_status => '1'
  4933. }
  4934. )
  4935. end
  4936. issue = Issue.order('id DESC').first
  4937. assert_equal 1, issue.status_id
  4938. end
  4939. def test_create_as_copy_should_fail_without_add_issue_permission_on_original_tracker
  4940. role = Role.find(2)
  4941. role.set_permission_trackers :add_issues, [1, 3]
  4942. role.save!
  4943. Role.non_member.remove_permission! :add_issues
  4944. issue = Issue.generate!(:project_id => 1, :tracker_id => 2)
  4945. @request.session[:user_id] = 3
  4946. assert_no_difference 'Issue.count' do
  4947. post(
  4948. :create,
  4949. :params => {
  4950. :project_id => 1,
  4951. :copy_from => issue.id,
  4952. :issue => {
  4953. :project_id => '1'
  4954. }
  4955. }
  4956. )
  4957. end
  4958. assert_select_error 'Tracker is invalid'
  4959. end
  4960. def test_create_as_copy_should_copy_attachments
  4961. @request.session[:user_id] = 2
  4962. issue = Issue.find(3)
  4963. count = issue.attachments.count
  4964. assert count > 0
  4965. assert_difference 'Issue.count' do
  4966. assert_difference 'Attachment.count', count do
  4967. post(
  4968. :create,
  4969. :params => {
  4970. :project_id => 1,
  4971. :copy_from => 3,
  4972. :issue => {
  4973. :project_id => '1',
  4974. :tracker_id => '3',
  4975. :status_id => '1',
  4976. :subject => 'Copy with attachments'
  4977. },
  4978. :copy_attachments => '1'
  4979. }
  4980. )
  4981. end
  4982. end
  4983. copy = Issue.order('id DESC').first
  4984. assert_equal count, copy.attachments.count
  4985. assert_equal issue.attachments.map(&:filename).sort, copy.attachments.map(&:filename).sort
  4986. end
  4987. def test_create_as_copy_without_copy_attachments_option_should_not_copy_attachments
  4988. @request.session[:user_id] = 2
  4989. issue = Issue.find(3)
  4990. count = issue.attachments.count
  4991. assert count > 0
  4992. assert_difference 'Issue.count' do
  4993. assert_no_difference 'Attachment.count' do
  4994. post(
  4995. :create,
  4996. :params => {
  4997. :project_id => 1,
  4998. :copy_from => 3,
  4999. :issue => {
  5000. :project_id => '1',
  5001. :tracker_id => '3',
  5002. :status_id => '1',
  5003. :subject => 'Copy with attachments'
  5004. }
  5005. }
  5006. )
  5007. end
  5008. end
  5009. copy = Issue.order('id DESC').first
  5010. assert_equal 0, copy.attachments.count
  5011. end
  5012. def test_create_as_copy_with_attachments_should_also_add_new_files
  5013. set_tmp_attachments_directory
  5014. @request.session[:user_id] = 2
  5015. issue = Issue.find(3)
  5016. count = issue.attachments.count
  5017. assert count > 0
  5018. assert_difference 'Issue.count' do
  5019. assert_difference 'Attachment.count', count + 1 do
  5020. post(
  5021. :create,
  5022. :params => {
  5023. :project_id => 1,
  5024. :copy_from => 3,
  5025. :issue => {
  5026. :project_id => '1',
  5027. :tracker_id => '3',
  5028. :status_id => '1',
  5029. :subject => 'Copy with attachments'
  5030. },
  5031. :copy_attachments => '1',
  5032. :attachments => {
  5033. '1' => {
  5034. 'file' => uploaded_test_file('testfile.txt', 'text/plain'),
  5035. 'description' => 'test file'
  5036. }
  5037. }
  5038. }
  5039. )
  5040. end
  5041. end
  5042. copy = Issue.order('id DESC').first
  5043. assert_equal count + 1, copy.attachments.count
  5044. end
  5045. def test_create_as_copy_should_add_relation_with_copied_issue
  5046. @request.session[:user_id] = 2
  5047. assert_difference 'Issue.count' do
  5048. assert_difference 'IssueRelation.count' do
  5049. post(
  5050. :create,
  5051. :params => {
  5052. :project_id => 1,
  5053. :copy_from => 1,
  5054. :link_copy => '1',
  5055. :issue => {
  5056. :project_id => '1',
  5057. :tracker_id => '3',
  5058. :status_id => '1',
  5059. :subject => 'Copy'
  5060. }
  5061. }
  5062. )
  5063. end
  5064. end
  5065. copy = Issue.order('id DESC').first
  5066. assert_equal 1, copy.relations.size
  5067. end
  5068. def test_create_as_copy_should_allow_not_to_add_relation_with_copied_issue
  5069. @request.session[:user_id] = 2
  5070. assert_difference 'Issue.count' do
  5071. assert_no_difference 'IssueRelation.count' do
  5072. post(
  5073. :create,
  5074. :params => {
  5075. :project_id => 1,
  5076. :copy_from => 1,
  5077. :issue => {
  5078. :subject => 'Copy'
  5079. }
  5080. }
  5081. )
  5082. end
  5083. end
  5084. end
  5085. def test_create_as_copy_should_always_add_relation_with_copied_issue_by_setting
  5086. with_settings :link_copied_issue => 'yes' do
  5087. @request.session[:user_id] = 2
  5088. assert_difference 'Issue.count' do
  5089. assert_difference 'IssueRelation.count' do
  5090. post(
  5091. :create,
  5092. :params => {
  5093. :project_id => 1,
  5094. :copy_from => 1,
  5095. :issue => {
  5096. :subject => 'Copy'
  5097. }
  5098. }
  5099. )
  5100. end
  5101. end
  5102. end
  5103. end
  5104. def test_create_as_copy_should_never_add_relation_with_copied_issue_by_setting
  5105. with_settings :link_copied_issue => 'no' do
  5106. @request.session[:user_id] = 2
  5107. assert_difference 'Issue.count' do
  5108. assert_no_difference 'IssueRelation.count' do
  5109. post(
  5110. :create,
  5111. :params => {
  5112. :project_id => 1,
  5113. :copy_from => 1,
  5114. :link_copy => '1',
  5115. :issue => {
  5116. :subject => 'Copy'
  5117. }
  5118. }
  5119. )
  5120. end
  5121. end
  5122. end
  5123. end
  5124. def test_create_as_copy_should_copy_subtasks
  5125. @request.session[:user_id] = 2
  5126. issue = Issue.generate_with_descendants!
  5127. count = issue.descendants.count
  5128. assert_difference 'Issue.count', count + 1 do
  5129. post(
  5130. :create,
  5131. :params => {
  5132. :project_id => 1,
  5133. :copy_from => issue.id,
  5134. :issue => {
  5135. :project_id => '1',
  5136. :tracker_id => '3',
  5137. :status_id => '1',
  5138. :subject => 'Copy with subtasks'
  5139. },
  5140. :copy_subtasks => '1'
  5141. }
  5142. )
  5143. end
  5144. copy = Issue.where(:parent_id => nil).order('id DESC').first
  5145. assert_equal count, copy.descendants.count
  5146. assert_equal issue.descendants.map(&:subject).sort, copy.descendants.map(&:subject).sort
  5147. end
  5148. def test_create_as_copy_to_a_different_project_should_copy_subtask_custom_fields
  5149. issue = Issue.generate! {|i| i.custom_field_values = {'2' => 'Foo'}}
  5150. child = Issue.generate!(:parent_issue_id => issue.id) {|i| i.custom_field_values = {'2' => 'Bar'}}
  5151. @request.session[:user_id] = 1
  5152. assert_difference 'Issue.count', 2 do
  5153. post(
  5154. :create,
  5155. :params => {
  5156. :project_id => 'ecookbook',
  5157. :copy_from => issue.id,
  5158. :issue => {
  5159. :project_id => '2',
  5160. :tracker_id => 1,
  5161. :status_id => '1',
  5162. :subject => 'Copy with subtasks',
  5163. :custom_field_values => {
  5164. '2' => 'Foo'
  5165. }
  5166. },
  5167. :copy_subtasks => '1'
  5168. }
  5169. )
  5170. end
  5171. child_copy, issue_copy = Issue.order(:id => :desc).limit(2).to_a
  5172. assert_equal 2, issue_copy.project_id
  5173. assert_equal 'Foo', issue_copy.custom_field_value(2)
  5174. assert_equal 'Bar', child_copy.custom_field_value(2)
  5175. end
  5176. def test_create_as_copy_without_copy_subtasks_option_should_not_copy_subtasks
  5177. @request.session[:user_id] = 2
  5178. issue = Issue.generate_with_descendants!
  5179. assert_difference 'Issue.count', 1 do
  5180. post(
  5181. :create,
  5182. :params => {
  5183. :project_id => 1,
  5184. :copy_from => 3,
  5185. :issue => {
  5186. :project_id => '1',
  5187. :tracker_id => '3',
  5188. :status_id => '1',
  5189. :subject => 'Copy with subtasks'
  5190. }
  5191. }
  5192. )
  5193. end
  5194. copy = Issue.where(:parent_id => nil).order('id DESC').first
  5195. assert_equal 0, copy.descendants.count
  5196. end
  5197. def test_create_as_copy_with_failure
  5198. @request.session[:user_id] = 2
  5199. post(
  5200. :create,
  5201. :params => {
  5202. :project_id => 1,
  5203. :copy_from => 1,
  5204. :issue => {
  5205. :project_id => '2',
  5206. :tracker_id => '3',
  5207. :status_id => '1',
  5208. :subject => ''
  5209. }
  5210. }
  5211. )
  5212. assert_response :success
  5213. assert_select 'form#issue-form[action="/projects/ecookbook/issues"]' do
  5214. assert_select 'select[name=?]', 'issue[project_id]' do
  5215. assert_select 'option[value="1"]:not([selected])', :text => 'eCookbook'
  5216. assert_select 'option[value="2"][selected=selected]', :text => 'OnlineStore'
  5217. end
  5218. assert_select 'input[name=copy_from][value="1"]'
  5219. end
  5220. end
  5221. def test_create_as_copy_on_project_without_permission_should_ignore_target_project
  5222. @request.session[:user_id] = 2
  5223. assert !User.find(2).member_of?(Project.find(4))
  5224. assert_difference 'Issue.count' do
  5225. post(
  5226. :create,
  5227. :params => {
  5228. :project_id => 1,
  5229. :copy_from => 1,
  5230. :issue => {
  5231. :project_id => '4',
  5232. :tracker_id => '3',
  5233. :status_id => '1',
  5234. :subject => 'Copy'
  5235. }
  5236. }
  5237. )
  5238. end
  5239. issue = Issue.order('id DESC').first
  5240. assert_equal 1, issue.project_id
  5241. end
  5242. def test_create_as_copy_with_watcher_user_ids_should_copy_watchers
  5243. @request.session[:user_id] = 2
  5244. copied = Issue.generate!
  5245. copied.add_watcher User.find(2)
  5246. copied.add_watcher User.find(3)
  5247. assert_difference 'Issue.count' do
  5248. post(
  5249. :create,
  5250. :params => {
  5251. :project_id => 1,
  5252. :copy_from => copied.id,
  5253. :issue => {
  5254. :subject => 'Copy cleared watchers',
  5255. :watcher_user_ids => ['', '3', '10']
  5256. }
  5257. }
  5258. )
  5259. end
  5260. issue = Issue.order('id DESC').first
  5261. assert_equal [3, 10], issue.watcher_user_ids.sort
  5262. end
  5263. def test_create_as_copy_without_watcher_user_ids_should_not_copy_watchers
  5264. @request.session[:user_id] = 2
  5265. copied = Issue.generate!
  5266. copied.add_watcher User.find(2)
  5267. copied.add_watcher User.find(3)
  5268. assert_difference 'Issue.count' do
  5269. post(
  5270. :create,
  5271. :params => {
  5272. :project_id => 1,
  5273. :copy_from => copied.id,
  5274. :issue => {
  5275. :subject => 'Copy cleared watchers',
  5276. :watcher_user_ids => ['']
  5277. }
  5278. }
  5279. )
  5280. end
  5281. issue = Issue.order('id DESC').first
  5282. assert_equal [], issue.watcher_user_ids
  5283. end
  5284. def test_get_edit
  5285. @request.session[:user_id] = 2
  5286. get(:edit, :params => {:id => 1})
  5287. assert_response :success
  5288. assert_select 'select[name=?]', 'issue[project_id]'
  5289. # Be sure we don't display inactive IssuePriorities
  5290. assert_not IssuePriority.find(15).active?
  5291. assert_select 'select[name=?]', 'issue[priority_id]' do
  5292. assert_select 'option[value="15"]', 0
  5293. end
  5294. assert_select 'span.icon-warning', 0
  5295. end
  5296. def test_edit_should_hide_project_if_user_is_not_allowed_to_change_project
  5297. WorkflowPermission.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1,
  5298. :field_name => 'project_id', :rule => 'readonly')
  5299. @request.session[:user_id] = 2
  5300. get(:edit, :params => {:id => 1})
  5301. assert_response :success
  5302. assert_select 'select[name=?]', 'issue[project_id]', 0
  5303. end
  5304. def test_new_should_hide_project_if_user_is_not_allowed_to_change_project_in_hierarchy_projects
  5305. WorkflowPermission.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1,
  5306. :field_name => 'project_id', :rule => 'readonly')
  5307. @request.session[:user_id] = 2
  5308. get(:new, :params => { :tracker_id => 1, :project_id => 1 })
  5309. assert_response :success
  5310. assert_select 'select[name=?]', 'issue[project_id]', 0
  5311. end
  5312. def test_new_should_show_project_if_user_is_not_allowed_to_change_project_global_new_issue
  5313. WorkflowPermission.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1,
  5314. :field_name => 'project_id', :rule => 'readonly')
  5315. @request.session[:user_id] = 2
  5316. get(:new, :params => { :tracker_id => 1})
  5317. assert_response :success
  5318. assert_select 'select[name=?]', 'issue[project_id]'
  5319. end
  5320. def test_edit_should_not_hide_project_when_user_changes_the_project_even_if_project_is_readonly_on_target_project
  5321. WorkflowPermission.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1,
  5322. :field_name => 'project_id', :rule => 'readonly')
  5323. issue = Issue.generate!(:project_id => 2)
  5324. @request.session[:user_id] = 2
  5325. get(
  5326. :edit,
  5327. :params => {
  5328. :id => issue.id,
  5329. :issue => {
  5330. :project_id => 1
  5331. }
  5332. }
  5333. )
  5334. assert_response :success
  5335. assert_select 'select[name=?]', 'issue[project_id]'
  5336. end
  5337. def test_get_edit_should_display_the_time_entry_form_with_log_time_permission
  5338. @request.session[:user_id] = 2
  5339. Role.find_by_name('Manager').update_attribute :permissions, [:view_issues, :edit_issues, :log_time]
  5340. get(:edit, :params => {:id => 1})
  5341. assert_select 'input[name=?]', 'time_entry[hours]'
  5342. end
  5343. def test_get_edit_should_not_display_the_time_entry_form_without_log_time_permission
  5344. @request.session[:user_id] = 2
  5345. Role.find_by_name('Manager').remove_permission! :log_time
  5346. get(:edit, :params => {:id => 1})
  5347. assert_select 'input[name=?]', 'time_entry[hours]', 0
  5348. end
  5349. def test_get_edit_with_params
  5350. @request.session[:user_id] = 2
  5351. get(
  5352. :edit,
  5353. :params => {
  5354. :id => 1,
  5355. :issue => {
  5356. :status_id => 5,
  5357. :priority_id => 7
  5358. },
  5359. :time_entry => {
  5360. :hours => '2:30',
  5361. :comments => 'test_get_edit_with_params',
  5362. :activity_id => 10
  5363. }
  5364. }
  5365. )
  5366. assert_response :success
  5367. assert_select 'select[name=?]', 'issue[status_id]' do
  5368. assert_select 'option[value="5"][selected=selected]', :text => 'Closed'
  5369. end
  5370. assert_select 'select[name=?]', 'issue[priority_id]' do
  5371. assert_select 'option[value="7"][selected=selected]', :text => 'Urgent'
  5372. end
  5373. assert_select 'input[name=?][value="2:30"]', 'time_entry[hours]'
  5374. assert_select 'select[name=?]', 'time_entry[activity_id]' do
  5375. assert_select 'option[value="10"][selected=selected]', :text => 'Development'
  5376. end
  5377. assert_select 'input[name=?][value=test_get_edit_with_params]', 'time_entry[comments]'
  5378. end
  5379. def test_get_edit_with_multi_custom_field
  5380. field = CustomField.find(1)
  5381. field.update_attribute :multiple, true
  5382. issue = Issue.find(1)
  5383. issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
  5384. issue.save!
  5385. @request.session[:user_id] = 2
  5386. get(:edit, :params => {:id => 1})
  5387. assert_response :success
  5388. assert_select 'select[name=?][multiple=multiple]', 'issue[custom_field_values][1][]' do
  5389. assert_select 'option', 3
  5390. assert_select 'option[value=MySQL][selected=selected]'
  5391. assert_select 'option[value=Oracle][selected=selected]'
  5392. assert_select 'option[value=PostgreSQL]:not([selected])'
  5393. end
  5394. end
  5395. def test_get_edit_with_me_assigned_to_id
  5396. @request.session[:user_id] = 2
  5397. get(
  5398. :edit,
  5399. :params => {
  5400. :id => 1,
  5401. :issue => {:assigned_to_id => 'me'}
  5402. }
  5403. )
  5404. assert_response :success
  5405. assert_select 'select[name=?]', 'issue[assigned_to_id]' do
  5406. assert_select 'option[value="2"][selected=selected]'
  5407. end
  5408. end
  5409. def test_get_edit_for_issue_with_transition_warning_should_show_the_warning
  5410. @request.session[:user_id] = 2
  5411. get(
  5412. :edit,
  5413. :params => {
  5414. :id => 9,
  5415. }
  5416. )
  5417. assert_response :success
  5418. reason = l(:notice_issue_not_closable_by_blocking_issue)
  5419. assert_select 'span.icon-warning[title=?]', reason, :text => reason
  5420. end
  5421. def test_get_edit_should_display_visible_spent_time_custom_field
  5422. @request.session[:user_id] = 2
  5423. get(
  5424. :edit,
  5425. :params => {
  5426. :id => 13,
  5427. }
  5428. )
  5429. assert_response :success
  5430. assert_select '#issue-form select.cf_10', 1
  5431. end
  5432. def test_get_edit_should_not_display_spent_time_custom_field_not_visible
  5433. cf = TimeEntryCustomField.find(10)
  5434. cf.visible = false
  5435. cf.role_ids = [1]
  5436. cf.save!
  5437. @request.session[:user_id] = 2
  5438. get(
  5439. :edit,
  5440. :params => {
  5441. :id => 13,
  5442. }
  5443. )
  5444. assert_response :success
  5445. assert_select '#issue-form select.cf_10', 0
  5446. end
  5447. def test_update_form_for_existing_issue
  5448. @request.session[:user_id] = 2
  5449. patch(
  5450. :edit,
  5451. :params => {
  5452. :id => 1,
  5453. :issue => {
  5454. :tracker_id => 2,
  5455. :subject => 'This is the test_new issue',
  5456. :description => 'This is the description',
  5457. :priority_id => 5
  5458. }
  5459. },
  5460. :xhr => true
  5461. )
  5462. assert_response :success
  5463. assert_equal 'text/javascript', response.media_type
  5464. assert_include 'This is the test_new issue', response.body
  5465. end
  5466. def test_update_form_for_existing_issue_should_keep_issue_author
  5467. @request.session[:user_id] = 3
  5468. patch(
  5469. :edit,
  5470. :params => {
  5471. :id => 1,
  5472. :issue => {
  5473. :subject => 'Changed'
  5474. }
  5475. }
  5476. )
  5477. assert_response :success
  5478. assert_equal User.find(2), Issue.find(1).author
  5479. end
  5480. def test_update_form_for_existing_issue_should_propose_transitions_based_on_initial_status
  5481. @request.session[:user_id] = 2
  5482. WorkflowTransition.delete_all
  5483. WorkflowTransition.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 2, :new_status_id => 1)
  5484. WorkflowTransition.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 2, :new_status_id => 5)
  5485. WorkflowTransition.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 5, :new_status_id => 4)
  5486. patch(
  5487. :edit,
  5488. :params => {
  5489. :id => 2,
  5490. :issue => {
  5491. :tracker_id => 2,
  5492. :status_id => 5,
  5493. :subject => 'This is an issue'
  5494. }
  5495. }
  5496. )
  5497. assert_select 'select[name=?]', 'issue[status_id]' do
  5498. assert_select 'option[value="1"]'
  5499. assert_select 'option[value="2"]'
  5500. assert_select 'option[value="5"][selected=selected]'
  5501. assert_select 'option', 3
  5502. end
  5503. end
  5504. def test_update_form_for_existing_issue_with_project_change
  5505. @request.session[:user_id] = 2
  5506. patch(
  5507. :edit,
  5508. :params => {
  5509. :id => 1,
  5510. :issue => {
  5511. :project_id => 2,
  5512. :tracker_id => 2,
  5513. :subject => 'This is the test_new issue',
  5514. :description => 'This is the description',
  5515. :priority_id => 5
  5516. }
  5517. }
  5518. )
  5519. assert_response :success
  5520. assert_select 'select[name=?]', 'issue[project_id]' do
  5521. assert_select 'option[value="2"][selected=selected]'
  5522. end
  5523. assert_select 'select[name=?]', 'issue[tracker_id]' do
  5524. assert_select 'option[value="2"][selected=selected]'
  5525. end
  5526. assert_select 'input[name=?][value=?]', 'issue[subject]', 'This is the test_new issue'
  5527. end
  5528. def test_update_form_should_keep_category_with_same_when_changing_project
  5529. source = Project.generate!
  5530. target = Project.generate!
  5531. source_category = IssueCategory.create!(:name => 'Foo', :project => source)
  5532. target_category = IssueCategory.create!(:name => 'Foo', :project => target)
  5533. issue = Issue.generate!(:project => source, :category => source_category)
  5534. @request.session[:user_id] = 1
  5535. patch(
  5536. :edit,
  5537. :params => {
  5538. :id => issue.id,
  5539. :issue => {
  5540. :project_id => target.id,
  5541. :category_id => source_category.id
  5542. }
  5543. }
  5544. )
  5545. assert_response :success
  5546. assert_select 'select[name=?]', 'issue[category_id]' do
  5547. assert_select 'option[value=?][selected=selected]', target_category.id.to_s
  5548. end
  5549. end
  5550. def test_update_form_should_propose_default_status_for_existing_issue
  5551. @request.session[:user_id] = 2
  5552. WorkflowTransition.delete_all
  5553. WorkflowTransition.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 2, :new_status_id => 3)
  5554. patch(:edit, :params => {:id => 2})
  5555. assert_response :success
  5556. assert_select 'select[name=?]', 'issue[status_id]' do
  5557. assert_select 'option[value="2"]'
  5558. assert_select 'option[value="3"]'
  5559. assert_select 'option', 2
  5560. end
  5561. end
  5562. def test_put_update_without_custom_fields_param
  5563. @request.session[:user_id] = 2
  5564. issue = Issue.find(1)
  5565. assert_equal '125', issue.custom_value_for(2).value
  5566. assert_difference('Journal.count') do
  5567. assert_difference('JournalDetail.count') do
  5568. put(
  5569. :update,
  5570. :params => {
  5571. :id => 1,
  5572. :issue => {
  5573. :subject => 'New subject'
  5574. }
  5575. }
  5576. )
  5577. end
  5578. end
  5579. assert_redirected_to :action => 'show', :id => '1'
  5580. issue.reload
  5581. assert_equal 'New subject', issue.subject
  5582. # Make sure custom fields were not cleared
  5583. assert_equal '125', issue.custom_value_for(2).value
  5584. end
  5585. def test_put_update_with_project_change
  5586. @request.session[:user_id] = 2
  5587. ActionMailer::Base.deliveries.clear
  5588. with_settings :notified_events => %w(issue_updated) do
  5589. assert_difference('Journal.count') do
  5590. assert_difference('JournalDetail.count', 3) do
  5591. put(
  5592. :update,
  5593. :params => {
  5594. :id => 1,
  5595. :issue => {
  5596. :project_id => '2',
  5597. :tracker_id => '1', # no change
  5598. :priority_id => '6',
  5599. :category_id => '3'
  5600. }
  5601. }
  5602. )
  5603. end
  5604. end
  5605. end
  5606. assert_redirected_to :action => 'show', :id => '1'
  5607. issue = Issue.find(1)
  5608. assert_equal 2, issue.project_id
  5609. assert_equal 1, issue.tracker_id
  5610. assert_equal 6, issue.priority_id
  5611. assert_equal 3, issue.category_id
  5612. mail = ActionMailer::Base.deliveries.last
  5613. assert_not_nil mail
  5614. assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
  5615. assert_mail_body_match "Project changed from eCookbook to OnlineStore", mail
  5616. end
  5617. def test_put_update_trying_to_move_issue_to_project_without_tracker_should_not_error
  5618. target = Project.generate!(:tracker_ids => [])
  5619. assert target.trackers.empty?
  5620. issue = Issue.generate!
  5621. @request.session[:user_id] = 1
  5622. put(
  5623. :update,
  5624. :params => {
  5625. :id => issue.id,
  5626. :issue => {
  5627. :project_id => target.id
  5628. }
  5629. }
  5630. )
  5631. assert_response 302
  5632. end
  5633. def test_put_update_with_tracker_change
  5634. @request.session[:user_id] = 2
  5635. ActionMailer::Base.deliveries.clear
  5636. with_settings :notified_events => %w(issue_updated) do
  5637. assert_difference('Journal.count') do
  5638. assert_difference('JournalDetail.count', 3) do
  5639. put(
  5640. :update,
  5641. :params => {
  5642. :id => 1,
  5643. :issue => {
  5644. :project_id => '1',
  5645. :tracker_id => '2',
  5646. :priority_id => '6'
  5647. }
  5648. }
  5649. )
  5650. end
  5651. end
  5652. end
  5653. assert_redirected_to :action => 'show', :id => '1'
  5654. issue = Issue.find(1)
  5655. assert_equal 1, issue.project_id
  5656. assert_equal 2, issue.tracker_id
  5657. assert_equal 6, issue.priority_id
  5658. assert_equal 1, issue.category_id
  5659. mail = ActionMailer::Base.deliveries.last
  5660. assert_not_nil mail
  5661. assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
  5662. assert_mail_body_match "Tracker changed from Bug to Feature request", mail
  5663. end
  5664. def test_put_update_with_custom_field_change
  5665. @request.session[:user_id] = 2
  5666. issue = Issue.find(1)
  5667. assert_equal '125', issue.custom_value_for(2).value
  5668. with_settings :notified_events => %w(issue_updated) do
  5669. assert_difference('Journal.count') do
  5670. assert_difference('JournalDetail.count', 3) do
  5671. put(
  5672. :update,
  5673. :params => {
  5674. :id => 1,
  5675. :issue => {
  5676. :subject => 'Custom field change',
  5677. :priority_id => '6',
  5678. :category_id => '1', # no change
  5679. :custom_field_values => {'2' => 'New custom value'}
  5680. }
  5681. }
  5682. )
  5683. end
  5684. end
  5685. end
  5686. assert_redirected_to :action => 'show', :id => '1'
  5687. issue.reload
  5688. assert_equal 'New custom value', issue.custom_value_for(2).value
  5689. mail = ActionMailer::Base.deliveries.last
  5690. assert_not_nil mail
  5691. assert_mail_body_match "Searchable field changed from 125 to New custom value", mail
  5692. end
  5693. def test_put_update_with_multi_custom_field_change
  5694. field = CustomField.find(1)
  5695. field.update_attribute :multiple, true
  5696. issue = Issue.find(1)
  5697. issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
  5698. issue.save!
  5699. @request.session[:user_id] = 2
  5700. assert_difference('Journal.count') do
  5701. assert_difference('JournalDetail.count', 3) do
  5702. put(
  5703. :update,
  5704. :params => {
  5705. :id => 1,
  5706. :issue => {
  5707. :subject => 'Custom field change',
  5708. :custom_field_values => {
  5709. '1' => ['', 'Oracle', 'PostgreSQL']
  5710. }
  5711. }
  5712. }
  5713. )
  5714. end
  5715. end
  5716. assert_redirected_to :action => 'show', :id => '1'
  5717. assert_equal ['Oracle', 'PostgreSQL'], Issue.find(1).custom_field_value(1).sort
  5718. end
  5719. def test_put_update_with_status_and_assignee_change
  5720. issue = Issue.find(1)
  5721. assert_equal 1, issue.status_id
  5722. @request.session[:user_id] = 2
  5723. with_settings :notified_events => %w(issue_updated) do
  5724. assert_difference('TimeEntry.count', 0) do
  5725. put(
  5726. :update,
  5727. :params => {
  5728. :id => 1,
  5729. :issue => {
  5730. :status_id => 2,
  5731. :assigned_to_id => 3,
  5732. :notes => 'Assigned to dlopper'
  5733. },
  5734. :time_entry => {
  5735. :hours => '',
  5736. :comments => '',
  5737. :activity_id => TimeEntryActivity.first
  5738. }
  5739. }
  5740. )
  5741. end
  5742. end
  5743. assert_redirected_to :action => 'show', :id => '1'
  5744. issue.reload
  5745. assert_equal 2, issue.status_id
  5746. j = Journal.order('id DESC').first
  5747. assert_equal 'Assigned to dlopper', j.notes
  5748. assert_equal 2, j.details.size
  5749. mail = ActionMailer::Base.deliveries.last
  5750. assert_mail_body_match "Status changed from New to Assigned", mail
  5751. # subject should contain the new status
  5752. assert mail.subject.include?("(#{IssueStatus.find(2).name})")
  5753. end
  5754. def test_put_update_with_note_only
  5755. notes = 'Note added by IssuesControllerTest#test_update_with_note_only'
  5756. with_settings :notified_events => %w(issue_updated) do
  5757. # anonymous user
  5758. put(
  5759. :update,
  5760. :params => {
  5761. :id => 1,
  5762. :issue => {
  5763. :notes => notes
  5764. }
  5765. }
  5766. )
  5767. end
  5768. assert_redirected_to :action => 'show', :id => '1'
  5769. j = Journal.order('id DESC').first
  5770. assert_equal notes, j.notes
  5771. assert_equal 0, j.details.size
  5772. assert_equal User.anonymous, j.user
  5773. mail = ActionMailer::Base.deliveries.last
  5774. assert_mail_body_match notes, mail
  5775. end
  5776. def test_put_update_with_private_note_only
  5777. notes = 'Private note'
  5778. @request.session[:user_id] = 2
  5779. assert_difference 'Journal.count' do
  5780. put(
  5781. :update,
  5782. :params => {
  5783. :id => 1,
  5784. :issue => {
  5785. :notes => notes,
  5786. :private_notes => '1'
  5787. }
  5788. }
  5789. )
  5790. assert_redirected_to :action => 'show', :id => '1'
  5791. end
  5792. j = Journal.order('id DESC').first
  5793. assert_equal notes, j.notes
  5794. assert_equal true, j.private_notes
  5795. end
  5796. def test_put_update_with_private_note_and_changes
  5797. notes = 'Private note'
  5798. @request.session[:user_id] = 2
  5799. assert_difference 'Journal.count', 2 do
  5800. put(
  5801. :update,
  5802. :params => {
  5803. :id => 1,
  5804. :issue => {
  5805. :subject => 'New subject',
  5806. :notes => notes,
  5807. :private_notes => '1'
  5808. }
  5809. }
  5810. )
  5811. assert_redirected_to :action => 'show', :id => '1'
  5812. end
  5813. j = Journal.order('id DESC').first
  5814. assert_equal notes, j.notes
  5815. assert_equal true, j.private_notes
  5816. assert_equal 0, j.details.count
  5817. j = Journal.order('id DESC').offset(1).first
  5818. assert_nil j.notes
  5819. assert_equal false, j.private_notes
  5820. assert_equal 1, j.details.count
  5821. end
  5822. def test_put_update_with_note_and_spent_time
  5823. @request.session[:user_id] = 2
  5824. spent_hours_before = Issue.find(1).spent_hours
  5825. assert_difference('TimeEntry.count') do
  5826. put(
  5827. :update,
  5828. :params => {
  5829. :id => 1,
  5830. :issue => {
  5831. :notes => '2.5 hours added'
  5832. },
  5833. :time_entry => {
  5834. :hours => '2.5',
  5835. :comments => 'test_put_update_with_note_and_spent_time',
  5836. :activity_id => TimeEntryActivity.first.id
  5837. }
  5838. }
  5839. )
  5840. end
  5841. assert_redirected_to :action => 'show', :id => '1'
  5842. issue = Issue.find(1)
  5843. j = Journal.order('id DESC').first
  5844. assert_equal '2.5 hours added', j.notes
  5845. assert_equal 0, j.details.size
  5846. t = issue.time_entries.find_by_comments('test_put_update_with_note_and_spent_time')
  5847. assert_not_nil t
  5848. assert_equal 2.5, t.hours
  5849. assert_equal spent_hours_before + 2.5, issue.spent_hours
  5850. end
  5851. def test_put_update_should_check_add_issue_notes_permission
  5852. role = Role.find(1)
  5853. role.remove_permission! :add_issue_notes
  5854. @request.session[:user_id] = 2
  5855. assert_no_difference 'Journal.count' do
  5856. put(
  5857. :update,
  5858. :params => {
  5859. :id => 1,
  5860. :issue => {
  5861. :notes => 'New note'
  5862. }
  5863. }
  5864. )
  5865. end
  5866. end
  5867. def test_put_update_should_preserve_parent_issue_even_if_not_visible
  5868. parent = Issue.generate!(:project_id => 1, :is_private => true)
  5869. issue = Issue.generate!(:parent_issue_id => parent.id)
  5870. assert !parent.visible?(User.find(3))
  5871. @request.session[:user_id] = 3
  5872. get(:edit, :params => {:id => issue.id})
  5873. assert_select 'input[name=?][value=?]', 'issue[parent_issue_id]', parent.id.to_s
  5874. put(
  5875. :update,
  5876. :params => {
  5877. :id => issue.id,
  5878. :issue => {
  5879. :subject => 'New subject',
  5880. :parent_issue_id => parent.id.to_s
  5881. }
  5882. }
  5883. )
  5884. assert_response 302
  5885. assert_equal parent, issue.parent
  5886. end
  5887. def test_put_update_with_attachment_only
  5888. set_tmp_attachments_directory
  5889. # Delete all fixtured journals, a race condition can occur causing the wrong
  5890. # journal to get fetched in the next find.
  5891. Journal.delete_all
  5892. JournalDetail.delete_all
  5893. with_settings :notified_events => %w(issue_updated) do
  5894. # anonymous user
  5895. assert_difference 'Attachment.count' do
  5896. put(
  5897. :update,
  5898. :params => {
  5899. :id => 1,
  5900. :issue => {
  5901. :notes => ''
  5902. },
  5903. :attachments => {
  5904. '1' => {
  5905. 'file' => uploaded_test_file('testfile.txt', 'text/plain'),
  5906. 'description' => 'test file'
  5907. }
  5908. }
  5909. }
  5910. )
  5911. end
  5912. end
  5913. assert_redirected_to :action => 'show', :id => '1'
  5914. j = Issue.find(1).journals.reorder('id DESC').first
  5915. assert j.notes.blank?
  5916. assert_equal 1, j.details.size
  5917. assert_equal 'testfile.txt', j.details.first.value
  5918. assert_equal User.anonymous, j.user
  5919. attachment = Attachment.order('id DESC').first
  5920. assert_equal Issue.find(1), attachment.container
  5921. assert_equal User.anonymous, attachment.author
  5922. assert_equal 'testfile.txt', attachment.filename
  5923. assert_equal 'text/plain', attachment.content_type
  5924. assert_equal 'test file', attachment.description
  5925. assert_equal 59, attachment.filesize
  5926. assert File.exist?(attachment.diskfile)
  5927. assert_equal 59, File.size(attachment.diskfile)
  5928. mail = ActionMailer::Base.deliveries.last
  5929. assert_mail_body_match 'testfile.txt', mail
  5930. end
  5931. def test_put_update_with_failure_should_save_attachments
  5932. set_tmp_attachments_directory
  5933. @request.session[:user_id] = 2
  5934. assert_no_difference 'Journal.count' do
  5935. assert_difference 'Attachment.count' do
  5936. put(
  5937. :update,
  5938. :params => {
  5939. :id => 1,
  5940. :issue => {
  5941. :subject => ''
  5942. },
  5943. :attachments => {
  5944. '1' => {
  5945. 'file' => uploaded_test_file('testfile.txt', 'text/plain'),
  5946. 'description' => 'test file'
  5947. }
  5948. }
  5949. }
  5950. )
  5951. assert_response :success
  5952. end
  5953. end
  5954. attachment = Attachment.order('id DESC').first
  5955. assert_equal 'testfile.txt', attachment.filename
  5956. assert File.exist?(attachment.diskfile)
  5957. assert_nil attachment.container
  5958. assert_select 'input[name=?][value=?]', 'attachments[p0][token]', attachment.token
  5959. assert_select 'input[name=?][value=?]', 'attachments[p0][filename]', 'testfile.txt'
  5960. end
  5961. def test_put_update_with_failure_should_keep_saved_attachments
  5962. set_tmp_attachments_directory
  5963. attachment = Attachment.create!(:file => uploaded_test_file("testfile.txt", "text/plain"), :author_id => 2)
  5964. @request.session[:user_id] = 2
  5965. assert_no_difference 'Journal.count' do
  5966. assert_no_difference 'Attachment.count' do
  5967. put(
  5968. :update,
  5969. :params => {
  5970. :id => 1,
  5971. :issue => {
  5972. :subject => ''
  5973. },
  5974. :attachments => {
  5975. 'p0' => {
  5976. 'token' => attachment.token
  5977. }
  5978. }
  5979. }
  5980. )
  5981. assert_response :success
  5982. end
  5983. end
  5984. assert_select 'input[name=?][value=?]', 'attachments[p0][token]', attachment.token
  5985. assert_select 'input[name=?][value=?]', 'attachments[p0][filename]', 'testfile.txt'
  5986. end
  5987. def test_put_update_should_attach_saved_attachments
  5988. set_tmp_attachments_directory
  5989. attachment = Attachment.create!(:file => uploaded_test_file("testfile.txt", "text/plain"), :author_id => 2)
  5990. @request.session[:user_id] = 2
  5991. assert_difference 'Journal.count' do
  5992. assert_difference 'JournalDetail.count' do
  5993. assert_no_difference 'Attachment.count' do
  5994. put(
  5995. :update,
  5996. :params => {
  5997. :id => 1,
  5998. :issue => {
  5999. :notes => 'Attachment added'
  6000. },
  6001. :attachments => {
  6002. 'p0' => {
  6003. 'token' => attachment.token
  6004. }
  6005. }
  6006. }
  6007. )
  6008. assert_redirected_to '/issues/1'
  6009. end
  6010. end
  6011. end
  6012. attachment.reload
  6013. assert_equal Issue.find(1), attachment.container
  6014. journal = Journal.order('id DESC').first
  6015. assert_equal 1, journal.details.size
  6016. assert_equal 'testfile.txt', journal.details.first.value
  6017. end
  6018. def test_put_update_with_attachment_that_fails_to_save
  6019. set_tmp_attachments_directory
  6020. # anonymous user
  6021. with_settings :attachment_max_size => 0 do
  6022. put(
  6023. :update,
  6024. :params => {
  6025. :id => 1,
  6026. :issue => {
  6027. :notes => ''
  6028. },
  6029. :attachments => {
  6030. '1' => {
  6031. 'file' => uploaded_test_file('testfile.txt', 'text/plain')
  6032. }
  6033. }
  6034. }
  6035. )
  6036. assert_redirected_to :action => 'show', :id => '1'
  6037. assert_equal '1 file(s) could not be saved.', flash[:warning]
  6038. end
  6039. end
  6040. def test_put_update_with_attachment_deletion_should_create_a_single_journal
  6041. set_tmp_attachments_directory
  6042. ActionMailer::Base.deliveries.clear
  6043. @request.session[:user_id] = 2
  6044. journal = new_record(Journal) do
  6045. assert_difference 'Attachment.count', -2 do
  6046. put(
  6047. :update,
  6048. :params => {
  6049. :id => 3,
  6050. :issue => {
  6051. :notes => 'Removing attachments',
  6052. :deleted_attachment_ids => ['1', '5']
  6053. }
  6054. }
  6055. )
  6056. end
  6057. end
  6058. assert_equal 'Removing attachments', journal.notes
  6059. assert_equal 2, journal.details.count
  6060. assert_select_email do
  6061. assert_select 'ul.journal.details li', 2
  6062. assert_select 'del', :text => 'error281.txt'
  6063. assert_select 'del', :text => 'changeset_iso8859-1.diff'
  6064. end
  6065. end
  6066. def test_put_update_with_attachment_deletion_and_failure_should_preserve_selected_attachments
  6067. set_tmp_attachments_directory
  6068. @request.session[:user_id] = 2
  6069. assert_no_difference 'Journal.count' do
  6070. assert_no_difference 'Attachment.count' do
  6071. put(
  6072. :update,
  6073. :params => {
  6074. :id => 3,
  6075. :issue => {
  6076. :subject => '',
  6077. :notes => 'Removing attachments',
  6078. :deleted_attachment_ids => ['1', '5']
  6079. }
  6080. }
  6081. )
  6082. end
  6083. end
  6084. assert_select 'input[name=?][value="1"][checked=checked]', 'issue[deleted_attachment_ids][]'
  6085. assert_select 'input[name=?][value="5"][checked=checked]', 'issue[deleted_attachment_ids][]'
  6086. assert_select 'input[name=?][value="6"]:not([checked])', 'issue[deleted_attachment_ids][]'
  6087. end
  6088. def test_put_update_with_no_change
  6089. issue = Issue.find(1)
  6090. issue.journals.clear
  6091. ActionMailer::Base.deliveries.clear
  6092. put(
  6093. :update,
  6094. :params => {
  6095. :id => 1,
  6096. :issue => {
  6097. :notes => ''
  6098. }
  6099. }
  6100. )
  6101. assert_redirected_to :action => 'show', :id => '1'
  6102. issue.reload
  6103. assert issue.journals.empty?
  6104. # No email should be sent
  6105. assert ActionMailer::Base.deliveries.empty?
  6106. end
  6107. def test_put_update_should_send_a_notification
  6108. @request.session[:user_id] = 2
  6109. ActionMailer::Base.deliveries.clear
  6110. issue = Issue.find(1)
  6111. old_subject = issue.subject
  6112. new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
  6113. with_settings :notified_events => %w(issue_updated) do
  6114. put(
  6115. :update,
  6116. :params => {
  6117. :id => 1,
  6118. :issue => {
  6119. :subject => new_subject,
  6120. :priority_id => '6',
  6121. :category_id => '1' # no change
  6122. }
  6123. }
  6124. )
  6125. assert_equal 2, ActionMailer::Base.deliveries.size
  6126. end
  6127. end
  6128. def test_put_update_with_invalid_spent_time_hours_only
  6129. @request.session[:user_id] = 2
  6130. notes = 'Note added by IssuesControllerTest#test_post_edit_with_invalid_spent_time'
  6131. assert_no_difference('Journal.count') do
  6132. put(
  6133. :update,
  6134. :params => {
  6135. :id => 1,
  6136. :issue => {
  6137. :notes => notes
  6138. },
  6139. :time_entry => {
  6140. "comments"=>"", "activity_id"=>"", "hours"=>"2z"
  6141. }
  6142. }
  6143. )
  6144. end
  6145. assert_response :success
  6146. assert_select_error /Activity cannot be blank/
  6147. assert_select 'textarea[name=?]', 'issue[notes]', :text => notes
  6148. assert_select 'input[name=?][value=?]', 'time_entry[hours]', '2z'
  6149. end
  6150. def test_put_update_with_invalid_spent_time_comments_only
  6151. @request.session[:user_id] = 2
  6152. notes = 'Note added by IssuesControllerTest#test_post_edit_with_invalid_spent_time'
  6153. assert_no_difference('Journal.count') do
  6154. put(
  6155. :update,
  6156. :params => {
  6157. :id => 1,
  6158. :issue => {
  6159. :notes => notes
  6160. },
  6161. :time_entry => {
  6162. "comments"=>"this is my comment", "activity_id"=>"", "hours"=>""
  6163. }
  6164. }
  6165. )
  6166. end
  6167. assert_response :success
  6168. assert_select_error /Activity cannot be blank/
  6169. assert_select_error /Hours cannot be blank/
  6170. assert_select 'textarea[name=?]', 'issue[notes]', :text => notes
  6171. assert_select 'input[name=?][value=?]', 'time_entry[comments]', 'this is my comment'
  6172. end
  6173. def test_put_with_spent_time_when_assigned_to_of_private_issue_is_update_at_the_same_time
  6174. @request.session[:user_id] = 3
  6175. Role.find(2).update! :issues_visibility => 'own'
  6176. private_issue = Issue.find(3)
  6177. assert_difference('TimeEntry.count', 1) do
  6178. put(
  6179. :update,
  6180. params: {
  6181. id: private_issue.id,
  6182. issue: { assigned_to_id: nil },
  6183. time_entry: {
  6184. comments: "add spent time", activity_id: TimeEntryActivity.first.id, hours: 1
  6185. }
  6186. }
  6187. )
  6188. end
  6189. assert_select '#errorExplanation', {text: /Log time is invalid/, count: 0}
  6190. assert_select '#errorExplanation', {text: /Issue is invalid/, count: 0}
  6191. assert_redirected_to action: 'show', id: private_issue.id
  6192. assert_not private_issue.reload.visible?
  6193. end
  6194. def test_put_update_should_allow_fixed_version_to_be_set_to_a_subproject
  6195. issue = Issue.find(2)
  6196. @request.session[:user_id] = 2
  6197. put(
  6198. :update,
  6199. :params => {
  6200. :id => issue.id,
  6201. :issue => {
  6202. :fixed_version_id => 4
  6203. }
  6204. }
  6205. )
  6206. assert_response :redirect
  6207. issue.reload
  6208. assert_equal 4, issue.fixed_version_id
  6209. assert_not_equal issue.project_id, issue.fixed_version.project_id
  6210. end
  6211. def test_put_update_should_redirect_back_using_the_back_url_parameter
  6212. issue = Issue.find(2)
  6213. @request.session[:user_id] = 2
  6214. put(
  6215. :update,
  6216. :params => {
  6217. :id => issue.id,
  6218. :issue => {
  6219. :fixed_version_id => 4
  6220. },
  6221. :back_url => '/issues'
  6222. }
  6223. )
  6224. assert_response :redirect
  6225. assert_redirected_to '/issues'
  6226. end
  6227. def test_put_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
  6228. issue = Issue.find(2)
  6229. @request.session[:user_id] = 2
  6230. put(
  6231. :update,
  6232. :params => {
  6233. :id => issue.id,
  6234. :issue => {
  6235. :fixed_version_id => 4
  6236. },
  6237. :back_url => 'http://google.com'
  6238. }
  6239. )
  6240. assert_response :redirect
  6241. assert_redirected_to :controller => 'issues', :action => 'show', :id => issue.id
  6242. end
  6243. def test_put_update_should_redirect_with_previous_and_next_issue_ids_params
  6244. @request.session[:user_id] = 2
  6245. put(
  6246. :update,
  6247. :params => {
  6248. :id => 11,
  6249. :issue => {
  6250. :status_id => 6,
  6251. :notes => 'Notes'
  6252. },
  6253. :prev_issue_id => 8,
  6254. :next_issue_id => 12,
  6255. :issue_position => 2,
  6256. :issue_count => 3
  6257. }
  6258. )
  6259. assert_redirected_to '/issues/11?issue_count=3&issue_position=2&next_issue_id=12&prev_issue_id=8'
  6260. end
  6261. def test_update_with_permission_on_tracker_should_be_allowed
  6262. role = Role.find(1)
  6263. role.set_permission_trackers :edit_issues, [1]
  6264. role.save!
  6265. issue = Issue.generate!(:project_id => 1, :tracker_id => 1, :subject => 'Original subject')
  6266. @request.session[:user_id] = 2
  6267. put(
  6268. :update,
  6269. :params => {
  6270. :id => issue.id,
  6271. :issue => {
  6272. :subject => 'Changed subject'
  6273. }
  6274. }
  6275. )
  6276. assert_response 302
  6277. assert_equal 'Changed subject', issue.reload.subject
  6278. end
  6279. def test_update_without_permission_on_tracker_should_be_denied
  6280. role = Role.find(1)
  6281. role.set_permission_trackers :edit_issues, [1]
  6282. role.save!
  6283. issue = Issue.generate!(:project_id => 1, :tracker_id => 2, :subject => 'Original subject')
  6284. @request.session[:user_id] = 2
  6285. put(
  6286. :update,
  6287. :params => {
  6288. :id => issue.id,
  6289. :issue => {
  6290. :subject => 'Changed subject'
  6291. }
  6292. }
  6293. )
  6294. assert_response 302
  6295. assert_equal 'Original subject', issue.reload.subject
  6296. end
  6297. def test_update_with_me_assigned_to_id
  6298. @request.session[:user_id] = 2
  6299. issue = Issue.find(1)
  6300. assert_not_equal 2, issue.assigned_to_id
  6301. put(
  6302. :update,
  6303. :params => {
  6304. :id => issue.id,
  6305. :issue => {
  6306. :assigned_to_id => 'me'
  6307. }
  6308. }
  6309. )
  6310. assert_response 302
  6311. assert_equal 2, issue.reload.assigned_to_id
  6312. end
  6313. def test_update_with_value_of_none_should_set_the_values_to_blank
  6314. @request.session[:user_id] = 2
  6315. issue = Issue.find(1)
  6316. issue.custom_field_values = {1 => 'MySQL'}
  6317. issue.assigned_to_id = 2
  6318. issue.save!
  6319. put(
  6320. :update,
  6321. params: {
  6322. id: issue.id,
  6323. issue: {
  6324. assigned_to_id: 'none',
  6325. category_id: 'none',
  6326. fixed_version_id: 'none',
  6327. custom_field_values: { 1 => '__none__' }
  6328. }
  6329. }
  6330. )
  6331. issue.reload
  6332. assert_nil issue.assigned_to
  6333. assert_nil issue.category
  6334. assert_nil issue.fixed_version
  6335. assert_equal '', issue.custom_field_value(1)
  6336. end
  6337. def test_get_bulk_edit
  6338. @request.session[:user_id] = 2
  6339. get(:bulk_edit, :params => {:ids => [1, 3]})
  6340. assert_response :success
  6341. assert_select 'ul#bulk-selection' do
  6342. assert_select 'li', 2
  6343. assert_select 'li a', :text => 'Bug #1'
  6344. end
  6345. assert_select 'form#bulk_edit_form[action=?]', '/issues/bulk_update' do
  6346. assert_select 'input[name=?]', 'ids[]', 2
  6347. assert_select 'input[name=?][value="1"][type=hidden]', 'ids[]'
  6348. assert_select 'select[name=?]', 'issue[project_id]'
  6349. assert_select 'input[name=?]', 'issue[parent_issue_id]'
  6350. # Project specific custom field, date type
  6351. field = CustomField.find(9)
  6352. assert !field.is_for_all?
  6353. assert_equal 'date', field.field_format
  6354. assert_select 'input[name=?]', 'issue[custom_field_values][9]'
  6355. # System wide custom field
  6356. assert CustomField.find(1).is_for_all?
  6357. assert_select 'select[name=?]', 'issue[custom_field_values][1]'
  6358. # Be sure we don't display inactive IssuePriorities
  6359. assert_not IssuePriority.find(15).active?
  6360. assert_select 'select[name=?]', 'issue[priority_id]' do
  6361. assert_select 'option[value="15"]', 0
  6362. end
  6363. # Initial form should hide 'follow' button
  6364. assert_select 'input[type=submit]', 1 do
  6365. assert_select '[name=?]', 'commit', 1
  6366. assert_select '[name=?]', 'follow', 0
  6367. end
  6368. end
  6369. end
  6370. test "bulk_edit should show follow button when project is selected" do
  6371. @request.session[:user_id] = 2
  6372. post(
  6373. :bulk_edit,
  6374. :params => {
  6375. :ids => [1, 3],
  6376. :issue => {
  6377. :project_id => 2,
  6378. }
  6379. }
  6380. )
  6381. assert_response :success
  6382. assert_select 'form#bulk_edit_form[action=?]', '/issues/bulk_update' do
  6383. assert_select 'input[type=submit]', 2 do
  6384. assert_select '[name=?]', 'commit', 1
  6385. assert_select '[name=?]', 'follow', 1
  6386. end
  6387. end
  6388. end
  6389. test "bulk_edit should hide follow button when project is not changed" do
  6390. @request.session[:user_id] = 2
  6391. post(
  6392. :bulk_edit,
  6393. :params => {
  6394. :ids => [1, 3],
  6395. :issue => {
  6396. :project_id => "",
  6397. }
  6398. }
  6399. )
  6400. assert_response :success
  6401. assert_select 'form#bulk_edit_form[action=?]', '/issues/bulk_update' do
  6402. assert_select 'input[type=submit]', 1 do
  6403. assert_select '[name=?]', 'commit', 1
  6404. assert_select '[name=?]', 'follow', 0
  6405. end
  6406. end
  6407. end
  6408. def test_get_bulk_edit_on_different_projects
  6409. @request.session[:user_id] = 2
  6410. get(:bulk_edit, :params => {:ids => [1, 2, 6]})
  6411. assert_response :success
  6412. # Can not set issues from different projects as children of an issue
  6413. assert_select 'input[name=?]', 'issue[parent_issue_id]', 0
  6414. # Project specific custom field, date type
  6415. field = CustomField.find(9)
  6416. assert !field.is_for_all?
  6417. assert !field.project_ids.include?(Issue.find(6).project_id)
  6418. assert_select 'input[name=?]', 'issue[custom_field_values][9]', 0
  6419. end
  6420. def test_get_bulk_edit_with_user_custom_field
  6421. field =
  6422. IssueCustomField.
  6423. create!(
  6424. :name => 'Tester',
  6425. :field_format => 'user',
  6426. :is_for_all => true,
  6427. :tracker_ids => [1, 2, 3]
  6428. )
  6429. @request.session[:user_id] = 2
  6430. get(:bulk_edit, :params => {:ids => [1, 2]})
  6431. assert_response :success
  6432. assert_select 'select.user_cf[name=?]', "issue[custom_field_values][#{field.id}]" do
  6433. assert_select 'option', Project.find(1).users.count + 3 # "no change" + "none" + "me" options
  6434. end
  6435. end
  6436. def test_get_bulk_edit_with_version_custom_field
  6437. field =
  6438. IssueCustomField.
  6439. create!(
  6440. :name => 'Affected version',
  6441. :field_format => 'version',
  6442. :is_for_all => true,
  6443. :tracker_ids => [1, 2, 3]
  6444. )
  6445. @request.session[:user_id] = 2
  6446. get(:bulk_edit, :params => {:ids => [1, 2]})
  6447. assert_response :success
  6448. assert_select 'select.version_cf[name=?]', "issue[custom_field_values][#{field.id}]" do
  6449. assert_select 'option', Project.find(1).shared_versions.count + 2 # "no change" + "none" options
  6450. end
  6451. end
  6452. def test_get_bulk_edit_with_multi_custom_field
  6453. field = CustomField.find(1)
  6454. field.update_attribute :multiple, true
  6455. @request.session[:user_id] = 2
  6456. get(:bulk_edit, :params => {:ids => [1, 3]})
  6457. assert_response :success
  6458. assert_select 'select[name=?]', 'issue[custom_field_values][1][]' do
  6459. assert_select 'option', field.possible_values.size + 1 # "none" options
  6460. end
  6461. end
  6462. def test_bulk_edit_should_propose_to_clear_text_custom_fields
  6463. @request.session[:user_id] = 2
  6464. get(:bulk_edit, :params => {:ids => [1, 3]})
  6465. assert_response :success
  6466. assert_select 'input[name=?][value=?]', 'issue[custom_field_values][2]', '__none__'
  6467. end
  6468. def test_bulk_edit_should_only_propose_statuses_allowed_for_all_issues
  6469. WorkflowTransition.delete_all
  6470. WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
  6471. :old_status_id => 1, :new_status_id => 1)
  6472. WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
  6473. :old_status_id => 1, :new_status_id => 3)
  6474. WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
  6475. :old_status_id => 1, :new_status_id => 4)
  6476. WorkflowTransition.create!(:role_id => 1, :tracker_id => 2,
  6477. :old_status_id => 2, :new_status_id => 1)
  6478. WorkflowTransition.create!(:role_id => 1, :tracker_id => 2,
  6479. :old_status_id => 2, :new_status_id => 3)
  6480. WorkflowTransition.create!(:role_id => 1, :tracker_id => 2,
  6481. :old_status_id => 2, :new_status_id => 5)
  6482. @request.session[:user_id] = 2
  6483. get(:bulk_edit, :params => {:ids => [1, 2]})
  6484. assert_select 'select[name=?]', 'issue[status_id]' do
  6485. assert_select 'option[value=""]'
  6486. assert_select 'option[value="1"]'
  6487. assert_select 'option[value="3"]'
  6488. assert_select 'option', 3 # 2 statuses + "no change" option
  6489. end
  6490. end
  6491. def test_bulk_edit_should_propose_target_project_open_shared_versions
  6492. @request.session[:user_id] = 2
  6493. post(
  6494. :bulk_edit,
  6495. :params => {
  6496. :ids => [1, 2, 6],
  6497. :issue => {
  6498. :project_id => 1
  6499. }
  6500. }
  6501. )
  6502. assert_response :success
  6503. expected_versions = Project.find(1).shared_versions.open.to_a.sort
  6504. assert_select 'select[name=?]', 'issue[fixed_version_id]' do
  6505. expected_versions.each do |version|
  6506. assert_select 'option[value=?]', version.id.to_s
  6507. end
  6508. assert_select 'option[value=""]'
  6509. assert_select 'option[value="none"]'
  6510. assert_select 'option', expected_versions.size + 2
  6511. end
  6512. end
  6513. def test_bulk_edit_should_propose_target_project_categories
  6514. @request.session[:user_id] = 2
  6515. post(
  6516. :bulk_edit,
  6517. :params => {
  6518. :ids => [1, 2, 6],
  6519. :issue => {
  6520. :project_id => 1
  6521. }
  6522. }
  6523. )
  6524. assert_response :success
  6525. expected_categories = Project.find(1).issue_categories.sort
  6526. assert_select 'select[name=?]', 'issue[category_id]' do
  6527. expected_categories.each do |category|
  6528. assert_select 'option[value=?]', category.id.to_s
  6529. end
  6530. assert_select 'option[value=""]'
  6531. assert_select 'option[value="none"]'
  6532. assert_select 'option', expected_categories.size + 2
  6533. end
  6534. end
  6535. def test_bulk_edit_should_only_propose_issues_trackers_custom_fields
  6536. IssueCustomField.delete_all
  6537. field1 = IssueCustomField.generate!(:tracker_ids => [1], :is_for_all => true)
  6538. field2 = IssueCustomField.generate!(:tracker_ids => [2], :is_for_all => true)
  6539. @request.session[:user_id] = 2
  6540. issue_ids = Issue.where(:project_id => 1, :tracker_id => 1).limit(2).ids
  6541. get(
  6542. :bulk_edit,
  6543. :params => {
  6544. :ids => issue_ids
  6545. }
  6546. )
  6547. assert_response :success
  6548. assert_select 'input[name=?]', "issue[custom_field_values][#{field1.id}]"
  6549. assert_select 'input[name=?]', "issue[custom_field_values][#{field2.id}]", 0
  6550. end
  6551. def test_bulk_edit_should_propose_target_tracker_custom_fields
  6552. IssueCustomField.delete_all
  6553. field1 = IssueCustomField.generate!(:tracker_ids => [1], :is_for_all => true)
  6554. field2 = IssueCustomField.generate!(:tracker_ids => [2], :is_for_all => true)
  6555. @request.session[:user_id] = 2
  6556. issue_ids = Issue.where(:project_id => 1, :tracker_id => 1).limit(2).ids
  6557. get(
  6558. :bulk_edit,
  6559. :params => {
  6560. :ids => issue_ids,
  6561. :issue => {
  6562. :tracker_id => 2
  6563. }
  6564. }
  6565. )
  6566. assert_response :success
  6567. assert_select 'input[name=?]', "issue[custom_field_values][#{field1.id}]", 0
  6568. assert_select 'input[name=?]', "issue[custom_field_values][#{field2.id}]"
  6569. end
  6570. def test_bulk_edit_should_warn_about_custom_field_values_about_to_be_cleared
  6571. CustomField.destroy_all
  6572. cleared = IssueCustomField.generate!(:name => 'Cleared',
  6573. :tracker_ids => [2],
  6574. :is_for_all => true)
  6575. CustomValue.create!(:customized => Issue.find(2),
  6576. :custom_field => cleared,
  6577. :value => 'foo')
  6578. not_cleared = IssueCustomField.generate!(:name => 'Not cleared',
  6579. :tracker_ids => [2, 3],
  6580. :is_for_all => true)
  6581. CustomValue.create!(:customized => Issue.find(2),
  6582. :custom_field => not_cleared,
  6583. :value => 'bar')
  6584. @request.session[:user_id] = 2
  6585. get(
  6586. :bulk_edit,
  6587. :params => {
  6588. :ids => [1, 2],
  6589. :issue => {
  6590. :tracker_id => 3
  6591. }
  6592. }
  6593. )
  6594. assert_response :success
  6595. assert_select '.warning', :text => /automatic deletion of values/
  6596. assert_select '.warning span', :text => 'Cleared (1)'
  6597. assert_select '.warning span', :text => /Not cleared/, :count => 0
  6598. end
  6599. def test_bulk_update
  6600. @request.session[:user_id] = 2
  6601. # update issues priority
  6602. post(
  6603. :bulk_update,
  6604. :params => {
  6605. :ids => [1, 2],
  6606. :notes => 'Bulk editing',
  6607. :issue => {
  6608. :priority_id => 7,
  6609. :assigned_to_id => '',
  6610. :custom_field_values => {
  6611. '2' => ''
  6612. }
  6613. }
  6614. }
  6615. )
  6616. assert_response 302
  6617. # check that the issues were updated
  6618. assert_equal [7, 7], Issue.where(:id =>[1, 2]).collect {|i| i.priority.id}
  6619. issue = Issue.find(1)
  6620. journal = issue.journals.reorder('created_on DESC').first
  6621. assert_equal '125', issue.custom_value_for(2).value
  6622. assert_equal 'Bulk editing', journal.notes
  6623. assert_equal 1, journal.details.size
  6624. end
  6625. def test_bulk_update_with_group_assignee
  6626. group = Group.find(11)
  6627. project = Project.find(1)
  6628. project.members << Member.new(:principal => group, :roles => [Role.givable.first])
  6629. @request.session[:user_id] = 2
  6630. # update issues assignee
  6631. with_settings :issue_group_assignment => '1' do
  6632. post(
  6633. :bulk_update,
  6634. :params => {
  6635. :ids => [1, 2],
  6636. :notes => 'Bulk editing',
  6637. :issue => {
  6638. :priority_id => '',
  6639. :assigned_to_id => group.id,
  6640. :custom_field_values => {
  6641. '2' => ''
  6642. }
  6643. }
  6644. }
  6645. )
  6646. assert_response 302
  6647. assert_equal [group, group], Issue.where(:id => [1, 2]).collect {|i| i.assigned_to}
  6648. end
  6649. end
  6650. def test_bulk_update_on_different_projects
  6651. @request.session[:user_id] = 2
  6652. # update issues priority
  6653. post(
  6654. :bulk_update,
  6655. :params => {
  6656. :ids => [1, 2, 6],
  6657. :notes => 'Bulk editing',
  6658. :issue => {
  6659. :priority_id => 7,
  6660. :assigned_to_id => '',
  6661. :custom_field_values => {
  6662. '2' => ''
  6663. }
  6664. }
  6665. }
  6666. )
  6667. assert_response 302
  6668. # check that the issues were updated
  6669. assert_equal [7, 7, 7], Issue.find([1, 2, 6]).map(&:priority_id)
  6670. issue = Issue.find(1)
  6671. journal = issue.journals.reorder('created_on DESC').first
  6672. assert_equal '125', issue.custom_value_for(2).value
  6673. assert_equal 'Bulk editing', journal.notes
  6674. assert_equal 1, journal.details.size
  6675. end
  6676. def test_bulk_update_on_different_projects_without_rights
  6677. @request.session[:user_id] = 3
  6678. user = User.find(3)
  6679. action = {:controller => "issues", :action => "bulk_update"}
  6680. assert user.allowed_to?(action, Issue.find(1).project)
  6681. assert_not user.allowed_to?(action, Issue.find(6).project)
  6682. post(
  6683. :bulk_update,
  6684. :params => {
  6685. :ids => [1, 6],
  6686. :notes => 'Bulk should fail',
  6687. :issue => {
  6688. :priority_id => 7,
  6689. :assigned_to_id => '',
  6690. :custom_field_values => {
  6691. '2' => ''
  6692. }
  6693. }
  6694. }
  6695. )
  6696. assert_response 403
  6697. assert_not_equal "Bulk should fail", Journal.last.notes
  6698. end
  6699. def test_bulk_update_should_send_a_notification
  6700. @request.session[:user_id] = 2
  6701. ActionMailer::Base.deliveries.clear
  6702. with_settings :notified_events => %w(issue_updated) do
  6703. post(
  6704. :bulk_update,
  6705. :params => {
  6706. :ids => [1, 2],
  6707. :notes => 'Bulk editing',
  6708. :issue => {
  6709. :priority_id => 7,
  6710. :assigned_to_id => '',
  6711. :custom_field_values => {'2' => ''}
  6712. }
  6713. }
  6714. )
  6715. assert_response 302
  6716. # 4 emails for 2 members and 2 issues
  6717. # 1 email for a watcher of issue #2
  6718. assert_equal 5, ActionMailer::Base.deliveries.size
  6719. end
  6720. end
  6721. def test_bulk_update_project
  6722. @request.session[:user_id] = 2
  6723. post(
  6724. :bulk_update,
  6725. :params => {
  6726. :ids => [1, 2],
  6727. :issue => {
  6728. :project_id => '2'
  6729. }
  6730. }
  6731. )
  6732. assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
  6733. # Issues moved to project 2
  6734. assert_equal 2, Issue.find(1).project_id
  6735. assert_equal 2, Issue.find(2).project_id
  6736. # No tracker change
  6737. assert_equal 1, Issue.find(1).tracker_id
  6738. assert_equal 2, Issue.find(2).tracker_id
  6739. end
  6740. def test_bulk_update_project_on_single_issue_should_follow_when_needed
  6741. @request.session[:user_id] = 2
  6742. post(
  6743. :bulk_update,
  6744. :params => {
  6745. :id => 1,
  6746. :issue => {
  6747. :project_id => '2'
  6748. },
  6749. :follow => '1'
  6750. }
  6751. )
  6752. assert_redirected_to '/issues/1'
  6753. end
  6754. def test_bulk_update_project_on_multiple_issues_should_follow_when_needed
  6755. @request.session[:user_id] = 2
  6756. post(
  6757. :bulk_update,
  6758. :params => {
  6759. :id => [1, 2],
  6760. :issue => {
  6761. :project_id => '2'
  6762. },
  6763. :follow => '1'
  6764. }
  6765. )
  6766. assert_redirected_to '/projects/onlinestore/issues'
  6767. end
  6768. def test_bulk_update_tracker
  6769. @request.session[:user_id] = 2
  6770. post(
  6771. :bulk_update, :params => {
  6772. :ids => [1, 2],
  6773. :issue => {
  6774. :tracker_id => '2'
  6775. }
  6776. }
  6777. )
  6778. assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
  6779. assert_equal 2, Issue.find(1).tracker_id
  6780. assert_equal 2, Issue.find(2).tracker_id
  6781. end
  6782. def test_bulk_update_status
  6783. @request.session[:user_id] = 2
  6784. # update issues priority
  6785. post(
  6786. :bulk_update,
  6787. :params => {
  6788. :ids => [1, 2],
  6789. :notes => 'Bulk editing status',
  6790. :issue => {
  6791. :priority_id => '',
  6792. :assigned_to_id => '',
  6793. :status_id => '5'
  6794. }
  6795. }
  6796. )
  6797. assert_response 302
  6798. issue = Issue.find(1)
  6799. assert issue.closed?
  6800. end
  6801. def test_bulk_update_priority
  6802. @request.session[:user_id] = 2
  6803. post(
  6804. :bulk_update,
  6805. :params => {
  6806. :ids => [1, 2],
  6807. :issue => {
  6808. :priority_id => 6
  6809. }
  6810. }
  6811. )
  6812. assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
  6813. assert_equal 6, Issue.find(1).priority_id
  6814. assert_equal 6, Issue.find(2).priority_id
  6815. end
  6816. def test_bulk_update_with_notes
  6817. @request.session[:user_id] = 2
  6818. post(
  6819. :bulk_update,
  6820. :params => {
  6821. :ids => [1, 2],
  6822. :notes => 'Moving two issues'
  6823. }
  6824. )
  6825. assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
  6826. assert_equal 'Moving two issues', Issue.find(1).journals.sort_by(&:id).last.notes
  6827. assert_equal 'Moving two issues', Issue.find(2).journals.sort_by(&:id).last.notes
  6828. assert_equal false, Issue.find(1).journals.sort_by(&:id).last.private_notes
  6829. assert_equal false, Issue.find(2).journals.sort_by(&:id).last.private_notes
  6830. end
  6831. def test_bulk_update_with_private_notes
  6832. @request.session[:user_id] = 2
  6833. post(
  6834. :bulk_update,
  6835. :params => {
  6836. :ids => [1, 2],
  6837. :notes => 'Moving two issues',
  6838. :issue => {:private_notes => 'true'}
  6839. }
  6840. )
  6841. assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
  6842. assert_equal 'Moving two issues', Issue.find(1).journals.sort_by(&:id).last.notes
  6843. assert_equal 'Moving two issues', Issue.find(2).journals.sort_by(&:id).last.notes
  6844. assert_equal true, Issue.find(1).journals.sort_by(&:id).last.private_notes
  6845. assert_equal true, Issue.find(2).journals.sort_by(&:id).last.private_notes
  6846. end
  6847. def test_bulk_update_parent_id
  6848. IssueRelation.delete_all
  6849. @request.session[:user_id] = 2
  6850. post(
  6851. :bulk_update,
  6852. :params => {
  6853. :ids => [1, 3],
  6854. :notes => 'Bulk editing parent',
  6855. :issue => {
  6856. :priority_id => '',
  6857. :assigned_to_id => '',
  6858. :status_id => '',
  6859. :parent_issue_id => '2'
  6860. }
  6861. }
  6862. )
  6863. assert_response 302
  6864. parent = Issue.find(2)
  6865. assert_equal parent.id, Issue.find(1).parent_id
  6866. assert_equal parent.id, Issue.find(3).parent_id
  6867. assert_equal [1, 3], parent.children.collect(&:id).sort
  6868. end
  6869. def test_bulk_update_estimated_hours
  6870. @request.session[:user_id] = 2
  6871. post(
  6872. :bulk_update,
  6873. :params => {
  6874. :ids => [1, 2],
  6875. :issue => {
  6876. :estimated_hours => 4.25
  6877. }
  6878. }
  6879. )
  6880. assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
  6881. assert_equal 4.25, Issue.find(1).estimated_hours
  6882. assert_equal 4.25, Issue.find(2).estimated_hours
  6883. end
  6884. def test_bulk_update_custom_field
  6885. @request.session[:user_id] = 2
  6886. # update issues priority
  6887. post(
  6888. :bulk_update,
  6889. :params => {
  6890. :ids => [1, 2],
  6891. :notes => 'Bulk editing custom field',
  6892. :issue => {
  6893. :priority_id => '',
  6894. :assigned_to_id => '',
  6895. :custom_field_values => {
  6896. '2' => '777'
  6897. }
  6898. }
  6899. }
  6900. )
  6901. assert_response 302
  6902. issue = Issue.find(1)
  6903. journal = issue.journals.reorder('created_on DESC').first
  6904. assert_equal '777', issue.custom_value_for(2).value
  6905. assert_equal 1, journal.details.size
  6906. assert_equal '125', journal.details.first.old_value
  6907. assert_equal '777', journal.details.first.value
  6908. end
  6909. def test_bulk_update_custom_field_to_blank
  6910. @request.session[:user_id] = 2
  6911. post(
  6912. :bulk_update,
  6913. :params => {
  6914. :ids => [1, 3],
  6915. :notes => 'Bulk editing custom field',
  6916. :issue => {
  6917. :priority_id => '',
  6918. :assigned_to_id => '',
  6919. :custom_field_values => {
  6920. '1' => '__none__'
  6921. }
  6922. }
  6923. }
  6924. )
  6925. assert_response 302
  6926. assert_equal '', Issue.find(1).custom_field_value(1)
  6927. assert_equal '', Issue.find(3).custom_field_value(1)
  6928. end
  6929. def test_bulk_update_multi_custom_field
  6930. field = CustomField.find(1)
  6931. field.update_attribute :multiple, true
  6932. @request.session[:user_id] = 2
  6933. post(
  6934. :bulk_update,
  6935. :params => {
  6936. :ids => [1, 2, 3],
  6937. :notes => 'Bulk editing multi custom field',
  6938. :issue => {
  6939. :priority_id => '',
  6940. :assigned_to_id => '',
  6941. :custom_field_values => {
  6942. '1' => ['MySQL', 'Oracle']
  6943. }
  6944. }
  6945. }
  6946. )
  6947. assert_response 302
  6948. assert_equal ['MySQL', 'Oracle'], Issue.find(1).custom_field_value(1).sort
  6949. assert_equal ['MySQL', 'Oracle'], Issue.find(3).custom_field_value(1).sort
  6950. # the custom field is not associated with the issue tracker
  6951. assert_nil Issue.find(2).custom_field_value(1)
  6952. end
  6953. def test_bulk_update_multi_custom_field_to_blank
  6954. field = CustomField.find(1)
  6955. field.update_attribute :multiple, true
  6956. @request.session[:user_id] = 2
  6957. post(
  6958. :bulk_update,
  6959. :params => {
  6960. :ids => [1, 3],
  6961. :notes => 'Bulk editing multi custom field',
  6962. :issue => {
  6963. :priority_id => '',
  6964. :assigned_to_id => '',
  6965. :custom_field_values => {
  6966. '1' => ['__none__']
  6967. }
  6968. }
  6969. }
  6970. )
  6971. assert_response 302
  6972. assert_equal [''], Issue.find(1).custom_field_value(1)
  6973. assert_equal [''], Issue.find(3).custom_field_value(1)
  6974. end
  6975. def test_bulk_update_unassign
  6976. assert_not_nil Issue.find(2).assigned_to
  6977. @request.session[:user_id] = 2
  6978. # unassign issues
  6979. post(
  6980. :bulk_update,
  6981. :params => {
  6982. :ids => [1, 2],
  6983. :notes => 'Bulk unassigning',
  6984. :issue => {
  6985. :assigned_to_id => 'none'
  6986. }
  6987. }
  6988. )
  6989. assert_response 302
  6990. # check that the issues were updated
  6991. assert_nil Issue.find(2).assigned_to
  6992. end
  6993. def test_post_bulk_update_should_allow_fixed_version_to_be_set_to_a_subproject
  6994. @request.session[:user_id] = 2
  6995. post(
  6996. :bulk_update,
  6997. :params => {
  6998. :ids => [1, 2],
  6999. :issue => {
  7000. :fixed_version_id => 4
  7001. }
  7002. }
  7003. )
  7004. assert_response :redirect
  7005. issues = Issue.find([1, 2])
  7006. issues.each do |issue|
  7007. assert_equal 4, issue.fixed_version_id
  7008. assert_not_equal issue.project_id, issue.fixed_version.project_id
  7009. end
  7010. end
  7011. def test_post_bulk_update_should_redirect_back_using_the_back_url_parameter
  7012. @request.session[:user_id] = 2
  7013. post(
  7014. :bulk_update,
  7015. :params => {
  7016. :ids => [1, 2],
  7017. :back_url => '/issues'
  7018. }
  7019. )
  7020. assert_response :redirect
  7021. assert_redirected_to '/issues'
  7022. end
  7023. def test_post_bulk_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
  7024. @request.session[:user_id] = 2
  7025. post(
  7026. :bulk_update,
  7027. :params => {
  7028. :ids => [1, 2],
  7029. :back_url => 'http://google.com'
  7030. }
  7031. )
  7032. assert_response :redirect
  7033. assert_redirected_to(
  7034. :controller => 'issues', :action => 'index',
  7035. :project_id => Project.find(1).identifier
  7036. )
  7037. end
  7038. def test_bulk_update_with_all_failures_should_show_errors
  7039. @request.session[:user_id] = 2
  7040. post(
  7041. :bulk_update,
  7042. :params => {
  7043. :ids => [1, 2],
  7044. :issue => {
  7045. :start_date => 'foo'
  7046. }
  7047. }
  7048. )
  7049. assert_response :success
  7050. assert_select '#errorExplanation span', :text => 'Failed to save 2 issue(s) on 2 selected: #1, #2.'
  7051. assert_select '#errorExplanation ul li', :text => 'Start date is not a valid date: #1, #2'
  7052. end
  7053. def test_bulk_update_with_some_failures_should_show_errors
  7054. issue1 = Issue.generate!(:start_date => '2013-05-12')
  7055. issue2 = Issue.generate!(:start_date => '2013-05-15')
  7056. issue3 = Issue.generate!
  7057. @request.session[:user_id] = 2
  7058. post(
  7059. :bulk_update,
  7060. :params => {
  7061. :ids => [issue1.id, issue2.id, issue3.id],
  7062. :issue => {
  7063. :due_date => '2013-05-01'
  7064. }
  7065. }
  7066. )
  7067. assert_response :success
  7068. assert_select '#errorExplanation span',
  7069. :text => "Failed to save 2 issue(s) on 3 selected: ##{issue1.id}, ##{issue2.id}."
  7070. assert_select '#errorExplanation ul li',
  7071. :text => "Due date must be greater than start date: ##{issue1.id}, ##{issue2.id}"
  7072. assert_select '#bulk-selection li', 2
  7073. end
  7074. def test_bulk_update_with_failure_should_preserved_form_values
  7075. @request.session[:user_id] = 2
  7076. post(
  7077. :bulk_update,
  7078. :params => {
  7079. :ids => [1, 2],
  7080. :issue => {
  7081. :tracker_id => '2',
  7082. :start_date => 'foo'
  7083. }
  7084. }
  7085. )
  7086. assert_response :success
  7087. assert_select 'select[name=?]', 'issue[tracker_id]' do
  7088. assert_select 'option[value="2"][selected=selected]'
  7089. end
  7090. assert_select 'input[name=?][value=?]', 'issue[start_date]', 'foo'
  7091. end
  7092. def test_get_bulk_copy
  7093. assert_not Issue.find(1).attachments.any?
  7094. assert Issue.find(2).attachments.any?
  7095. assert Issue.find(3).attachments.any?
  7096. @request.session[:user_id] = 2
  7097. get(
  7098. :bulk_edit,
  7099. :params => {
  7100. :ids => [1, 2, 3],
  7101. :copy => '1'
  7102. }
  7103. )
  7104. assert_response :success
  7105. assert_select '#bulk-selection li', 3
  7106. assert_select 'form#bulk_edit_form[action=?]', '/issues/bulk_update' do
  7107. assert_select 'select[name=?]', 'issue[project_id]' do
  7108. assert_select 'option[value=""]'
  7109. end
  7110. assert_select 'input[name=copy_attachments]'
  7111. end
  7112. end
  7113. test "bulk copy should show follow button when project is selected" do
  7114. @request.session[:user_id] = 2
  7115. post(
  7116. :bulk_edit,
  7117. :params => {
  7118. :ids => [1, 3],
  7119. :issue => {
  7120. :project_id => 2,
  7121. },
  7122. :copy => '1',
  7123. }
  7124. )
  7125. assert_response :success
  7126. assert_select 'form#bulk_edit_form[action=?]', '/issues/bulk_update' do
  7127. assert_select 'input[type=submit]', 2 do
  7128. assert_select '[name=?]', 'commit', 1
  7129. assert_select '[name=?]', 'follow', 1
  7130. end
  7131. end
  7132. end
  7133. def test_get_bulk_copy_without_add_issues_permission_should_not_propose_current_project_as_target
  7134. user = setup_user_with_copy_but_not_add_permission
  7135. @request.session[:user_id] = user.id
  7136. get(
  7137. :bulk_edit,
  7138. :params => {
  7139. :ids => [1, 2, 3],
  7140. :copy => '1'
  7141. }
  7142. )
  7143. assert_response :success
  7144. assert_select 'form#bulk_edit_form[action=?]', '/issues/bulk_update' do
  7145. assert_select 'select[name=?]', 'issue[project_id]' do
  7146. assert_select 'option[value=""]', 0
  7147. assert_select 'option[value="2"]'
  7148. end
  7149. end
  7150. end
  7151. def test_bulk_copy_to_another_project
  7152. @request.session[:user_id] = 2
  7153. issue_ids = [1, 2]
  7154. assert_difference 'Issue.count', issue_ids.size do
  7155. assert_no_difference 'Project.find(1).issues.count' do
  7156. post(
  7157. :bulk_update,
  7158. :params => {
  7159. :ids => issue_ids,
  7160. :issue => {
  7161. :project_id => '2'
  7162. },
  7163. :copy => '1'
  7164. }
  7165. )
  7166. end
  7167. end
  7168. assert_redirected_to '/projects/ecookbook/issues'
  7169. copies = Issue.order('id DESC').limit(issue_ids.size)
  7170. copies.each do |copy|
  7171. assert_equal 2, copy.project_id
  7172. end
  7173. end
  7174. def test_bulk_copy_without_add_issues_permission_should_be_allowed_on_project_with_permission
  7175. user = setup_user_with_copy_but_not_add_permission
  7176. @request.session[:user_id] = user.id
  7177. assert_difference 'Issue.count', 3 do
  7178. post(
  7179. :bulk_update,
  7180. :params => {
  7181. :ids => [1, 2, 3],
  7182. :issue => {
  7183. :project_id => '2'
  7184. },
  7185. :copy => '1'
  7186. }
  7187. )
  7188. assert_response 302
  7189. end
  7190. end
  7191. def test_bulk_copy_on_same_project_without_add_issues_permission_should_be_denied
  7192. user = setup_user_with_copy_but_not_add_permission
  7193. @request.session[:user_id] = user.id
  7194. post(
  7195. :bulk_update,
  7196. :params => {
  7197. :ids => [1, 2, 3],
  7198. :issue => {
  7199. :project_id => ''
  7200. },
  7201. :copy => '1'
  7202. }
  7203. )
  7204. assert_response 403
  7205. end
  7206. def test_bulk_copy_on_different_project_without_add_issues_permission_should_be_denied
  7207. user = setup_user_with_copy_but_not_add_permission
  7208. @request.session[:user_id] = user.id
  7209. post(
  7210. :bulk_update,
  7211. :params => {
  7212. :ids => [1, 2, 3],
  7213. :issue => {
  7214. :project_id => '1'
  7215. },
  7216. :copy => '1'
  7217. }
  7218. )
  7219. assert_response 403
  7220. end
  7221. def test_bulk_copy_should_allow_not_changing_the_issue_attributes
  7222. @request.session[:user_id] = 2
  7223. issues = [
  7224. Issue.create!(:project_id => 1, :tracker_id => 1, :status_id => 1,
  7225. :priority_id => 2, :subject => 'issue 1', :author_id => 1,
  7226. :assigned_to_id => nil),
  7227. Issue.create!(:project_id => 2, :tracker_id => 3, :status_id => 2,
  7228. :priority_id => 1, :subject => 'issue 2', :author_id => 2,
  7229. :assigned_to_id => 2)
  7230. ]
  7231. assert_difference 'Issue.count', issues.size do
  7232. post(
  7233. :bulk_update,
  7234. :params => {
  7235. :ids => issues.map(&:id),
  7236. :copy => '1',
  7237. :issue => {
  7238. :project_id => '',
  7239. :tracker_id => '',
  7240. :assigned_to_id => '',
  7241. :status_id => '',
  7242. :start_date => '',
  7243. :due_date => ''
  7244. }
  7245. }
  7246. )
  7247. end
  7248. copies = Issue.order('id DESC').limit(issues.size)
  7249. issues.each do |orig|
  7250. copy = copies.detect {|c| c.subject == orig.subject}
  7251. assert_not_nil copy
  7252. assert_equal orig.project_id, copy.project_id
  7253. assert_equal orig.tracker_id, copy.tracker_id
  7254. assert_equal 1, copy.status_id
  7255. if orig.assigned_to_id
  7256. assert_equal orig.assigned_to_id, copy.assigned_to_id
  7257. else
  7258. assert_nil copy.assigned_to_id
  7259. end
  7260. assert_equal orig.priority_id, copy.priority_id
  7261. end
  7262. end
  7263. def test_bulk_copy_should_allow_changing_the_issue_attributes
  7264. # Fixes random test failure with Mysql
  7265. # where Issue.where(:project_id => 2).limit(2).order('id desc')
  7266. # doesn't return the expected results
  7267. Issue.where("project_id=2").delete_all
  7268. @request.session[:user_id] = 2
  7269. assert_difference 'Issue.count', 2 do
  7270. assert_no_difference 'Project.find(1).issues.count' do
  7271. post(
  7272. :bulk_update,
  7273. :params => {
  7274. :ids => [1, 2],
  7275. :copy => '1',
  7276. :issue => {
  7277. :project_id => '2',
  7278. :tracker_id => '',
  7279. :assigned_to_id => '2',
  7280. :status_id => '1',
  7281. :start_date => '2009-12-01',
  7282. :due_date => '2009-12-31'
  7283. }
  7284. }
  7285. )
  7286. end
  7287. end
  7288. copied_issues = Issue.where(:project_id => 2).limit(2).order('id desc').to_a
  7289. assert_equal 2, copied_issues.size
  7290. copied_issues.each do |issue|
  7291. assert_equal 2, issue.project_id, "Project is incorrect"
  7292. assert_equal 2, issue.assigned_to_id, "Assigned to is incorrect"
  7293. assert_equal 1, issue.status_id, "Status is incorrect"
  7294. assert_equal '2009-12-01', issue.start_date.to_s, "Start date is incorrect"
  7295. assert_equal '2009-12-31', issue.due_date.to_s, "Due date is incorrect"
  7296. end
  7297. end
  7298. def test_bulk_copy_should_allow_adding_a_note
  7299. @request.session[:user_id] = 2
  7300. assert_difference 'Issue.count', 1 do
  7301. post(
  7302. :bulk_update,
  7303. :params => {
  7304. :ids => [1],
  7305. :copy => '1',
  7306. :notes => 'Copying one issue',
  7307. :issue => {
  7308. :project_id => '',
  7309. :tracker_id => '',
  7310. :status_id => '3',
  7311. :start_date => '2009-12-01',
  7312. :due_date => '2009-12-31'
  7313. }
  7314. }
  7315. )
  7316. end
  7317. issue = Issue.order('id DESC').first
  7318. assert_equal 1, issue.journals.size
  7319. journal = issue.journals.first
  7320. assert_equal 'Copying one issue', journal.notes
  7321. end
  7322. def test_bulk_copy_should_allow_not_copying_the_attachments
  7323. attachment_count = Issue.find(3).attachments.size
  7324. assert attachment_count > 0
  7325. @request.session[:user_id] = 2
  7326. assert_difference 'Issue.count', 1 do
  7327. assert_no_difference 'Attachment.count' do
  7328. post(
  7329. :bulk_update,
  7330. :params => {
  7331. :ids => [3],
  7332. :copy => '1',
  7333. :copy_attachments => '0',
  7334. :issue => {
  7335. :project_id => ''
  7336. }
  7337. }
  7338. )
  7339. end
  7340. end
  7341. end
  7342. def test_bulk_copy_should_allow_copying_the_attachments
  7343. attachment_count = Issue.find(3).attachments.size
  7344. assert attachment_count > 0
  7345. @request.session[:user_id] = 2
  7346. assert_difference 'Issue.count', 1 do
  7347. assert_difference 'Attachment.count', attachment_count do
  7348. post(
  7349. :bulk_update,
  7350. :params => {
  7351. :ids => [3],
  7352. :copy => '1',
  7353. :copy_attachments => '1',
  7354. :issue => {
  7355. :project_id => ''
  7356. }
  7357. }
  7358. )
  7359. end
  7360. end
  7361. end
  7362. def test_bulk_copy_should_add_relations_with_copied_issues
  7363. @request.session[:user_id] = 2
  7364. assert_difference 'Issue.count', 2 do
  7365. assert_difference 'IssueRelation.count', 2 do
  7366. post(
  7367. :bulk_update,
  7368. :params => {
  7369. :ids => [1, 3],
  7370. :copy => '1',
  7371. :link_copy => '1',
  7372. :issue => {
  7373. :project_id => '1'
  7374. }
  7375. }
  7376. )
  7377. end
  7378. end
  7379. end
  7380. def test_bulk_copy_should_allow_not_copying_the_subtasks
  7381. issue = Issue.generate_with_descendants!
  7382. @request.session[:user_id] = 2
  7383. assert_difference 'Issue.count', 1 do
  7384. post(
  7385. :bulk_update,
  7386. :params => {
  7387. :ids => [issue.id],
  7388. :copy => '1',
  7389. :copy_subtasks => '0',
  7390. :issue => {
  7391. :project_id => ''
  7392. }
  7393. }
  7394. )
  7395. end
  7396. end
  7397. test "bulk copy should allow copying the subtasks" do
  7398. issue = Issue.generate_with_descendants!
  7399. count = issue.descendants.count
  7400. @request.session[:user_id] = 2
  7401. assert_difference 'Issue.count', count + 1 do
  7402. post(
  7403. :bulk_update,
  7404. :params => {
  7405. :ids => [issue.id],
  7406. :copy => '1',
  7407. :copy_subtasks => '1',
  7408. :issue => {
  7409. :project_id => ''
  7410. }
  7411. }
  7412. )
  7413. end
  7414. copy = Issue.where(:parent_id => nil).order("id DESC").first
  7415. assert_equal count, copy.descendants.count
  7416. end
  7417. test "issue bulk copy copy watcher" do
  7418. issue = Issue.find(1)
  7419. Watcher.create!(:watchable => issue, :user => User.find(3))
  7420. Watcher.create!(:watchable => issue, :user => Group.find(10))
  7421. @request.session[:user_id] = 2
  7422. assert_difference 'Issue.count' do
  7423. post(
  7424. :bulk_update,
  7425. :params => {
  7426. :ids => [1],
  7427. :copy => '1',
  7428. :copy_watchers => '1',
  7429. :issue => {
  7430. :project_id => ''
  7431. }
  7432. }
  7433. )
  7434. end
  7435. copy = Issue.order(:id => :desc).first
  7436. assert_equal 2, copy.watchers.count
  7437. assert_equal [3, 10], copy.watcher_user_ids.sort
  7438. end
  7439. def test_bulk_copy_should_not_copy_selected_subtasks_twice
  7440. issue = Issue.generate_with_descendants!
  7441. count = issue.descendants.count
  7442. @request.session[:user_id] = 2
  7443. assert_difference 'Issue.count', count + 1 do
  7444. post(
  7445. :bulk_update,
  7446. :params => {
  7447. :ids => issue.self_and_descendants.map(&:id),
  7448. :copy => '1',
  7449. :copy_subtasks => '1',
  7450. :issue => {
  7451. :project_id => ''
  7452. }
  7453. }
  7454. )
  7455. end
  7456. copy = Issue.where(:parent_id => nil).order("id DESC").first
  7457. assert_equal count, copy.descendants.count
  7458. end
  7459. def test_bulk_copy_to_another_project_should_follow_when_needed
  7460. @request.session[:user_id] = 2
  7461. post(
  7462. :bulk_update,
  7463. :params => {
  7464. :ids => [1],
  7465. :copy => '1',
  7466. :issue => {
  7467. :project_id => 2
  7468. },
  7469. :follow => '1'
  7470. }
  7471. )
  7472. issue = Issue.order('id DESC').first
  7473. assert_redirected_to :controller => 'issues', :action => 'show', :id => issue
  7474. end
  7475. def test_bulk_copy_with_all_failures_should_display_errors
  7476. @request.session[:user_id] = 2
  7477. post(
  7478. :bulk_update,
  7479. :params => {
  7480. :ids => [1, 2],
  7481. :copy => '1',
  7482. :issue => {
  7483. :start_date => 'foo'
  7484. }
  7485. }
  7486. )
  7487. assert_response :success
  7488. end
  7489. def test_destroy_issue_with_no_time_entries_should_delete_the_issues
  7490. set_tmp_attachments_directory
  7491. assert_nil TimeEntry.find_by_issue_id(2)
  7492. @request.session[:user_id] = 2
  7493. assert_difference 'Issue.count', -1 do
  7494. delete(:destroy, :params => {:id => 2})
  7495. end
  7496. assert_redirected_to :action => 'index', :project_id => 'ecookbook'
  7497. assert_equal 'Successful deletion.', flash[:notice]
  7498. assert_nil Issue.find_by_id(2)
  7499. end
  7500. def test_destroy_issues_with_time_entries_should_show_the_reassign_form
  7501. set_tmp_attachments_directory
  7502. @request.session[:user_id] = 2
  7503. with_settings :timelog_required_fields => [] do
  7504. assert_no_difference 'Issue.count' do
  7505. delete(
  7506. :destroy,
  7507. :params => {
  7508. :ids => [1, 3]
  7509. }
  7510. )
  7511. end
  7512. end
  7513. assert_response :success
  7514. assert_select 'form' do
  7515. assert_select 'input[name=_method][value=delete]'
  7516. assert_select 'input[name=todo][value=destroy]'
  7517. assert_select 'input[name=todo][value=nullify]'
  7518. assert_select 'input[name=todo][value=reassign]'
  7519. end
  7520. end
  7521. def test_destroy_issues_with_time_entries_should_not_show_the_nullify_option_when_issue_is_required_for_time_entries
  7522. set_tmp_attachments_directory
  7523. with_settings :timelog_required_fields => ['issue_id'] do
  7524. @request.session[:user_id] = 2
  7525. assert_no_difference 'Issue.count' do
  7526. delete(
  7527. :destroy,
  7528. :params => {
  7529. :ids => [1, 3]
  7530. }
  7531. )
  7532. end
  7533. assert_response :success
  7534. assert_select 'form' do
  7535. assert_select 'input[name=_method][value=delete]'
  7536. assert_select 'input[name=todo][value=destroy]'
  7537. assert_select 'input[name=todo][value=nullify]', 0
  7538. assert_select 'input[name=todo][value=reassign]'
  7539. end
  7540. end
  7541. end
  7542. def test_destroy_issues_with_time_entries_should_show_hours_on_issues_and_descendants
  7543. parent = Issue.generate_with_child!
  7544. TimeEntry.generate!(:issue => parent)
  7545. TimeEntry.generate!(:issue => parent.children.first)
  7546. leaf = Issue.generate!
  7547. TimeEntry.generate!(:issue => leaf)
  7548. @request.session[:user_id] = 2
  7549. delete(
  7550. :destroy,
  7551. :params => {
  7552. :ids => [parent.id, leaf.id]
  7553. }
  7554. )
  7555. assert_response :success
  7556. assert_select 'p', :text => /3\.00 hours were reported/
  7557. end
  7558. def test_destroy_issues_and_destroy_time_entries
  7559. set_tmp_attachments_directory
  7560. @request.session[:user_id] = 2
  7561. assert_difference 'Issue.count', -2 do
  7562. assert_difference 'TimeEntry.count', -3 do
  7563. delete(
  7564. :destroy,
  7565. :params => {
  7566. :ids => [1, 3],
  7567. :todo => 'destroy'
  7568. }
  7569. )
  7570. end
  7571. end
  7572. assert_redirected_to :action => 'index', :project_id => 'ecookbook'
  7573. assert_equal 'Successful deletion.', flash[:notice]
  7574. assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
  7575. assert_nil TimeEntry.find_by_id([1, 2])
  7576. end
  7577. def test_destroy_issues_and_assign_time_entries_to_project
  7578. set_tmp_attachments_directory
  7579. @request.session[:user_id] = 2
  7580. with_settings :timelog_required_fields => [] do
  7581. assert_difference 'Issue.count', -2 do
  7582. assert_no_difference 'TimeEntry.count' do
  7583. delete(
  7584. :destroy,
  7585. :params => {
  7586. :ids => [1, 3],
  7587. :todo => 'nullify'
  7588. }
  7589. )
  7590. end
  7591. end
  7592. end
  7593. assert_redirected_to :action => 'index', :project_id => 'ecookbook'
  7594. assert_equal 'Successful deletion.', flash[:notice]
  7595. assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
  7596. assert_nil TimeEntry.find(1).issue_id
  7597. assert_nil TimeEntry.find(2).issue_id
  7598. end
  7599. def test_destroy_issues_and_reassign_time_entries_to_another_issue
  7600. set_tmp_attachments_directory
  7601. @request.session[:user_id] = 2
  7602. assert_difference 'Issue.count', -2 do
  7603. assert_no_difference 'TimeEntry.count' do
  7604. delete(
  7605. :destroy,
  7606. :params => {
  7607. :ids => [1, 3],
  7608. :todo => 'reassign',
  7609. :reassign_to_id => 2
  7610. }
  7611. )
  7612. end
  7613. end
  7614. assert_redirected_to :action => 'index', :project_id => 'ecookbook'
  7615. assert_equal 'Successful deletion.', flash[:notice]
  7616. assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
  7617. assert_equal 2, TimeEntry.find(1).issue_id
  7618. assert_equal 2, TimeEntry.find(2).issue_id
  7619. end
  7620. def test_destroy_issues_with_time_entries_should_reassign_time_entries_of_issues_and_descendants
  7621. parent = Issue.generate_with_child!
  7622. TimeEntry.generate!(:issue => parent)
  7623. TimeEntry.generate!(:issue => parent.children.first)
  7624. leaf = Issue.generate!
  7625. TimeEntry.generate!(:issue => leaf)
  7626. target = Issue.generate!
  7627. @request.session[:user_id] = 2
  7628. assert_difference 'Issue.count', -3 do
  7629. assert_no_difference 'TimeEntry.count' do
  7630. delete(
  7631. :destroy,
  7632. :params => {
  7633. :ids => [parent.id, leaf.id],
  7634. :todo => 'reassign',
  7635. :reassign_to_id => target.id
  7636. }
  7637. )
  7638. assert_response 302
  7639. assert_equal 'Successful deletion.', flash[:notice]
  7640. end
  7641. end
  7642. assert_equal 3, target.time_entries.count
  7643. end
  7644. def test_destroy_issues_and_reassign_time_entries_to_an_invalid_issue_should_fail
  7645. set_tmp_attachments_directory
  7646. @request.session[:user_id] = 2
  7647. assert_no_difference 'Issue.count' do
  7648. assert_no_difference 'TimeEntry.count' do
  7649. # try to reassign time to an issue of another project
  7650. delete(
  7651. :destroy,
  7652. :params => {
  7653. :ids => [1, 3],
  7654. :todo => 'reassign',
  7655. :reassign_to_id => 4
  7656. }
  7657. )
  7658. end
  7659. end
  7660. assert_response :success
  7661. end
  7662. def test_destroy_issues_and_reassign_time_entries_to_an_issue_to_delete_should_fail
  7663. set_tmp_attachments_directory
  7664. @request.session[:user_id] = 2
  7665. assert_no_difference 'Issue.count' do
  7666. assert_no_difference 'TimeEntry.count' do
  7667. delete(
  7668. :destroy,
  7669. :params => {
  7670. :ids => [1, 3],
  7671. :todo => 'reassign',
  7672. :reassign_to_id => 3
  7673. }
  7674. )
  7675. end
  7676. end
  7677. assert_response :success
  7678. assert_select '#flash_error', :text => I18n.t(:error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted)
  7679. end
  7680. def test_destroy_issues_and_nullify_time_entries_should_fail_when_issue_is_required_for_time_entries
  7681. set_tmp_attachments_directory
  7682. @request.session[:user_id] = 2
  7683. with_settings :timelog_required_fields => ['issue_id'] do
  7684. assert_no_difference 'Issue.count' do
  7685. assert_no_difference 'TimeEntry.count' do
  7686. delete(
  7687. :destroy,
  7688. :params => {
  7689. :ids => [1, 3],
  7690. :todo => 'nullify'
  7691. }
  7692. )
  7693. end
  7694. end
  7695. end
  7696. assert_response :success
  7697. assert_select '#flash_error', :text => 'Issue cannot be blank'
  7698. end
  7699. def test_destroy_issues_from_different_projects
  7700. set_tmp_attachments_directory
  7701. @request.session[:user_id] = 2
  7702. assert_difference 'Issue.count', -3 do
  7703. delete(
  7704. :destroy,
  7705. :params => {
  7706. :ids => [1, 2, 6],
  7707. :todo => 'destroy'
  7708. }
  7709. )
  7710. end
  7711. assert_redirected_to :controller => 'issues', :action => 'index'
  7712. assert_equal 'Successful deletion.', flash[:notice]
  7713. assert !(Issue.find_by_id(1) || Issue.find_by_id(2) || Issue.find_by_id(6))
  7714. end
  7715. def test_destroy_child_issue
  7716. parent = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :subject => 'Parent Issue')
  7717. child = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :subject => 'Child Issue', :parent_issue_id => parent.id)
  7718. assert child.is_descendant_of?(parent.reload)
  7719. @request.session[:user_id] = 2
  7720. assert_difference 'Issue.count', -1 do
  7721. delete :destroy, :params => {:id => child.id}
  7722. end
  7723. assert_response :found
  7724. assert_redirected_to :action => 'index', :project_id => 'ecookbook'
  7725. parent.reload
  7726. assert_equal 2, parent.journals.count
  7727. get :show, :params => {:id => parent.id}
  7728. assert_response :success
  7729. assert_select 'div#tab-content-history' do
  7730. assert_select 'div[id=?]', "change-#{parent.journals.last.id}" do
  7731. assert_select 'ul.details', :text => "Subtask deleted (##{child.id})"
  7732. end
  7733. end
  7734. end
  7735. def test_destroy_parent_and_child_issues
  7736. parent = Issue.create!(:project_id => 1, :author_id => 1,
  7737. :tracker_id => 1, :subject => 'Parent Issue')
  7738. child = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1,
  7739. :subject => 'Child Issue', :parent_issue_id => parent.id)
  7740. assert child.is_descendant_of?(parent.reload)
  7741. @request.session[:user_id] = 2
  7742. assert_difference 'Issue.count', -2 do
  7743. delete(
  7744. :destroy,
  7745. :params => {
  7746. :ids => [parent.id, child.id],
  7747. :todo => 'destroy'
  7748. }
  7749. )
  7750. end
  7751. assert_response 302
  7752. assert_equal 'Successful deletion.', flash[:notice]
  7753. end
  7754. def test_destroy_invalid_should_respond_with_404
  7755. @request.session[:user_id] = 2
  7756. assert_no_difference 'Issue.count' do
  7757. delete(:destroy, :params => {:id => 999})
  7758. end
  7759. assert_response 404
  7760. end
  7761. def test_destroy_with_permission_on_tracker_should_be_allowed
  7762. role = Role.find(1)
  7763. role.set_permission_trackers :delete_issues, [1]
  7764. role.save!
  7765. issue = Issue.generate!(:project_id => 1, :tracker_id => 1)
  7766. @request.session[:user_id] = 2
  7767. assert_difference 'Issue.count', -1 do
  7768. delete(:destroy, :params => {:id => issue.id})
  7769. end
  7770. assert_response 302
  7771. assert_equal 'Successful deletion.', flash[:notice]
  7772. end
  7773. def test_destroy_without_permission_on_tracker_should_be_denied
  7774. role = Role.find(1)
  7775. role.set_permission_trackers :delete_issues, [2]
  7776. role.save!
  7777. issue = Issue.generate!(:project_id => 1, :tracker_id => 1)
  7778. @request.session[:user_id] = 2
  7779. assert_no_difference 'Issue.count' do
  7780. delete(:destroy, :params => {:id => issue.id})
  7781. end
  7782. assert_response 403
  7783. end
  7784. def test_default_search_scope
  7785. get :index
  7786. assert_select 'div#quick-search form' do
  7787. assert_select 'input[name=issues][value="1"][type=hidden]'
  7788. end
  7789. end
  7790. def setup_user_with_copy_but_not_add_permission
  7791. Role.all.each {|r| r.remove_permission! :add_issues}
  7792. Role.find_by_name('Manager').add_permission! :add_issues
  7793. user = User.generate!
  7794. User.add_to_project(user, Project.find(1), Role.find_by_name('Developer'))
  7795. User.add_to_project(user, Project.find(2), Role.find_by_name('Manager'))
  7796. user
  7797. end
  7798. def test_cancel_edit_link_for_issue_show_action_should_have_onclick_action
  7799. @request.session[:user_id] = 1
  7800. get(:show, :params => {:id => 1})
  7801. assert_response :success
  7802. assert_select 'a[href=?][onclick=?]', "/issues/1", "$('#update').hide(); return false;", :text => 'Cancel'
  7803. end
  7804. def test_cancel_edit_link_for_issue_edit_action_should_not_have_onclick_action
  7805. @request.session[:user_id] = 1
  7806. get(:edit, :params => {:id => 1})
  7807. assert_response :success
  7808. assert_select 'a[href=?][onclick=?]', "/issues/1", "", :text => 'Cancel'
  7809. end
  7810. def test_show_should_display_author_gravatar_only_when_not_assigned
  7811. issue = Issue.find(1)
  7812. assert_nil issue.assigned_to_id
  7813. @request.session[:user_id] = 1
  7814. with_settings :gravatar_enabled => '1' do
  7815. get :show, :params => {:id => issue.id}
  7816. assert_select 'div.gravatar-with-child' do
  7817. assert_select 'img.gravatar', 1
  7818. end
  7819. end
  7820. end
  7821. def test_show_should_display_author_and_assignee_gravatars_when_assigned
  7822. issue = Issue.find(1)
  7823. issue.assigned_to_id = 2
  7824. issue.save!
  7825. @request.session[:user_id] = 1
  7826. with_settings :gravatar_enabled => '1' do
  7827. get :show, :params => {:id => issue.id}
  7828. assert_select 'div.gravatar-with-child' do
  7829. assert_select 'img.gravatar', 2
  7830. assert_select 'img.gravatar-child', 1
  7831. end
  7832. end
  7833. end
  7834. def test_show_should_be_able_to_link_to_another_journal_attachment_of_the_same_issue
  7835. @request.session[:user_id] = 1
  7836. issue = Issue.find(2)
  7837. attachment = issue.journals.first.attachments.first
  7838. issue.init_journal(User.first, "attachment:#{attachment.filename}")
  7839. issue.save!
  7840. issue.reload
  7841. get :show, params: { id: issue.id }
  7842. assert_select "div#history div#journal-#{issue.journals.last.id}-notes" do
  7843. assert_select "a[href='/attachments/#{attachment.id}']", :text => 'source.rb'
  7844. end
  7845. end
  7846. def test_show_with_thumbnail_macro_should_be_able_to_fetch_image_of_different_journal
  7847. @request.session[:user_id] = 1
  7848. issue = Issue.find(2)
  7849. attachment = Attachment.generate!(filename: 'foo.png', digest: Redmine::Utils.random_hex(32))
  7850. attachment.update(container: issue)
  7851. issue.init_journal(User.first, "{{thumbnail(#{attachment.filename})}}")
  7852. issue.save!
  7853. issue.reload
  7854. get :show, params: { id: issue.id }
  7855. assert_select "div#history div#journal-#{issue.journals.last.id}-notes" do
  7856. assert_select "a.thumbnail[title=?][href='/attachments/#{attachment.id}']", 'foo.png'
  7857. end
  7858. end
  7859. def test_index_should_retrieve_default_query
  7860. query = IssueQuery.find(4)
  7861. IssueQuery.stubs(:default).returns query
  7862. [nil, 1].each do |user_id|
  7863. @request.session[:user_id] = user_id
  7864. get :index
  7865. assert_select 'h2', text: query.name
  7866. get :index, params: { project_id: 1 }
  7867. assert_select 'h2', text: query.name
  7868. end
  7869. end
  7870. def test_index_should_ignore_default_query_with_without_default
  7871. query = IssueQuery.find(4)
  7872. IssueQuery.stubs(:default).returns query
  7873. [nil, 1].each do |user_id|
  7874. @request.session[:user_id] = user_id
  7875. get :index, params: { set_filter: '1', without_default: '1' }
  7876. assert_select 'h2', text: I18n.t(:label_issue_plural)
  7877. get :index, params: { project_id: 1, set_filter: '1', without_default: '1' }
  7878. assert_select 'h2', text: I18n.t(:label_issue_plural)
  7879. end
  7880. end
  7881. def test_index_should_ignore_default_query_with_session_query
  7882. query = IssueQuery.find 4
  7883. IssueQuery.stubs(:default).returns query
  7884. session_query = IssueQuery.find 1
  7885. @request.session[:issue_query] = { id: 1, project_id: 1}
  7886. @request.session[:user_id] = 1
  7887. get :index, params: { project_id: '1' }
  7888. assert_select 'h2', text: session_query.name
  7889. end
  7890. def test_index_global_should_ignore_default_query_with_session_query
  7891. query = IssueQuery.find 4
  7892. IssueQuery.stubs(:default).returns query
  7893. session_query = IssueQuery.find 5
  7894. @request.session[:issue_query] = { id: 5, project_id: nil}
  7895. @request.session[:user_id] = 1
  7896. get :index
  7897. assert_select 'h2', text: session_query.name
  7898. end
  7899. def test_index_should_use_default_query_with_invalid_session_query
  7900. query = IssueQuery.find 4
  7901. IssueQuery.stubs(:default).returns query
  7902. @request.session[:issue_query] = { id: 1, project_id: 1}
  7903. @request.session[:user_id] = 1
  7904. get :index
  7905. assert_select 'h2', text: query.name
  7906. end
  7907. def test_index_should_not_load_default_query_for_api_request
  7908. query = IssueQuery.find 4
  7909. IssueQuery.stubs(:default).returns query
  7910. @request.session[:user_id] = 1
  7911. get :index, params: { format: 'json' }
  7912. assert results = JSON.parse(@response.body)['issues']
  7913. # query filters for tracker_id == 3
  7914. assert results.detect{ |i| i['tracker_id'] != 3 }
  7915. end
  7916. def test_index_should_ignore_user_default_query_if_it_is_invisible
  7917. query = IssueQuery.find(4)
  7918. query.update(visibility: Query::VISIBILITY_PRIVATE, user_id: 2)
  7919. query.save!
  7920. # If visible default query
  7921. @request.session[:user_id] = 2
  7922. @request.session[:issue_query] = nil
  7923. User.find(2).pref.update(default_issue_query: query.id)
  7924. get :index
  7925. assert_select 'h2', text: query.name
  7926. # If invisible default query
  7927. @request.session[:user_id] = 3
  7928. @request.session[:issue_query] = nil
  7929. User.find(3).pref.update(default_issue_query: query.id)
  7930. get :index
  7931. assert_select 'h2', text: 'Issues'
  7932. end
  7933. def test_index_should_ignore_project_default_query_if_it_is_not_public
  7934. query = IssueQuery.find(1)
  7935. query.project.update(default_issue_query: query)
  7936. query.update(visibility: Query::VISIBILITY_PRIVATE, user_id: 2)
  7937. query.save!
  7938. [User.find(1), User.find(2)].each do |user|
  7939. @request.session[:user_id] = user.id
  7940. @request.session[:issue_query] = nil
  7941. get :index, params: { project_id: query.project.id }
  7942. assert_select 'h2', text: 'Issues'
  7943. end
  7944. end
  7945. def test_index_should_ignore_global_default_query_if_it_is_not_public
  7946. query = IssueQuery.find(1)
  7947. with_settings default_issue_query: query.id do
  7948. query.update(visibility: Query::VISIBILITY_PRIVATE, user_id: 2)
  7949. query.save!
  7950. [User.find(1), User.find(2)].each do |user|
  7951. @request.session[:user_id] = user.id
  7952. @request.session[:issue_query] = nil
  7953. get :index
  7954. assert_select 'h2', text: 'Issues'
  7955. end
  7956. end
  7957. end
  7958. end