Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions tcms/core/widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@ class Media:
css = {"all": ["simplemde/dist/simplemde.min.css"]}


class SimpleMDENotes(SimpleMDE):
"""
SimpleMDE widget for notes field with unique file upload ID
"""

file_upload_id = "simplemde-notes-file-upload"


class DurationWidget(forms.Widget):
template_name = "widgets/duration.html"

Expand Down
10 changes: 8 additions & 2 deletions tcms/static/js/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,18 @@ $(() => {
$('[data-toggle="tooltip"]').tooltip()
}

window.markdownEditors = {}

$('.js-simplemde-textarea').each(function () {
const fileUploadId = $(this).data('file-upload-id')
const uploadField = $(`#${fileUploadId}`)

// this value is only used in testcases/js/mutable.js
window.markdownEditor = initSimpleMDE(this, uploadField)
// Use textarea id/name for unique autosave ID to prevent content sharing
const textareaId = $(this).attr('id') || $(this).attr('name') || 'default'
const autoSaveId = window.location.toString() + '#' + textareaId

const editor = initSimpleMDE(this, uploadField, autoSaveId)
window.markdownEditors[textareaId] = editor
})

$('#logout_link').click(function () {
Expand Down
8 changes: 8 additions & 0 deletions tcms/static/js/simplemde_security_override.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ export function initSimpleMDE (textArea, fileUploadElement, autoSaveId = window.
return null
}

// Capture the server-rendered value before SimpleMDE replaces it
const serverValue = textArea.value

const simpleMDE = new SimpleMDE({
element: textArea,
autoDownloadFontAwesome: false,
Expand Down Expand Up @@ -72,6 +75,11 @@ export function initSimpleMDE (textArea, fileUploadElement, autoSaveId = window.
}
})

// Remove legacy shared autosave key (before per-textarea unique IDs)
// to prevent cross-field content leaking
const legacyKey = 'smde_' + window.location.toString()
try { localStorage.removeItem(legacyKey) } catch (e) { /* ignore */ }

fileUploadElement.change(function () {
const attachment = this.files[0]
const reader = new FileReader()
Expand Down
6 changes: 5 additions & 1 deletion tcms/testcases/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from django.forms import inlineformset_factory

from tcms.core.forms.fields import UserField
from tcms.core.widgets import DurationWidget, SimpleMDE
from tcms.core.widgets import DurationWidget, SimpleMDE, SimpleMDENotes
from tcms.management.models import Component, Priority, Product
from tcms.testcases.fields import MultipleEmailField
from tcms.testcases.models import (
Expand Down Expand Up @@ -42,6 +42,10 @@ class Meta:
widget=DurationWidget(),
required=False,
)
notes = forms.CharField(
widget=SimpleMDENotes(),
required=False,
)
text = forms.CharField(
widget=SimpleMDE(),
required=False,
Expand Down
5 changes: 4 additions & 1 deletion tcms/testcases/static/testcases/js/mutable.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ import { updateCategorySelectFromProduct } from '../../../../static/js/utils'

export function pageTestcasesMutableReadyHandler () {
$('#id_template').change(function () {
window.markdownEditor.codemirror.setValue($(this).val())
const editor = window.markdownEditors && window.markdownEditors['id_text']
if (editor) {
editor.codemirror.setValue($(this).val())
}
})

$('#add_id_template').click(function () {
Expand Down
43 changes: 39 additions & 4 deletions tcms/testcases/templates/testcases/get.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,54 @@
{% block page_id %}page-testcases-get{% endblock %}
{% block body_class %}cards-pf{% endblock %}


{% block contents %}
<div class="container-cards-pf">
<!-- Important: if you need to nest additional .row within a .row.row-cards-pf, do *not* use .row-cards-pf on the nested .row -->
<h1 class="col-md-12 kiwi-margin-top-0">
<span id="test_case_pk"
data-pk="{{ object.pk }}"
data-from-plan="{{ request.GET.from_plan|default:'' }}"
data-perm-remove-tag="{{ perms.testcases.delete_testcasetag }}"
data-perm-remove-component="{{ perms.testcases.delete_testcasecomponent }}"
data-perm-remove-plan="{{ perms.testcases.delete_testcaseplan }}"
data-perm-remove-bug="{{ perms.testcases.delete_bug }}"
>TC-{{ object.pk }}:</span> {{ object.summary }}
</h1>

<div id="tc-navigation-bar" class="col-md-12" style="display:none;">
<div class="pull-left">
<button id="btn-prev-case" class="btn btn-default btn-sm" disabled>
<span class="fa fa-arrow-left"></span> {% trans 'Previous' %}
</button>
<span id="tc-position" class="text-muted"></span>
<button id="btn-next-case" class="btn btn-default btn-sm" disabled>
{% trans 'Next' %} <span class="fa fa-arrow-right"></span>
</button>
</div>
<div class="pull-right">
<button id="btn-toggle-sidebar" class="btn btn-default btn-sm">
<span class="fa fa-list"></span> {% trans 'Case List' %}
</button>
</div>
<div class="clearfix"></div>
</div>

<div id="tc-sidebar-panel" class="col-md-3 pull-right" style="display:none;">
<div class="card-pf card-pf-accented">
<h2 class="card-pf-title">
<span class="fa fa-sitemap"></span>
<a id="sidebar-plan-link" href="#"></a>
<button class="btn btn-link btn-sm pull-right" id="btn-close-sidebar">
<span class="fa fa-times"></span>
</button>
</h2>
<div class="card-pf-body" id="tc-sidebar-body" style="max-height:70vh; overflow-y:auto;">
<div class="spinner spinner-lg"></div>
</div>
</div>
</div>

<div class="row row-cards-pf">
<div class="col-xs-12 col-sm-12 col-md-3">
<div class="card-pf card-pf-accented card-pf-aggregate-status">
Expand Down Expand Up @@ -129,10 +164,10 @@ <h2 class="card-pf-title kiwi-text-align-left">
{{ object.text|markdown2html }}
</div>

<p>
<strong>{% trans 'Notes' %}:</strong>
{{ object.notes }}
</p>
<div class="markdown-text">
{{ object.notes|markdown2html }}
</div>

</div>
</div>
</div>
Expand Down
7 changes: 5 additions & 2 deletions tcms/testcases/templates/testcases/mutable.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
<div class="container-fluid container-cards-pf">
<form class="form-horizontal" action="{% if object %}{% url 'testcases-edit' object.pk %}{% else %}{% url 'testcases-new' %}{% endif %}" method="post">
{% csrf_token %}
{% if request.GET.from_plan %}
<input type="hidden" name="from_plan" value="{{ request.GET.from_plan }}">
{% endif %}
<input type="hidden" name="author" value="{{ form.author.value }}">
<div class="form-group">
<label class="col-md-1 col-lg-1" for="id_summary">{% trans "Summary" %}</label>
Expand Down Expand Up @@ -169,7 +172,7 @@
<div class="form-group">
<label for="id_notes" class="col-md-1 col-lg-1">{% trans "Notes" %}</label>
<div class="col-md-12 col-lg-12 {% if form.notes.errors %}has-error{% endif %}">
<textarea id="id_notes" rows="4" name="notes" class="form-control">{{ form.notes.value|default:'' }}</textarea>
{{ form.notes }}
{{ form.notes.errors }}
</div>
</div>
Expand Down Expand Up @@ -260,7 +263,7 @@

<div class="form-group">
<div class="col-md-12 col-lg-12">
<a href="{% if object %}{% url 'testcases-get' object.pk %}{% else %}{% url 'core-views-index' %}{% endif %}"
<a href="{% if object %}{% url 'testcases-get' object.pk %}{% if request.GET.from_plan %}?from_plan={{ request.GET.from_plan }}{% endif %}{% else %}{% url 'core-views-index' %}{% endif %}"
class="btn btn-danger btn-lg">{% trans "Cancel" %}</a>
<button type="submit" class="btn btn-primary btn-lg">{% trans "Save" %}</button>
</div>
Expand Down
6 changes: 3 additions & 3 deletions tcms/testplans/static/testplans/js/get.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ function drawTestCases (testCases, testPlanId, permissions) {

if (testCases.length > 0) {
testCases.forEach(function (element) {
container.append(getTestCaseRowContent(testCaseRowDocumentFragment.cloneNode(true), element, permissions))
container.append(getTestCaseRowContent(testCaseRowDocumentFragment.cloneNode(true), element, permissions, testPlanId))
})
attachEvents(testPlanId, permissions)
} else {
Expand All @@ -153,7 +153,7 @@ function drawTestCases (testCases, testPlanId, permissions) {

function redrawSingleRow (testCaseId, testPlanId, permissions) {
const testCaseRowDocumentFragment = $('#test_case_row')[0].content
const newRow = getTestCaseRowContent(testCaseRowDocumentFragment.cloneNode(true), allTestCases[testCaseId], permissions)
const newRow = getTestCaseRowContent(testCaseRowDocumentFragment.cloneNode(true), allTestCases[testCaseId], permissions, testPlanId)

// remove from expanded list b/c the comment section may have changed
expandedTestCaseIds.splice(expandedTestCaseIds.indexOf(testCaseId), 1)
Expand Down Expand Up @@ -224,7 +224,7 @@ function getTestCaseRowContent (rowContent, testCase, permissions) {
function getTestCaseExpandArea (row, testCase, permissions) {
markdown2HTML(testCase.text, row.find('.js-test-case-expand-text'))
if (testCase.notes.trim().length > 0) {
row.find('.js-test-case-expand-notes').html(testCase.notes)
markdown2HTML(testCase.notes, row.find('.js-test-case-expand-notes'))
}

// draw the attachments
Expand Down
5 changes: 5 additions & 0 deletions tcms/testruns/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from django.utils.translation import gettext_lazy as _

from tcms.core.forms.fields import UserField
from tcms.core.widgets import SimpleMDENotes
from tcms.management.models import Build, Product, Version
from tcms.rpc.api.forms import DateTimeField
from tcms.testcases.models import TestCase
Expand All @@ -23,6 +24,10 @@ class Meta:
stop_date = DateTimeField(required=False)
planned_start = DateTimeField(required=False)
planned_stop = DateTimeField(required=False)
notes = forms.CharField(
widget=SimpleMDENotes(),
required=False,
)

case = forms.ModelMultipleChoiceField(
queryset=TestCase.objects.none(),
Expand Down
2 changes: 1 addition & 1 deletion tcms/testruns/static/testruns/js/get.js
Original file line number Diff line number Diff line change
Expand Up @@ -475,7 +475,7 @@ function getExpandArea (testExecution) {
}], (data) => {
data.forEach((entry) => {
markdown2HTML(entry.text, container.find('.test-execution-text')[0])
container.find('.test-execution-notes').append(entry.notes)
markdown2HTML(entry.notes, container.find('.test-execution-notes'))
})
})

Expand Down
7 changes: 6 additions & 1 deletion tcms/testruns/templates/testruns/mutable.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
{% load i18n %}
{% load static %}

{% block head %}
{{ form.media}}
{% endblock %}

{% block title %}
{% if object %}
{% trans "Edit TestRun" %}
Expand Down Expand Up @@ -201,7 +205,8 @@
<div class="form-group">
<label class="col-lg-12" for="id_notes">{% trans "Notes" %}</label>
<div class="col-lg-12">
<textarea class="form-control" id="id_notes" name="notes">{{ form.notes.value }}</textarea>
{{ form.notes }}
{{ form.notes.errors }}
</div>
</div>

Expand Down