Repository: oppia/oppia

Issue:
UI issues in the Topics and Skills Dashboard
### **Notes on what to improve in the Topics and Skills Dashboard:**

 - [x] **Long string overflows vertically**
![Screen Shot 2020-06-11 at 5 30 22 PM](https://user-images.githubusercontent.com/22177674/84455954-986f2b80-ac13-11ea-8075-577b55ea2334.png)

 - [ ] **Top right corner of "Topics" should have a 0 px corner radius**
- The top border above "Topics" also should be thinner to match that of Skills'
![Screen Shot 2020-06-11 at 5 51 29 PM](https://user-images.githubusercontent.com/22177674/84455962-a0c76680-ac13-11ea-9636-67b4b2ef8775.png)

 - [x] **Topic statuses should all be left aligned, and 3 dot buttons should all be right aligned**
![Screen Shot 2020-06-11 at 5 52 40 PM](https://user-images.githubusercontent.com/22177674/84455981-ae7cec00-ac13-11ea-857c-431b63993116.png) ![Screen Shot 2020-06-11 at 5 52 48 PM](https://user-images.githubusercontent.com/22177674/84455978-ade45580-ac13-11ea-8908-0943250a71ac.png)


 - [x] **Filter menu issues**
- Remove the hint text "Topic name and description" because it does not fit in the textbox
- Create a new line when a keyword is too wide to fit into the keyword box
- For keywords, let's actually remove the "search history." Keeping a history of these searches can create a long list and may pose a problem when unintentionally auto-completing a word. 
![Screen Shot 2020-06-11 at 6 26 59 PM](https://user-images.githubusercontent.com/22177674/84456003-bdfc3500-ac13-11ea-9551-d7c39ad7f1d7.png)
UI issues in the Topics and Skills Dashboard
### **Notes on what to improve in the Topics and Skills Dashboard:**

 - [x] **Long string overflows vertically**
![Screen Shot 2020-06-11 at 5 30 22 PM](https://user-images.githubusercontent.com/22177674/84455954-986f2b80-ac13-11ea-8075-577b55ea2334.png)

 - [ ] **Top right corner of "Topics" should have a 0 px corner radius**
- The top border above "Topics" also should be thinner to match that of Skills'
![Screen Shot 2020-06-11 at 5 51 29 PM](https://user-images.githubusercontent.com/22177674/84455962-a0c76680-ac13-11ea-9636-67b4b2ef8775.png)

 - [x] **Topic statuses should all be left aligned, and 3 dot buttons should all be right aligned**
![Screen Shot 2020-06-11 at 5 52 40 PM](https://user-images.githubusercontent.com/22177674/84455981-ae7cec00-ac13-11ea-857c-431b63993116.png) ![Screen Shot 2020-06-11 at 5 52 48 PM](https://user-images.githubusercontent.com/22177674/84455978-ade45580-ac13-11ea-8908-0943250a71ac.png)


 - [x] **Filter menu issues**
- Remove the hint text "Topic name and description" because it does not fit in the textbox
- Create a new line when a keyword is too wide to fit into the keyword box
- For keywords, let's actually remove the "search history." Keeping a history of these searches can create a long list and may pose a problem when unintentionally auto-completing a word. 
![Screen Shot 2020-06-11 at 6 26 59 PM](https://user-images.githubusercontent.com/22177674/84456003-bdfc3500-ac13-11ea-9551-d7c39ad7f1d7.png)


Discussion:
Adding one more thing:
- It's quite irritating that I can't click on the topic name to edit it. I would like to do that. It took me quite a while to find the edit option, but that's such a common operation that I think it should be easier to do. (@mschanteltc any thoughts on this?)

Btw, one other reason to have the topic editable-on-click is that, when you make the screen small and click the dropdown, the Delete menu option is the one that shows up under the cursor. If there was no confirmation modal I would have deleted the topic and lost a lot of work.

(In the screenshot below, the Delete option is just over the triple dots.)

![Screenshot from 2020-06-09 01-45-45](https://user-images.githubusercontent.com/10575562/84126600-fb35ac80-a9f2-11ea-8bda-6a4bb231fb97.png)


Adding one more thing:
- It's quite irritating that I can't click on the topic name to edit it. I would like to do that. It took me quite a while to find the edit option, but that's such a common operation that I think it should be easier to do. (@mschanteltc any thoughts on this?)

Btw, one other reason to have the topic editable-on-click is that, when you make the screen small and click the dropdown, the Delete menu option is the one that shows up under the cursor. If there was no confirmation modal I would have deleted the topic and lost a lot of work.

(In the screenshot below, the Delete option is just over the triple dots.)

![Screenshot from 2020-06-09 01-45-45](https://user-images.githubusercontent.com/10575562/84126600-fb35ac80-a9f2-11ea-8bda-6a4bb231fb97.png)



Resolving patch:
diff --git a/core/controllers/topics_and_skills_dashboard.py b/core/controllers/topics_and_skills_dashboard.py
--- a/core/controllers/topics_and_skills_dashboard.py
+++ b/core/controllers/topics_and_skills_dashboard.py
@@ -171,6 +171,10 @@ def post(self):
         name = self.payload.get('name')
         abbreviated_name = self.payload.get('abbreviated_name')
         description = self.payload.get('description')
+        thumbnail_filename = self.payload.get('filename')
+        thumbnail_bg_color = self.payload.get('thumbnailBgColor')
+        raw_image = self.request.get('image')
+
         try:
             topic_domain.Topic.require_valid_name(name)
         except:
@@ -181,6 +185,34 @@ def post(self):
             new_topic_id, name, abbreviated_name, description)
         topic_services.save_new_topic(self.user_id, topic)
 
+        try:
+            file_format = image_validation_services.validate_image_and_filename(
+                raw_image, thumbnail_filename)
+        except utils.ValidationError as e:
+            raise self.InvalidInputException(e)
+
+        entity_id = new_topic_id
+        filename_prefix = 'thumbnail'
+
+        image_is_compressible = (
+            file_format in feconf.COMPRESSIBLE_IMAGE_FORMATS)
+        fs_services.save_original_and_compressed_versions_of_image(
+            thumbnail_filename, feconf.ENTITY_TYPE_TOPIC, entity_id, raw_image,
+            filename_prefix, image_is_compressible)
+
+        topic_services.update_topic_and_subtopic_pages(
+            self.user_id, new_topic_id, [topic_domain.TopicChange({
+                'cmd': 'update_topic_property',
+                'property_name': 'thumbnail_filename',
+                'old_value': None,
+                'new_value': thumbnail_filename
+            }), topic_domain.TopicChange({
+                'cmd': 'update_topic_property',
+                'property_name': 'thumbnail_bg_color',
+                'old_value': None,
+                'new_value': thumbnail_bg_color
+            }), ], 'Add topic thumbnail.')
+
         self.render_json({
             'topicId': new_topic_id
         })


Test patch:
diff --git a/core/controllers/topics_and_skills_dashboard_test.py b/core/controllers/topics_and_skills_dashboard_test.py
--- a/core/controllers/topics_and_skills_dashboard_test.py
+++ b/core/controllers/topics_and_skills_dashboard_test.py
@@ -178,10 +178,19 @@ def test_topic_creation(self):
         payload = {
             'name': 'Topic name',
             'abbreviated_name': 'name',
-            'description': 'Topic description'
+            'description': 'Topic description',
+            'filename': 'test_svg.svg',
+            'thumbnailBgColor': '#C6DCDA',
         }
+
+        with python_utils.open_file(
+            os.path.join(feconf.TESTS_DATA_DIR, 'test_svg.svg'),
+            mode='rb', encoding=None) as f:
+            raw_image = f.read()
         json_response = self.post_json(
-            self.url, payload, csrf_token=csrf_token)
+            self.url, payload, csrf_token=csrf_token,
+            upload_files=(('image', 'unused_filename', raw_image),)
+        )
         topic_id = json_response['topicId']
         self.assertEqual(len(topic_id), 12)
         self.assertIsNotNone(
@@ -199,6 +208,31 @@ def test_topic_creation_with_invalid_name(self):
             self.url, payload, csrf_token=csrf_token, expected_status_int=400)
         self.logout()
 
+    def test_topic_creation_with_invalid_image(self):
+        self.login(self.ADMIN_EMAIL)
+        csrf_token = self.get_new_csrf_token()
+        payload = {
+            'name': 'Topic name',
+            'abbreviated_name': 'name',
+            'description': 'Topic description',
+            'filename': 'cafe.flac',
+            'thumbnailBgColor': '#C6DCDA',
+        }
+
+        with python_utils.open_file(
+            os.path.join(feconf.TESTS_DATA_DIR, 'cafe.flac'),
+            mode='rb', encoding=None) as f:
+            raw_image = f.read()
+
+        json_response = self.post_json(
+            self.url, payload, csrf_token=csrf_token,
+            upload_files=(('image', 'unused_filename', raw_image),),
+            expected_status_int=400
+        )
+
+        self.assertEqual(
+            json_response['error'], 'Image exceeds file size limit of 100 KB.')
+
 
 class NewSkillHandlerTests(BaseTopicsAndSkillsDashboardTests):
 
diff --git a/core/tests/data/test2_svg.svg b/core/tests/data/test2_svg.svg
new file mode 100644
--- /dev/null
+++ b/core/tests/data/test2_svg.svg
@@ -0,0 +1 @@
+<svg height="512pt" viewBox="0 0 512 512" width="512pt" xmlns="http://www.w3.org/2000/svg"><path d="m416 512h-320c-53.023438 0-96-42.976562-96-96v-320c0-53.023438 42.976562-96 96-96h320c53.023438 0 96 42.976562 96 96v320c0 53.023438-42.976562 96-96 96zm0 0" fill="#e3f8fa"/><path d="m333.328125 128h-154.671875c-16.175781 0-29.328125 13.152344-29.328125 29.328125v197.328125c0 16.175781 13.152344 29.328125 29.328125 29.328125h154.671875c16.175781 0 29.328125-13.152344 29.328125-29.328125v-197.328125c.015625-16.175781-13.152344-29.328125-29.328125-29.328125zm-130.65625 173.328125c12.351563 0 21.328125 8.222656 21.328125 19.535156 0 11.472657-9.375 20.464844-21.328125 20.464844h-13.328125c-4.414062 0-8-3.585937-8-8 0-4.417969 3.585938-8 8-8h13.328125c2.65625 0 5.328125-1.375 5.328125-4.464844 0-3.085937-3.34375-3.535156-5.328125-3.535156-12.351563 0-21.328125-8.335937-21.328125-19.839844 0-11.503906 9.167969-20.160156 21.328125-20.160156h13.328125c4.414062 0 8 3.582031 8 8 0 4.414063-3.585938 8-8 8h-13.328125c-.894531 0-5.328125.191406-5.328125 4.160156-.015625 3.566407 4.082031 3.839844 5.328125 3.839844zm74.367187-13.855469-13.328124 48c-.957032 3.457032-4.109376 5.855469-7.710938 5.855469s-6.753906-2.398437-7.710938-5.855469l-13.328124-48c-1.183594-4.257812 1.3125-8.671875 5.566406-9.855468 4.226562-1.183594 8.671875 1.328124 9.855468 5.566406l5.617188 20.273437 5.632812-20.257812c1.183594-4.238281 5.597657-6.734375 9.855469-5.566407 4.238281 1.183594 6.734375 5.582032 5.550781 9.839844zm29.632813 37.855469h8v-3.121094c-3.105469-1.085937-5.328125-4.0625-5.328125-7.535156 0-4.414063 3.585938-8 8-8h5.328125c4.417969 0 8 3.585937 8 8v18.671875c0 4.417969-3.582031 8-8 8h-16c-10.289063 0-18.671875-8.367188-18.671875-18.671875v-26.671875c0-10.289062 8.367188-18.671875 18.671875-18.671875h16c4.417969 0 8 3.582031 8 8 0 4.414063-3.582031 8-8 8h-16c-1.472656 0-2.671875 1.199219-2.671875 2.671875v26.671875c0 1.457031 1.199219 2.65625 2.671875 2.65625zm0 0" fill="#26c6da"/></svg>
diff --git a/core/tests/protractor_desktop/classroomPageFileUploadFeatures.js b/core/tests/protractor_desktop/classroomPageFileUploadFeatures.js
--- a/core/tests/protractor_desktop/classroomPageFileUploadFeatures.js
+++ b/core/tests/protractor_desktop/classroomPageFileUploadFeatures.js
@@ -61,7 +61,7 @@ describe('Classroom page functionality', function() {
       await topicsAndSkillsDashboardPage.get();
       await topicsAndSkillsDashboardPage.createTopic('Topic 1',
         'Description', false);
-      await topicEditorPage.submitTopicThumbnail('../data/test_svg.svg');
+      await topicEditorPage.submitTopicThumbnail('../data/test2_svg.svg', true);
       await topicEditorPage.saveTopic('Added thumbnail.');
       var url = await browser.getCurrentUrl();
       var topicId = url.split('/')[4].slice(0, -1);
diff --git a/core/tests/protractor_desktop/preferences.js b/core/tests/protractor_desktop/preferences.js
--- a/core/tests/protractor_desktop/preferences.js
+++ b/core/tests/protractor_desktop/preferences.js
@@ -33,7 +33,7 @@ describe('Preferences', function() {
     await preferencesPage.get();
     var defaultProfilePhotoSource = (
       await preferencesPage.getProfilePhotoSource());
-    await preferencesPage.submitProfilePhoto('../data/img.png');
+    await preferencesPage.submitProfilePhoto('../data/img.png', false);
     var newProfilePhotoSource = await preferencesPage.getProfilePhotoSource();
     expect(defaultProfilePhotoSource).not.toEqual(newProfilePhotoSource);
   });
@@ -43,7 +43,8 @@ describe('Preferences', function() {
     await users.login('lou@preferences.com');
     await preferencesPage.get();
     await waitFor.pageToFullyLoad();
-    await preferencesPage.uploadProfilePhoto('../data/dummyLargeImage.jpg');
+    await preferencesPage.uploadProfilePhoto(
+      '../data/dummyLargeImage.jpg', false);
     await preferencesPage.expectUploadError();
   });
 
diff --git a/core/tests/protractor_desktop/topicAndStoryEditorFileUploadFeatures.js b/core/tests/protractor_desktop/topicAndStoryEditorFileUploadFeatures.js
--- a/core/tests/protractor_desktop/topicAndStoryEditorFileUploadFeatures.js
+++ b/core/tests/protractor_desktop/topicAndStoryEditorFileUploadFeatures.js
@@ -65,7 +65,7 @@ describe('Topic editor functionality', function() {
     await topicEditorPage.changeTopicName(NEW_TOPIC_NAME);
     var defaultThumbnailImageSrc = (
       await topicEditorPage.getTopicThumbnailSource());
-    await topicEditorPage.submitTopicThumbnail('../data/test_svg.svg');
+    await topicEditorPage.submitTopicThumbnail('../data/test2_svg.svg', true);
     var updatedThumbnailImageSrc = (
       await topicEditorPage.getTopicThumbnailSource());
     expect(defaultThumbnailImageSrc).not.toEqual(updatedThumbnailImageSrc);
@@ -88,7 +88,7 @@ describe('Topic editor functionality', function() {
       TOPIC_NAME, TOPIC_DESCRIPTION, false);
     var defaultThumbnailSrc = (
       await topicEditorPage.getTopicThumbnailSource());
-    await topicEditorPage.submitTopicThumbnail('../data/test_svg.svg');
+    await topicEditorPage.submitTopicThumbnail('../data/test2_svg.svg', true);
     var updatedTopicThumbnailSrc = (
       await topicEditorPage.getTopicThumbnailSource());
     expect(defaultThumbnailSrc).not.toEqual(updatedTopicThumbnailSrc);
@@ -100,7 +100,8 @@ describe('Topic editor functionality', function() {
     await topicEditorPage.changeSubtopicTitle('Modified Title');
     await topicEditorPage.changeSubtopicPageContents(
       await forms.toRichText('Subtopic Contents'));
-    await topicEditorPage.submitSubtopicThumbnail('../data/test_svg.svg');
+    await topicEditorPage.submitSubtopicThumbnail(
+      '../data/test2_svg.svg', false);
     var updatedSubtopicThumbnailSrc = (
       await topicEditorPage.getSubtopicThumbnailSource());
     expect(defaultThumbnailSrc).not.toEqual(updatedSubtopicThumbnailSrc);
@@ -138,7 +139,7 @@ describe('Topic editor functionality', function() {
     await topicEditorPage.navigateToStoryWithIndex(0);
     var defaultThumbnailImageSrc = (
       await storyEditorPage.getStoryThumbnailSource());
-    await storyEditorPage.submitStoryThumbnail('../data/test_svg.svg');
+    await storyEditorPage.submitStoryThumbnail('../data/test2_svg.svg');
     expect(await storyEditorPage.getStoryThumbnailSource()).not.toEqual(
       defaultThumbnailImageSrc);
     await storyEditorPage.saveStory('Added thumbnail.');
@@ -209,19 +210,20 @@ describe('Chapter editor functionality', function() {
     await topicsAndSkillsDashboardPage.createTopic(TOPIC_NAME,
       'Topic description', false);
     defaultThumbnailImageSrc = await topicEditorPage.getTopicThumbnailSource();
-    await topicEditorPage.submitTopicThumbnail('../data/test_svg.svg');
+    await topicEditorPage.submitTopicThumbnail('../data/test2_svg.svg', true);
     expect(await topicEditorPage.getTopicThumbnailSource()).not.toEqual(
       defaultThumbnailImageSrc);
     await topicEditorPage.changeTopicDescription('Topic Description');
     await topicEditorPage.expectTopicDescriptionToBe('Topic Description');
     await topicEditorPage.saveTopic('Changed topic name and description.');
     await topicEditorPage.createStory('Story 0');
-    await storyEditorPage.submitStoryThumbnail('../data/test_svg.svg');
+    await storyEditorPage.submitStoryThumbnail('../data/test2_svg.svg');
     expect(await storyEditorPage.getStoryThumbnailSource()).not.toEqual(
       defaultThumbnailImageSrc);
     await storyEditorPage.createInitialChapter('Chapter 1');
     await storyEditorPage.selectInitialChapterByName('Chapter 1');
-    await storyEditorPage.submitChapterThumbnail('../data/test_svg.svg');
+    await storyEditorPage.submitChapterThumbnail(
+      '../data/test2_svg.svg', false);
     expect(await storyEditorPage.getChapterThumbnailSource()).not.toEqual(
       defaultThumbnailImageSrc);
     await storyEditorPage.changeNodeOutline(
diff --git a/core/tests/protractor_utils/PreferencesPage.js b/core/tests/protractor_utils/PreferencesPage.js
--- a/core/tests/protractor_utils/PreferencesPage.js
+++ b/core/tests/protractor_utils/PreferencesPage.js
@@ -60,13 +60,14 @@ var PreferencesPage = function() {
     expect(await profilePhotoUploadError.isDisplayed()).toBe(true);
   };
 
-  this.uploadProfilePhoto = async function(imgPath) {
-    return await workflow.uploadImage(profilePhotoClickable, imgPath);
+  this.uploadProfilePhoto = async function(imgPath, resetExistingImage) {
+    return await workflow.uploadImage(
+      profilePhotoClickable, imgPath, resetExistingImage);
   };
 
-  this.submitProfilePhoto = async function(imgPath) {
+  this.submitProfilePhoto = async function(imgPath, resetExistingImage) {
     return await workflow.submitImage(
-      profilePhotoClickable, profilePhotoCropper, imgPath);
+      profilePhotoClickable, profilePhotoCropper, imgPath, resetExistingImage);
   };
 
   this.getProfilePhotoSource = async function() {
diff --git a/core/tests/protractor_utils/StoryEditorPage.js b/core/tests/protractor_utils/StoryEditorPage.js
--- a/core/tests/protractor_utils/StoryEditorPage.js
+++ b/core/tests/protractor_utils/StoryEditorPage.js
@@ -128,14 +128,14 @@ var StoryEditorPage = function() {
     return await workflow.getImageSource(chapterThumbnailImageElement);
   };
 
-  this.submitStoryThumbnail = async function(imgPath) {
+  this.submitStoryThumbnail = async function(imgPath, resetExistingImage) {
     return await workflow.submitImage(
-      storyThumbnailButton, thumbnailContainer, imgPath);
+      storyThumbnailButton, thumbnailContainer, imgPath, resetExistingImage);
   };
 
-  this.submitChapterThumbnail = async function(imgPath) {
+  this.submitChapterThumbnail = async function(imgPath, resetExistingImage) {
     return await workflow.submitImage(
-      chapterThumbnailButton, thumbnailContainer, imgPath);
+      chapterThumbnailButton, thumbnailContainer, imgPath, resetExistingImage);
   };
 
   this.publishStory = async function() {
diff --git a/core/tests/protractor_utils/TopicEditorPage.js b/core/tests/protractor_utils/TopicEditorPage.js
--- a/core/tests/protractor_utils/TopicEditorPage.js
+++ b/core/tests/protractor_utils/TopicEditorPage.js
@@ -123,14 +123,15 @@ var TopicEditorPage = function() {
     return await workflow.getImageSource(subtopicThumbnailImageElement);
   };
 
-  this.submitTopicThumbnail = async function(imgPath) {
+  this.submitTopicThumbnail = async function(imgPath, resetExistingImage) {
     return await workflow.submitImage(
-      topicThumbnailButton, thumbnailContainer, imgPath);
+      topicThumbnailButton, thumbnailContainer, imgPath,
+      resetExistingImage);
   };
 
-  this.submitSubtopicThumbnail = async function(imgPath) {
+  this.submitSubtopicThumbnail = async function(imgPath, resetExistingImage) {
     return await workflow.submitImage(
-      subtopicThumbnailButton, thumbnailContainer, imgPath);
+      subtopicThumbnailButton, thumbnailContainer, imgPath, resetExistingImage);
   };
 
   this.publishTopic = async function() {
diff --git a/core/tests/protractor_utils/TopicsAndSkillsDashboardPage.js b/core/tests/protractor_utils/TopicsAndSkillsDashboardPage.js
--- a/core/tests/protractor_utils/TopicsAndSkillsDashboardPage.js
+++ b/core/tests/protractor_utils/TopicsAndSkillsDashboardPage.js
@@ -19,6 +19,7 @@
 
 var waitFor = require('./waitFor.js');
 var SkillEditorPage = require('./SkillEditorPage.js');
+var workflow = require('./workflow.js');
 
 var TopicsAndSkillsDashboardPage = function() {
   var DASHBOARD_URL = '/topics-and-skills-dashboard';
@@ -87,8 +88,11 @@ var TopicsAndSkillsDashboardPage = function() {
   var topicNamesInTopicSelectModal = element.all(
     by.css('.protractor-test-topic-name-in-topic-select-modal'));
   var topicsTabButton = element(
-    by.css('.protractor-test-topics-tab')
-  );
+    by.css('.protractor-test-topics-tab'));
+  var topicThumbnailButton = element(
+    by.css('.protractor-test-photo-button'));
+  var thumbnailContainer = element(
+    by.css('.protractor-test-thumbnail-container'));
 
   // Returns a promise of all topics with the given name.
   var _getTopicElements = async function(topicName) {
@@ -136,7 +140,7 @@ var TopicsAndSkillsDashboardPage = function() {
     await waitFor.visibilityOf(topicsTable,
       'Topic table taking too long to appear.');
     var topicEditOptionBox = topicEditOptions.get(index);
-    await browser.actions().mouseMove(topicEditOptionBox).perform();
+    await topicEditOptionBox.click();
     await waitFor.elementToBeClickable(
       editTopicButton,
       'Edit Topic button takes too long to be clickable');
@@ -197,6 +201,10 @@ var TopicsAndSkillsDashboardPage = function() {
       'Create Topic modal takes too long to appear.');
     await topicNameField.sendKeys(topicName);
     await topicDescriptionField.sendKeys(description);
+    await workflow.submitImage(
+      topicThumbnailButton, thumbnailContainer,
+      ('../data/test_svg.svg'), false);
+
     await confirmTopicCreationButton.click();
 
     await waitFor.newTabToBeCreated(
@@ -261,7 +269,7 @@ var TopicsAndSkillsDashboardPage = function() {
     await waitFor.visibilityOf(topicsTable,
       'Topic table taking too long to appear.');
     var topicEditOptionBox = topicEditOptions.get(index);
-    await browser.actions().mouseMove(topicEditOptionBox).perform();
+    await topicEditOptionBox.click();
     await waitFor.elementToBeClickable(
       deleteTopicButton,
       'Delete Topic button takes too long to be clickable');
diff --git a/core/tests/protractor_utils/workflow.js b/core/tests/protractor_utils/workflow.js
--- a/core/tests/protractor_utils/workflow.js
+++ b/core/tests/protractor_utils/workflow.js
@@ -30,6 +30,8 @@ var imageUploadInput = element(
   by.css('.protractor-test-photo-upload-input'));
 var imageSubmitButton = element(
   by.css('.protractor-test-photo-upload-submit'));
+var thumbnailResetButton = element(by.css(
+  '.protractor-thumbnail-reset-button'));
 
 // check if the save roles button is clickable
 var canAddRolesToUsers = async function() {
@@ -278,21 +280,33 @@ var getImageSource = async function(customImageElement) {
   return await customImageElement.getAttribute('src');
 };
 
-var uploadImage = async function(imageClickableElement, imgPath) {
+var uploadImage = async function(
+    imageClickableElement, imgPath, resetExistingImage) {
   await waitFor.visibilityOf(
     imageClickableElement,
     'Image element is taking too long to appear.');
   await imageClickableElement.click();
+
+  if (resetExistingImage) {
+    expect(await thumbnailResetButton.isPresent()).toBe(true);
+    await waitFor.elementToBeClickable(
+      thumbnailResetButton,
+      'Topic thumbnail reset button taking too long to appear.');
+    await thumbnailResetButton.click();
+  } else {
+    expect(await thumbnailResetButton.isPresent()).toBe(false);
+  }
+
   absPath = path.resolve(__dirname, imgPath);
   return await imageUploadInput.sendKeys(absPath);
 };
 
 var submitImage = async function(
-    imageClickableElement, imageContainer, imgPath) {
+    imageClickableElement, imageContainer, imgPath, resetExistingImage) {
   await waitFor.visibilityOf(
     imageClickableElement,
     'Image element is taking too long to appear.');
-  await uploadImage(imageClickableElement, imgPath);
+  await uploadImage(imageClickableElement, imgPath, resetExistingImage);
   await waitFor.visibilityOf(
     imageContainer, 'Image container is taking too long to appear');
   await imageSubmitButton.click();
diff --git a/scripts/check_e2e_tests_are_captured_in_ci.py b/scripts/check_e2e_tests_are_captured_in_ci.py
--- a/scripts/check_e2e_tests_are_captured_in_ci.py
+++ b/scripts/check_e2e_tests_are_captured_in_ci.py
@@ -33,6 +33,7 @@
     'classroomPageFileUploadFeatures', 'collections', 'embedding',
     'fileUploadFeatures', 'library', 'navigation', 'preferences',
     'profileFeatures', 'profileMenu', 'publication', 'subscriptions',
+    'topicsAndSkillsDashboard', 'topicAndStoryEditor',
     'topicAndStoryEditorFileUploadFeatures', 'users']
 
 TRAVIS_CI_FILE_PATH = os.path.join(os.getcwd(), '.travis.yml')