PLT-2525: Render attachment fields similar to Slack

* Render attachment fields similar to Slack

* Add /loadtest json url command

This allows us to easily create test posts with 
more props like Slack attachments
Этот коммит содержится в:
Andy Lo-A-Foe
2016-04-06 14:23:26 +02:00
коммит произвёл Christopher Speller
родитель 1954c44993
Коммит 1a1fd39cf8
4 изменённых файлов: 209 добавлений и 62 удалений

Просмотреть файл

@@ -49,6 +49,11 @@ var usage = `Mattermost load testing commands to help configure the system
Example: Example:
/loadtest http://www.example.com/sample_file.md /loadtest http://www.example.com/sample_file.md
Json - Add a post using the JSON file as payload to the current channel.
/loadtest json url
Example
/loadtest json http://www.example.com/sample_body.json
` `
@@ -105,7 +110,9 @@ func (me *LoadTestProvider) DoCommand(c *Context, channelId string, message stri
if strings.HasPrefix(message, "url") { if strings.HasPrefix(message, "url") {
return me.UrlCommand(c, channelId, message) return me.UrlCommand(c, channelId, message)
} }
if strings.HasPrefix(message, "json") {
return me.JsonCommand(c, channelId, message)
}
return me.HelpCommand(c, channelId, message) return me.HelpCommand(c, channelId, message)
} }
@@ -330,6 +337,42 @@ func (me *LoadTestProvider) UrlCommand(c *Context, channelId string, message str
return &model.CommandResponse{Text: "Loading data...", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL} return &model.CommandResponse{Text: "Loading data...", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
} }
func (me *LoadTestProvider) JsonCommand(c *Context, channelId string, message string) *model.CommandResponse {
url := strings.TrimSpace(strings.TrimPrefix(message, "json"))
if len(url) == 0 {
return &model.CommandResponse{Text: "Command must contain a url", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
}
// provide a shortcut to easily access tests stored in doc/developer/tests
if !strings.HasPrefix(url, "http") {
url = "https://raw.githubusercontent.com/mattermost/platform/master/tests/" + url
if path.Ext(url) == "" {
url += ".json"
}
}
var contents io.ReadCloser
if r, err := http.Get(url); err != nil {
return &model.CommandResponse{Text: "Unable to get file", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
} else if r.StatusCode > 400 {
return &model.CommandResponse{Text: "Unable to get file", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
} else {
contents = r.Body
}
post := model.PostFromJson(contents)
post.ChannelId = channelId
if post.Message == "" {
post.Message = message
}
if _, err := CreatePost(c, post, false); err != nil {
return &model.CommandResponse{Text: "Unable to create post", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
}
return &model.CommandResponse{Text: "Loading data...", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
}
func parseRange(command string, cmd string) (utils.Range, bool) { func parseRange(command string, cmd string) (utils.Range, bool) {
tokens := strings.Fields(strings.TrimPrefix(command, cmd)) tokens := strings.Fields(strings.TrimPrefix(command, cmd))
var begin int var begin int

Просмотреть файл

@@ -221,3 +221,44 @@ func TestLoadTestUrlCommands(t *testing.T) {
time.Sleep(2 * time.Second) time.Sleep(2 * time.Second)
} }
func TestLoadTestJsonCommands(t *testing.T) {
Setup()
// enable testing to use /loadtest but don't save it since we don't want to overwrite config.json
enableTesting := utils.Cfg.ServiceSettings.EnableTesting
defer func() {
utils.Cfg.ServiceSettings.EnableTesting = enableTesting
}()
utils.Cfg.ServiceSettings.EnableTesting = true
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey+test@test.com", Nickname: "Corey Hulen", Password: "pwd"}
user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user1.Id))
Client.LoginByEmail(team.Name, user1.Email, "pwd")
channel := &model.Channel{DisplayName: "00", Name: "00" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
channel = Client.Must(Client.CreateChannel(channel)).Data.(*model.Channel)
command := "/loadtest json "
if r := Client.Must(Client.Command(channel.Id, command, false)).Data.(*model.CommandResponse); r.Text != "Command must contain a url" {
t.Fatal("/loadtest url with no url should've failed")
}
command = "/loadtest json http://missingfiletonwhere/path/asdf/qwerty"
if r := Client.Must(Client.Command(channel.Id, command, false)).Data.(*model.CommandResponse); r.Text != "Unable to get file" {
t.Log(r.Text)
t.Fatal("/loadtest url with invalid url should've failed")
}
command = "/loadtest url https://secure.beldienst.nl/test.json" // Chicken-egg so will replace with mattermost/platform URL soon
if r := Client.Must(Client.Command(channel.Id, command, false)).Data.(*model.CommandResponse); r.Text != "Loading data..." {
t.Fatal("/loadtest url for README.md should've executed")
}
time.Sleep(2 * time.Second)
}

56
tests/test-slack-attachments.json Обычный файл
Просмотреть файл

@@ -0,0 +1,56 @@
{
"message": "Hello world",
"props": {
"attachments": [
{
"color": "#7CD197",
"fields": [
{
"short": false,
"title": "Area",
"value": "Testing with a very long piece of text that will take up the whole width of the table. This is one more sentence to really make it a long field."
},
{
"short": true,
"title": "Iteration",
"value": "Testing"
},
{
"short": true,
"title": "State",
"value": "New"
},
{
"short": false,
"title": "Reason",
"value": "New defect reported"
},
{
"short": false,
"title": "Random field",
"value": "This is a field which is not marked as short so it should be rendered on a separate row"
},
{
"short": true,
"title": "Short 1",
"value": "Short field"
},
{
"short": true,
"title": "Short 2",
"value": "Another one"
}
],
"mrkdwn_in": [
"pretext"
],
"pretext": "Some text here",
"text": "This is the text of the attachment. It should appear just above the image",
"thumb_url": "https://slack.global.ssl.fastly.net/7bf4/img/services/jenkins-ci_128.png",
"title": "A slack attachment",
"title_link": "https://www.google.com"
}
]
},
"type": "slack_attachment"
}

Просмотреть файл

@@ -87,75 +87,82 @@ class PostAttachment extends React.Component {
return ''; return '';
} }
const compactTable = fields.filter((field) => field.short).length > 0; let fieldTables = [];
let tHead;
let tBody;
if (compactTable) { let headerCols = [];
let headerCols = []; let bodyCols = [];
let bodyCols = []; let rowPos = 0;
let lastWasLong = false;
let nrTables = 0;
fields.forEach((field, i) => { fields.forEach((field, i) => {
headerCols.push( if (rowPos === 2 || !(field.short === true) || lastWasLong) {
<th fieldTables.push(
className='attachment___field-caption' <table
key={'attachment__field-caption-' + i} className='attachment___fields'
key={'attachment__table__' + nrTables}
> >
{field.title} <thead>
</th> <tr>
{headerCols}
</tr>
</thead>
<tbody>
<tr>
{bodyCols}
</tr>
</tbody>
</table>
); );
bodyCols.push( headerCols = [];
<td bodyCols = [];
className='attachment___field' rowPos = 0;
key={'attachment__field-' + i} nrTables += 1;
dangerouslySetInnerHTML={{__html: TextFormatting.formatText(field.value || '')}} lastWasLong = false;
}
headerCols.push(
<th
className='attachment___field-caption'
key={'attachment__field-caption-' + i + '__' + nrTables}
width='50%'
>
{field.title}
</th>
);
bodyCols.push(
<td
className='attachment___field'
key={'attachment__field-' + i + '__' + nrTables}
dangerouslySetInnerHTML={{__html: TextFormatting.formatText(field.value || '')}}
>
</td>
);
rowPos += 1;
lastWasLong = !(field.short === true);
});
if (headerCols.length > 0) { // Flush last fields
fieldTables.push(
<table
className='attachment___fields'
key={'attachment__table__' + nrTables}
> >
</td> <thead>
<tr>
{headerCols}
</tr>
</thead>
<tbody>
<tr>
{bodyCols}
</tr>
</tbody>
</table>
); );
});
tHead = (
<tr>
{headerCols}
</tr>
);
tBody = (
<tr>
{bodyCols}
</tr>
);
} else {
tBody = [];
fields.forEach((field, i) => {
tBody.push(
<tr key={'attachment__field-' + i}>
<td
className='attachment___field-caption'
>
{field.title}
</td>
<td
className='attachment___field'
dangerouslySetInnerHTML={{__html: TextFormatting.formatText(field.value || '')}}
>
</td>
</tr>
);
});
} }
return ( return (
<table <div>
className='attachment___fields' {fieldTables}
> </div>
<thead>
{tHead}
</thead>
<tbody>
{tBody}
</tbody>
</table>
); );
} }