提交 484d9520 编写于 作者: A Albert So 提交者: Kohsuke Kawaguchi

[JENKINS-11762] Changes to add a configurable display name to jobs

上级 05b46659
/*
* The MIT License
*
* Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi,
* Daniel Dyer, Tom Huybrechts
* Copyright (c) 2004-2011, Sun Microsystems, Inc., Kohsuke Kawaguchi,
* Daniel Dyer, Tom Huybrechts, Yahoo!, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
......@@ -34,6 +34,7 @@ import hudson.cli.declarative.CLIMethod;
import hudson.cli.declarative.CLIResolver;
import hudson.model.listeners.ItemListener;
import hudson.model.listeners.SaveableListener;
import hudson.search.SearchIndexBuilder;
import hudson.security.AccessControlled;
import hudson.security.Permission;
import hudson.security.ACL;
......@@ -91,6 +92,8 @@ public abstract class AbstractItem extends Actionable implements Item, HttpDelet
protected volatile String description;
private transient ItemGroup parent;
protected String displayName;
protected AbstractItem(ItemGroup parent, String name) {
this.parent = parent;
......@@ -116,9 +119,18 @@ public abstract class AbstractItem extends Actionable implements Item, HttpDelet
@Exported
public String getDisplayName() {
if(null!=displayName) {
return displayName;
}
// if the displayName is not set, then return the name as we use to do
return getName();
}
public void setDisplayName(String displayName) throws IOException {
this.displayName = displayName;
save();
}
public File getRootDir() {
return parent.getRootDirFor(this);
}
......@@ -548,6 +560,18 @@ public abstract class AbstractItem extends Actionable implements Item, HttpDelet
out.abort(); // don't leave anything behind
}
}
/* (non-Javadoc)
* @see hudson.model.AbstractModelObject#getSearchName()
*/
@Override
public String getSearchName() {
// the search name of abstract items should be the name and not display name.
// this will make suggestions use the names and not the display name
// so that the links will 302 directly to the thing the user was finding
return getName();
}
public String toString() {
return super.toString()+'['+getFullName()+']';
......
......@@ -941,6 +941,21 @@ public abstract class Job<JobT extends Job<JobT, RunT>, RunT extends Run<JobT, R
return null;
}
/**
* Takes the displayName request parameter and sets it into the the
* displayName member variable. If the displayName request parameter is a
* 0 length string, then set the displayName member variable to null.
* @param req
* @throws IOException
*/
void setDisplayNameFromRequest(StaplerRequest req) throws IOException {
String displayName = req.getParameter("displayName");
// if displayName is an empty string, just make it null so that we
// use the project name
displayName = Util.nullify(displayName);
setDisplayName(displayName);
}
//
//
// actions
......@@ -958,6 +973,8 @@ public abstract class Job<JobT extends Job<JobT, RunT>, RunT extends Run<JobT, R
keepDependencies = req.getParameter("keepDependencies") != null;
setDisplayNameFromRequest(req);
try {
JSONObject json = req.getSubmittedForm();
......
/*
* The MIT License
*
* Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Tom Huybrechts
* Copyright (c) 2004-2011, Sun Microsystems, Inc., Kohsuke Kawaguchi, Tom Huybrechts,
* Yahoo!, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
......@@ -66,6 +67,8 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import static jenkins.model.Jenkins.*;
......@@ -91,6 +94,9 @@ import static jenkins.model.Jenkins.*;
*/
@ExportedBean
public abstract class View extends AbstractModelObject implements AccessControlled, Describable<View>, ExtensionPoint, Saveable {
private final static Logger logger = Logger.getLogger(View.class.getName());
/**
* Container of this view. Set right after the construction
* and never change thereafter.
......@@ -672,14 +678,34 @@ public abstract class View extends AbstractModelObject implements AccessControll
}
}
void addDisplayNamesToSearchIndex(SearchIndexBuilder sib, Collection<TopLevelItem> items) {
for(TopLevelItem item : items) {
if(logger.isLoggable(Level.FINE)) {
logger.fine((String.format("Adding url=%s,displayName=%s",
item.getSearchUrl(), item.getDisplayName())));
}
sib.add(item.getSearchUrl(), item.getDisplayName());
}
}
@Override
public SearchIndexBuilder makeSearchIndex() {
return super.makeSearchIndex()
.add(new CollectionSearchIndex() {// for jobs in the view
SearchIndexBuilder sib = super.makeSearchIndex();
sib.add(new CollectionSearchIndex<TopLevelItem>() {// for jobs in the view
protected TopLevelItem get(String key) { return getItem(key); }
protected Collection<TopLevelItem> all() { return getItems(); }
protected Collection<TopLevelItem> all() { return getItems(); }
@Override
protected String getName(TopLevelItem o) {
// return the name instead of the display for suggestion searching
return o.getName();
}
});
// add the display name for each item in the search index
addDisplayNamesToSearchIndex(sib, getItems());
return sib;
}
/**
......
/*
* The MIT License
*
* Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi
* Copyright (c) 2004-2011, Sun Microsystems, Inc., Kohsuke Kawaguchi,
* Yahoo!, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
......@@ -25,6 +26,7 @@ package hudson.search;
import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
import hudson.Util;
import hudson.util.EditDistance;
import java.io.IOException;
import java.util.AbstractList;
......@@ -33,6 +35,9 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.ServletException;
import org.kohsuke.stapler.Ancestor;
import org.kohsuke.stapler.QueryParameter;
......@@ -49,12 +54,17 @@ import org.kohsuke.stapler.export.Flavor;
* @author Kohsuke Kawaguchi
*/
public class Search {
private final static Logger logger = Logger.getLogger(Search.class.getName());
public void doIndex(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
List<Ancestor> l = req.getAncestors();
for( int i=l.size()-1; i>=0; i-- ) {
Ancestor a = l.get(i);
if (a.getObject() instanceof SearchableModelObject) {
SearchableModelObject smo = (SearchableModelObject) a.getObject();
if(logger.isLoggable(Level.FINE)){
logger.fine(String.format("smo.displayName=%s, searchName=%s",smo.getDisplayName(), smo.getSearchName()));
}
SearchIndex index = smo.getSearchIndex();
String query = req.getParameter("q");
......@@ -171,12 +181,49 @@ public class Search {
}
/**
* Performs a search and returns the match, or null if no match was found.
* When there are mutiple suggested items, this method can narrow down the resultset
* to the SuggestedItem that has a url that contains the query. This is useful is one
* job has a display name that matches another job's project name.
* @param r A list of Suggested items. It is assumed that there is at least one
* SuggestedItem in r.
* @param query A query string
* @return Returns the SuggestedItem which has a search url that contains the query.
* If no SuggestedItems have a search url which contains the query, then the first
* SuggestedItem in the List is returned.
*/
static SuggestedItem findClosestSuggestedItem(List<SuggestedItem> r, String query) {
for(SuggestedItem curItem : r) {
if(logger.isLoggable(Level.FINE)) {
logger.fine(String.format("item's searchUrl:%s;query=%s", curItem.item.getSearchUrl(), query));
}
if(curItem.item.getSearchUrl().contains(Util.rawEncode(query))) {
return curItem;
}
}
// couldn't find an item with the query in the url so just
// return the first one
return r.get(0);
}
/**
* Performs a search and returns the match, or null if no match was found
* or more than one match was found
*/
public static SuggestedItem find(SearchIndex index, String query) {
List<SuggestedItem> r = find(Mode.FIND, index, query);
if(r.isEmpty()) return null;
else return r.get(0);
if(r.isEmpty()){
return null;
}
else if(1==r.size()){
return r.get(0);
}
else {
// we have more than one suggested item, so return the item who's url
// contains the query as this is probably the job's name
return findClosestSuggestedItem(r, query);
}
}
public static List<SuggestedItem> suggest(SearchIndex index, final String tokenList) {
......@@ -244,6 +291,18 @@ public class Search {
}
};
}
public String toString() {
StringBuilder s = new StringBuilder("TokenList{");
for(String token : tokens) {
s.append(token);
s.append(",");
}
s.append('}');
return s.toString();
}
}
private static List<SuggestedItem> find(Mode m, SearchIndex index, String tokenList) {
......@@ -256,14 +315,19 @@ public class Search {
List<SearchItem> items = new ArrayList<SearchItem>(); // items found in 1 step
if(logger.isLoggable(Level.FINE)) {
logger.fine("tokens="+tokens.toString());
}
// first token
int w=1; // width of token
for (String token : tokens.subSequence(0)) {
items.clear();
m.find(index,token,items);
for (SearchItem si : items)
for (SearchItem si : items) {
paths[w].add(new SuggestedItem(si));
logger.info("found search item:"+si.getSearchName());
}
w++;
}
......@@ -285,4 +349,5 @@ public class Search {
return paths[tokens.length()];
}
}
/*
* The MIT License
*
* Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi,
* Copyright (c) 2004-2011, Sun Microsystems, Inc., Kohsuke Kawaguchi,
* Erik Ramfelt, Koichi Fujikawa, Red Hat, Inc., Seiji Sogabe,
* Stephen Connolly, Tom Huybrechts, Yahoo! Inc., Alan Harder, CloudBees, Inc.
* Stephen Connolly, Tom Huybrechts, Yahoo! Inc., Alan Harder, CloudBees, Inc.,
* Yahoo!, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
......@@ -3396,6 +3397,85 @@ public class Jenkins extends AbstractCIBase implements ModifiableItemGroup<TopLe
return getPrimaryView();
}
/**
* This method checks all existing jobs to see if displayName is
* unique. It does not check the displayName against the displayName of the
* job that the user is configuring though to prevent a validation warning
* if the user sets the displayName to what it currently is.
* @param displayName
* @param currentJobName
* @return
*/
boolean isDisplayNameUnique(String displayName, String currentJobName) {
Collection<TopLevelItem> itemCollection = items.values();
// if there are a lot of projects, we'll have to store their
// display names in a HashSet or something for a quick check
for(TopLevelItem item : itemCollection) {
if(item.getName().equals(currentJobName)) {
// we won't compare the candidate displayName against the current
// item. This is to prevent an validation warning if the user
// sets the displayName to what the existing display name is
continue;
}
else if(displayName.equals(item.getDisplayName())) {
return false;
}
}
return true;
}
/**
* True if there is no item in Jenkins that has this name
* @param name The name to test
* @param currentJobName The name of the job that the user is configuring
* @return
*/
boolean isNameUnique(String name, String currentJobName) {
Item item = getItem(name);
if(null==item) {
// the candidate name didn't return any items so the name is unique
return true;
}
else if(item.getName().equals(currentJobName)) {
// the candidate name returned an item, but the item is the item
// that the user is configuring so this is ok
return true;
}
else {
// the candidate name returned an item, so it is not unique
return false;
}
}
/**
* Checks to see if the candidate displayName collides with any
* existing display names or project names
* @param displayName The display name to test
* @param jobName The name of the job the user is configuring
* @return
*/
public FormValidation doCheckDisplayName(@QueryParameter String displayName,
@QueryParameter String jobName) {
displayName = displayName.trim();
if(LOGGER.isLoggable(Level.FINE)) {
LOGGER.log(Level.FINE, "Current job name is " + jobName);
}
if(!isNameUnique(displayName, jobName)) {
return FormValidation.warning(Messages.Jenkins_CheckDisplayName_NameNotUniqueWarning(displayName));
}
else if(!isDisplayNameUnique(displayName, jobName)){
return FormValidation.warning(Messages.Jenkins_CheckDisplayName_DisplayNameNotUniqueWarning(displayName));
}
else {
return FormValidation.ok();
}
}
public static class MasterComputer extends Computer {
protected MasterComputer() {
super(Jenkins.getInstance());
......
<!--
The MIT License
Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi, Stephen Connolly
Copyright (c) 2004-2011, Sun Microsystems, Inc., Kohsuke Kawaguchi,
Stephen Connolly, Yahoo!, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
......@@ -37,6 +38,10 @@ THE SOFTWARE.
<p:config-blockWhenUpstreamBuilding />
<p:config-blockWhenDownstreamBuilding />
<p:config-customWorkspace />
<f:entry title="${%Display Name}" help="/help/project-config/displayName.html">
<f:textbox name="displayName" value="${it.displayName}"
checkUrl="'${rootURL}/checkDisplayName?displayName='+escape(this.value)+'&amp;jobName=${it.name}'"/>
</f:entry>
</f:advanced>
</f:section>
......
......@@ -27,7 +27,10 @@ THE SOFTWARE.
<l:layout title="${it.name}">
<st:include page="sidepanel.jelly" />
<l:main-panel>
<h1>${%Project} ${it.name}</h1>
<h1>${%Project} ${it.displayName}</h1>
<j:if test="${it.name!=it.displayName}">
${%Project name}: ${it.name}
</j:if>
<t:editableDescription permission="${it.CONFIGURE}"/>
<j:choose>
......
<!--
The MIT License
Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi,
Copyright (c) 2004-2011, Sun Microsystems, Inc., Kohsuke Kawaguchi,
Eric Lefevre-Ardant, id:cactusman, Yahoo! Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
......@@ -64,6 +64,11 @@ THE SOFTWARE.
<p:config-blockWhenUpstreamBuilding />
<p:config-blockWhenDownstreamBuilding />
<st:include page="configure-advanced.jelly" optional="true" />
<f:entry title="${%Display Name}" help="/help/project-config/displayName.html">
<f:textbox name="displayName" value="${it.displayName}"
checkUrl="'${rootURL}/checkDisplayName?displayName='+escape(this.value)+'&amp;jobName=${it.name}'"/>
</f:entry>
</f:advanced>
</f:section>
......
......@@ -23,3 +23,4 @@
default.value=(Default)
Advanced\ Project\ Options\ configure-common=Advanced Project Options
title.concurrentbuilds=Execute concurrent builds if necessary
Display Name=Display Name
......@@ -28,6 +28,9 @@ THE SOFTWARE.
<st:include page="sidepanel.jelly" />
<l:main-panel>
<h1>${it.pronoun} ${it.displayName}</h1>
<j:if test="${(it.name!=it.displayName)&amp;&amp;(it.class.name!='hudson.matrix.MatrixConfiguration')}">
${%Project name}: ${it.name}
</j:if>
<t:editableDescription permission="${it.CONFIGURE}"/>
<j:if test="${it.parent == app}">
......
......@@ -321,3 +321,7 @@ CLI.cancel-quiet-down.shortDescription=Cancel the effect of the "quiet-down" com
CLI.reload-configuration.shortDescription=Discard all the loaded data in memory and reload everything from file system. Useful when you modified config files directly on disk.
BuildAuthorizationToken.InvalidTokenProvided=Invalid token provided.
Jenkins.CheckDisplayName.NameNotUniqueWarning=The display name, "{0}", is used as a name by a job and could cause confusing search results.
Jenkins.CheckDisplayName.DisplayNameNotUniqueWarning=The display name, "{0}", is already in use by another job and could cause confusion and delay.
rterter
\ No newline at end of file
......@@ -25,6 +25,6 @@ THE SOFTWARE.
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form" xmlns:i="jelly:fmt">
<td style="${indenter.getCss(job)}">
<a href="${jobBaseUrl}${job.shortUrl}"> ${job.displayName}</a>
<a href="${jobBaseUrl}${job.shortUrl}" tooltip="${job.name}"> ${job.displayName}</a>
</td>
</j:jelly>
\ No newline at end of file
......@@ -2,7 +2,8 @@
#
# Copyright (c) 2004-2011, Sun Microsystems, Inc., Kohsuke Kawaguchi,
# Eric Lefevre-Ardant, Erik Ramfelt, Seiji Sogabe, id:cactusman,
# Manufacture Francaise des Pneumatiques Michelin, Romain Seguy
# Manufacture Francaise des Pneumatiques Michelin, Romain Seguy,
# Yahoo!, Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
......
......@@ -32,5 +32,5 @@ THE SOFTWARE.
</st:documentation>
<img src="${imagesURL}/16x16/${job.buildStatusUrl}" alt="${job.iconColor.description}" height="16" width="16"/>
<a href="${h.getRelativeLinkTo(job)}">${job.displayName}</a>
<a href="${h.getRelativeLinkTo(job)}" tooltip="${job.name}">${job.displayName}</a>
</j:jelly>
......@@ -207,7 +207,7 @@ THE SOFTWARE.
<j:if test="${anc.prev!=null}">
<j:whitespace> &#187; </j:whitespace>
</j:if>
<a href="${anc.url}/">
<a href="${anc.url}/" tooltip="${anc.object.name}">
${anc.object.displayName}
</a>
</j:if>
......
......@@ -23,10 +23,14 @@
*/
package hudson.search;
import hudson.Util;
import junit.framework.TestCase;
import java.util.ArrayList;
import java.util.List;
import org.junit.Assert;
/**
* @author Kohsuke Kawaguchi
*/
......@@ -49,4 +53,49 @@ public class SearchTest extends TestCase {
assertEquals("/abc-def-ghi",l.get(0).getUrl());
assertEquals("/abc/def-ghi",l.get(1).getUrl());
}
/**
* This test verifies that if 2 search results are found with the same
* search name, that the one with the search query in the url is returned
*/
public void testFindClosestSuggestedItem() {
final String query = "foobar 123";
final String searchName = "sameDisplayName";
SearchItem searchItemHit = new SearchItem() {
public SearchIndex getSearchIndex() {
return null;
}
public String getSearchName() {
return searchName;
}
public String getSearchUrl() {
return "/job/"+Util.rawEncode(query) + "/";
}
};
SearchItem searchItemNoHit = new SearchItem() {
public SearchIndex getSearchIndex() {
return null;
}
public String getSearchName() {
return searchName;
}
public String getSearchUrl() {
return "/job/someotherJob/";
}
};
SuggestedItem suggestedHit = new SuggestedItem(searchItemHit);
SuggestedItem suggestedNoHit = new SuggestedItem(searchItemNoHit);
ArrayList<SuggestedItem> list = new ArrayList<SuggestedItem>();
list.add(suggestedNoHit);
list.add(suggestedHit); // make sure the hit is the second item
SuggestedItem found = Search.findClosestSuggestedItem(list, query);
Assert.assertEquals(searchItemHit, found.item);
SuggestedItem found2 = Search.findClosestSuggestedItem(list, "abcd");
Assert.assertEquals(searchItemNoHit, found2.item);
}
}
/*
* The MIT License
*
* Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Erik Ramfelt,
* Copyright (c) 2004-2011, Sun Microsystems, Inc., Kohsuke Kawaguchi, Erik Ramfelt,
* Yahoo! Inc., Tom Huybrechts, Olivier Lamy
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
......@@ -354,11 +354,13 @@ public abstract class HudsonTestCase extends TestCase implements RootAction {
}
clients.clear();
for (Channel c : channels)
c.close();
for (Channel c : channels)
c.join();
channels.clear();
synchronized(channels) {
for (Channel c : channels)
c.close();
for (Channel c : channels)
c.join();
channels.clear();
}
} finally {
server.stop();
......@@ -497,11 +499,16 @@ public abstract class HudsonTestCase extends TestCase implements RootAction {
public void onOnline(Computer c, TaskListener listener) throws IOException, InterruptedException {
VirtualChannel ch = c.getChannel();
if (ch instanceof Channel)
TestEnvironment.get().testCase.channels.add((Channel)ch);
TestEnvironment.get().testCase.addChannel((Channel)ch);
}
}
private void addChannel(Channel ch) {
synchronized (channels) {
channels.add(ch);
}
}
// /**
// * Sets guest credentials to access java.net Subversion repo.
// */
......
......@@ -23,9 +23,19 @@
*/
package hudson.search;
import jenkins.model.Jenkins;
import hudson.model.FreeStyleProject;
import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException;
import com.gargoylesoftware.htmlunit.Page;
import com.gargoylesoftware.htmlunit.AlertHandler;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import net.sf.json.JSONSerializer;
import net.sf.json.util.JSONBuilder;
import org.junit.Assert;
import org.jvnet.hudson.test.HudsonTestCase;
import org.jvnet.hudson.test.Bug;
......@@ -63,4 +73,134 @@ public class SearchTest extends HudsonTestCase {
assertEquals(404,e.getResponse().getStatusCode());
}
}
public void testSearchByProjectName() throws Exception {
final String projectName = "testSearchByProjectName";
createFreeStyleProject(projectName);
Page result = search(projectName);
Assert.assertNotNull(result);
assertGoodStatus(result);
// make sure we've fetched the testSearchByDisplayName project page
String contents = result.getWebResponse().getContentAsString();
Assert.assertTrue(contents.contains(String.format("<title>%s [Jenkins]</title>", projectName)));
}
public void testSearchByDisplayName() throws Exception {
final String displayName = "displayName9999999";
FreeStyleProject project = createFreeStyleProject("testSearchByDisplayName");
project.setDisplayName(displayName);
Page result = search(displayName);
Assert.assertNotNull(result);
assertGoodStatus(result);
// make sure we've fetched the testSearchByDisplayName project page
String contents = result.getWebResponse().getContentAsString();
Assert.assertTrue(contents.contains(String.format("<title>%s [Jenkins]</title>", displayName)));
}
public void testSearch2ProjectsWithSameDisplayName() throws Exception {
// create 2 freestyle projects with the same display name
final String projectName1 = "projectName1";
final String projectName2 = "projectName2";
final String projectName3 = "projectName3";
final String displayName = "displayNameFoo";
final String otherDisplayName = "otherDisplayName";
FreeStyleProject project1 = createFreeStyleProject(projectName1);
project1.setDisplayName(displayName);
FreeStyleProject project2 = createFreeStyleProject(projectName2);
project2.setDisplayName(displayName);
FreeStyleProject project3 = createFreeStyleProject(projectName3);
project3.setDisplayName(otherDisplayName);
// make sure that on search we get back one of the projects, it doesn't
// matter which one as long as the one that is returned has displayName
// as the display name
Page result = search(displayName);
Assert.assertNotNull(result);
assertGoodStatus(result);
// make sure we've fetched the testSearchByDisplayName project page
String contents = result.getWebResponse().getContentAsString();
Assert.assertTrue(contents.contains(String.format("<title>%s [Jenkins]</title>", displayName)));
Assert.assertFalse(contents.contains(otherDisplayName));
}
public void testProjectNamePrecedesDisplayName() throws Exception {
final String project1Name = "foo";
final String project1DisplayName = "project1DisplayName";
final String project2Name = "project2Name";
final String project2DisplayName = project1Name;
final String project3Name = "project3Name";
final String project3DisplayName = "project3DisplayName";
// create 1 freestyle project with the name foo
FreeStyleProject project1 = createFreeStyleProject(project1Name);
project1.setDisplayName(project1DisplayName);
// create another with the display name foo
FreeStyleProject project2 = createFreeStyleProject(project2Name);
project2.setDisplayName(project2DisplayName);
// create a third project and make sure it's not picked up by search
FreeStyleProject project3 = createFreeStyleProject(project3Name);
project3.setDisplayName(project3DisplayName);
// search for foo
Page result = search(project1Name);
Assert.assertNotNull(result);
assertGoodStatus(result);
// make sure we get the project with the name foo
String contents = result.getWebResponse().getContentAsString();
Assert.assertTrue(contents.contains(String.format("<title>%s [Jenkins]</title>", project1DisplayName)));
// make sure projects 2 and 3 were not picked up
Assert.assertFalse(contents.contains(project2Name));
Assert.assertFalse(contents.contains(project3Name));
Assert.assertFalse(contents.contains(project3DisplayName));
}
public void testGetSuggestionsHasBothNamesAndDisplayNames() throws Exception {
final String projectName = "project name";
final String displayName = "display name";
FreeStyleProject project1 = createFreeStyleProject(projectName);
project1.setDisplayName(displayName);
WebClient wc = new WebClient();
Page result = wc.goTo("search/suggest?query=name", "application/javascript");
Assert.assertNotNull(result);
assertGoodStatus(result);
String content = result.getWebResponse().getContentAsString();
System.out.println(content);
JSONObject jsonContent = (JSONObject)JSONSerializer.toJSON(content);
Assert.assertNotNull(jsonContent);
JSONArray jsonArray = jsonContent.getJSONArray("suggestions");
Assert.assertNotNull(jsonArray);
Assert.assertEquals(2, jsonArray.size());
boolean foundProjectName = false;
boolean foundDispayName = false;
for(Object suggestion : jsonArray) {
JSONObject jsonSuggestion = (JSONObject)suggestion;
String name = (String)jsonSuggestion.get("name");
if(projectName.equals(name)) {
foundProjectName = true;
}
else if(displayName.equals(name)) {
foundDispayName = true;
}
}
Assert.assertTrue(foundProjectName);
Assert.assertTrue(foundDispayName);
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册